summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesusaves <cpntb1@ymail.com>2022-10-23 23:45:19 -0300
committerJesusaves <cpntb1@ymail.com>2022-10-23 23:45:19 -0300
commitc2ccb14ffff0e45398365e19dfd2874307ddb943 (patch)
tree976eaea586fb8f1ee0ab8cae67c69071aed8b952
downloadtools-c2ccb14ffff0e45398365e19dfd2874307ddb943.tar.gz
tools-c2ccb14ffff0e45398365e19dfd2874307ddb943.tar.bz2
tools-c2ccb14ffff0e45398365e19dfd2874307ddb943.tar.xz
tools-c2ccb14ffff0e45398365e19dfd2874307ddb943.zip
Initial commit
-rw-r--r--.gitignore89
-rw-r--r--Rules/castle_indoor/rule_castle_001.tmx176
-rw-r--r--Rules/castle_indoor/rule_castle_002.tmx77
-rw-r--r--Rules/castle_indoor/rule_castle_003.tmx68
-rw-r--r--Rules/castle_indoor/rule_castle_009.tmx106
-rw-r--r--Rules/castle_indoor/rule_castle_010.tmx113
-rw-r--r--Rules/castle_indoor/rule_castle_indoor.txt23
-rw-r--r--Rules/cave/rule_cave.txt41
-rw-r--r--Rules/cave/rule_cave_001.tmx169
-rw-r--r--Rules/cave/rule_cave_002.tmx75
-rw-r--r--Rules/cave/rule_cave_003.tmx47
-rw-r--r--Rules/cave/rule_cave_004.tmx70
-rw-r--r--Rules/cave/rule_cave_005.tmx70
-rw-r--r--Rules/cave/rule_cave_006.tmx135
-rw-r--r--Rules/cave/rule_cave_007.tmx147
-rw-r--r--Rules/cave/rule_cave_008.tmx144
-rw-r--r--Rules/cave/rule_cave_009.tmx75
-rw-r--r--Rules/cave/rule_cave_010.tmx92
-rw-r--r--Rules/cave/rule_cave_050.tmx129
-rw-r--r--Rules/cave/rule_cave_100.tmx53
-rw-r--r--Rules/cave/rule_cave_110.tmx47
-rw-r--r--Rules/caverules.txt6
-rw-r--r--Rules/desertcliffs/rule_cave.txt32
-rw-r--r--Rules/desertcliffs/rule_cave_001.tmx166
-rw-r--r--Rules/desertcliffs/rule_cave_002.tmx75
-rw-r--r--Rules/desertcliffs/rule_cave_003.tmx47
-rw-r--r--Rules/desertcliffs/rule_cave_004.tmx93
-rw-r--r--Rules/desertcliffs/rule_cave_005.tmx70
-rw-r--r--Rules/desertcliffs/rule_cave_006.tmx135
-rw-r--r--Rules/desertcliffs/rule_cave_007.tmx147
-rw-r--r--Rules/desertcliffs/rule_cave_008.tmx144
-rw-r--r--Rules/desertcliffs/rule_cave_03.tmx85
-rw-r--r--Rules/desertcliffs/rule_cave_050.tmx39
-rw-r--r--Rules/desertcliffs/rule_cave_100.tmx33
-rw-r--r--Rules/icecave/rule_icecave.txt34
-rw-r--r--Rules/icecave/rule_icecave_001.tmx54
-rw-r--r--Rules/icecave/rule_icecave_002.tmx45
-rw-r--r--Rules/icecave/rule_icecave_003.tmx35
-rw-r--r--Rules/icecave/rule_icecave_004.tmx40
-rw-r--r--Rules/icecave/rule_icecave_005.tmx40
-rw-r--r--Rules/icecave/rule_icecave_006.tmx40
-rw-r--r--Rules/icecave/rule_icecave_007.tmx40
-rw-r--r--Rules/icecave/rule_icecave_009.tmx55
-rw-r--r--Rules/icecave/rule_icecave_010.tmx55
-rw-r--r--Rules/icecave/rule_icecave_020.tmx40
-rw-r--r--Rules/icecave/rule_icecave_100.tmx40
-rw-r--r--Rules/rule_cave.tmx375
-rw-r--r--Rules/rules.txt17
-rw-r--r--Rules/tilesets/set_castle_indoor.pngbin0 -> 36121 bytes
-rw-r--r--Rules/tilesets/set_cave.pngbin0 -> 27678 bytes
-rw-r--r--Rules/tilesets/set_desertcliff.pngbin0 -> 28082 bytes
-rw-r--r--Rules/tilesets/set_hlw.pngbin0 -> 13104 bytes
-rw-r--r--Rules/tilesets/set_icecave.pngbin0 -> 4742 bytes
-rw-r--r--Rules/tilesets/set_icemountain.pngbin0 -> 1632 bytes
-rw-r--r--Rules/tilesets/set_rules.pngbin0 -> 563 bytes
-rw-r--r--Rules/tilesets/set_woodland.pngbin0 -> 4636 bytes
-rw-r--r--adler32/Makefile16
-rwxr-xr-xadler32/adler32bin0 -> 9216 bytes
-rw-r--r--adler32/adler32.c68
-rwxr-xr-xclient/aurora.py37
-rwxr-xr-xclient/dailylogin.py86
-rwxr-xr-xclient/magicacademy.py60
-rwxr-xr-xclient/minimap-dyecmd.py17
-rwxr-xr-xclient/minimap-dyecmd.sh0
-rw-r--r--client/minimap-override/none.pngbin0 -> 280 bytes
-rwxr-xr-xclient/minimap-render.py171
-rwxr-xr-xclient/skills.py137
-rwxr-xr-xclient/tmxrasterizerbin0 -> 1525440 bytes
-rwxr-xr-xclient/weapons.py89
-rw-r--r--hercules/.gitignore12
-rw-r--r--hercules/code/__init__.py0
-rw-r--r--hercules/code/clienttoserver/__init__.py0
-rw-r--r--hercules/code/clienttoserver/maps.py190
-rw-r--r--hercules/code/configutils.py51
-rw-r--r--hercules/code/fileutils.py86
-rw-r--r--hercules/code/server/__init__.py0
-rw-r--r--hercules/code/server/account.py63
-rw-r--r--hercules/code/server/accreg.py52
-rw-r--r--hercules/code/server/db/__init__.py0
-rw-r--r--hercules/code/server/db/char.py187
-rw-r--r--hercules/code/server/db/charregnumdb.py34
-rw-r--r--hercules/code/server/db/inventory.py44
-rw-r--r--hercules/code/server/db/skill.py31
-rw-r--r--hercules/code/server/dbitem.py7
-rw-r--r--hercules/code/server/dbskill.py7
-rw-r--r--hercules/code/server/dbuser.py7
-rw-r--r--hercules/code/server/evol/__init__.py0
-rw-r--r--hercules/code/server/evol/athena.py207
-rw-r--r--hercules/code/server/evol/consts.py49
-rw-r--r--hercules/code/server/evol/itemdb.py104
-rw-r--r--hercules/code/server/evol/main.py43
-rw-r--r--hercules/code/server/evol/mobdb.py89
-rw-r--r--hercules/code/server/evol/mobskilldb.py50
-rw-r--r--hercules/code/server/evol/npcs.py281
-rw-r--r--hercules/code/server/maps.py43
-rw-r--r--hercules/code/server/party.py80
-rw-r--r--hercules/code/server/questsdb.py33
-rw-r--r--hercules/code/server/storage.py81
-rw-r--r--hercules/code/server/tmw/__init__.py0
-rw-r--r--hercules/code/server/tmw/athena.py207
-rw-r--r--hercules/code/server/tmw/consts.py129
-rw-r--r--hercules/code/server/tmw/itemdb.py264
-rw-r--r--hercules/code/server/tmw/main.py49
-rw-r--r--hercules/code/server/tmw/mobdb.py170
-rw-r--r--hercules/code/server/tmw/mobskilldb.py55
-rw-r--r--hercules/code/server/tmw/npcs.py878
-rw-r--r--hercules/code/server/utils.py12
-rw-r--r--hercules/code/servertoclient/__init__.py0
-rw-r--r--hercules/code/servertoclient/homunculuses.py31
-rw-r--r--hercules/code/servertoclient/items.py138
-rw-r--r--hercules/code/servertoclient/luas.py51
-rw-r--r--hercules/code/servertoclient/maps.py48
-rw-r--r--hercules/code/servertoclient/mercenaries.py31
-rw-r--r--hercules/code/servertoclient/monsters.py43
-rw-r--r--hercules/code/servertoclient/npcs.py29
-rw-r--r--hercules/code/servertoclient/pets.py31
-rw-r--r--hercules/code/servertoclient/quests.py39
-rw-r--r--hercules/code/servertoclient/skills.py33
-rw-r--r--hercules/code/servertoclient/sprites.py467
-rw-r--r--hercules/code/serverutils.py12
-rw-r--r--hercules/code/stringutils.py62
-rw-r--r--hercules/code/tileutils.py48
-rwxr-xr-xhercules/convert_mapcache_to_tmx.py24
-rwxr-xr-xhercules/convert_tmx_to_mapcache.py9
-rwxr-xr-xhercules/extract_mapcache.py24
-rwxr-xr-xhercules/import_newdb.sh24
-rwxr-xr-xhercules/list_mapcache.py24
-rw-r--r--hercules/templates/acc_reg_num_db.sql11
-rw-r--r--hercules/templates/char.sql72
-rw-r--r--hercules/templates/char_reg_num_db.sql11
-rw-r--r--hercules/templates/collision.pngbin0 -> 275 bytes
-rw-r--r--hercules/templates/constants.tpl45
-rw-r--r--hercules/templates/group.tpl11
-rw-r--r--hercules/templates/homunculus.tpl3
-rw-r--r--hercules/templates/homunculuses.xml6
-rw-r--r--hercules/templates/inventory.sql23
-rw-r--r--hercules/templates/item.tpl7
-rw-r--r--hercules/templates/item_db.tpl63
-rw-r--r--hercules/templates/items.xml148
-rw-r--r--hercules/templates/login.sql25
-rw-r--r--hercules/templates/mercenaries.xml6
-rw-r--r--hercules/templates/mercenary.tpl3
-rw-r--r--hercules/templates/mob_db.tpl101
-rw-r--r--hercules/templates/mob_skill_db.tpl56
-rw-r--r--hercules/templates/monster.tpl3
-rw-r--r--hercules/templates/monsters.xml11
-rw-r--r--hercules/templates/npc.tpl3
-rw-r--r--hercules/templates/npcs.xml11
-rw-r--r--hercules/templates/party.sql12
-rw-r--r--hercules/templates/pet.tpl3
-rw-r--r--hercules/templates/pets.xml6
-rw-r--r--hercules/templates/quest.tpl5
-rw-r--r--hercules/templates/quest_db.tpl5
-rw-r--r--hercules/templates/quests.xml6
-rw-r--r--hercules/templates/skill.sql10
-rw-r--r--hercules/templates/skill.tpl1
-rw-r--r--hercules/templates/skills.xml15
-rw-r--r--hercules/templates/sprite.xml21
-rw-r--r--hercules/templates/storage.sql22
-rw-r--r--hercules/templates/template.tmx25
-rw-r--r--hercules/templates/tileset.pngbin0 -> 3550 bytes
-rwxr-xr-xhercules/tmx_converter.py659
-rwxr-xr-xherculeswrapper/char.sh7
-rw-r--r--herculeswrapper/herc-map-wrapper-config.example77
-rw-r--r--herculeswrapper/include.sh56
-rwxr-xr-xherculeswrapper/login.sh7
-rwxr-xr-xherculeswrapper/map.sh52
-rwxr-xr-ximagescheck/icccheck.sh5
-rwxr-xr-ximagescheck/icccheckfile.sh9
-rwxr-xr-xlicensecheck/checkfile.sh12
-rwxr-xr-xlicensecheck/clientdata.sh9
-rwxr-xr-xlicensecheck/serverdata.py71
-rw-r--r--localserver/.gitignore1
-rw-r--r--localserver/README14
-rw-r--r--localserver/applicator.py45
-rwxr-xr-xlocalserver/build.sh8
-rwxr-xr-xlocalserver/buildasan.sh6
-rwxr-xr-xlocalserver/checktime.sh9
-rwxr-xr-xlocalserver/clean.sh6
-rw-r--r--localserver/conf/battle.conf32
-rw-r--r--localserver/conf/char-server.conf31
-rw-r--r--localserver/conf/constants.conf30
-rw-r--r--localserver/conf/inter-server.conf32
-rw-r--r--localserver/conf/login-server.conf32
-rw-r--r--localserver/conf/logs.conf32
-rw-r--r--localserver/conf/map-server.conf32
-rw-r--r--localserver/conf/msg_conf.txt0
-rw-r--r--localserver/conf/ports.conf8
-rw-r--r--localserver/conf/script.conf32
-rw-r--r--localserver/conf/socket.conf32
-rw-r--r--localserver/conf/sql_connection.conf52
-rwxr-xr-xlocalserver/givegm.sh8
-rwxr-xr-xlocalserver/initdb.sh26
-rwxr-xr-xlocalserver/installconfigs.sh20
-rw-r--r--localserver/npc/006-10_blackbox.txt12
-rw-r--r--localserver/npc/015-8_blackbox.txt27
-rw-r--r--localserver/npc/017-1_stranger_blackbox.txt40
-rw-r--r--localserver/npc/018-7-1_demure_blackbox.txt12
-rw-r--r--localserver/npc/023-4_blackbox.txt36
-rw-r--r--localserver/npc/025-2-4_aegis_blackbox.txt33
-rw-r--r--localserver/npc/botcheck_blackbox.txt35
-rw-r--r--localserver/npc/easteregg_blackbox.txt16
-rw-r--r--localserver/npc/magic_blackbox.txt13
-rw-r--r--localserver/reapply.patch35
-rw-r--r--localserver/reapply.patch447
-rwxr-xr-xlocalserver/rebuild.sh9
-rwxr-xr-xlocalserver/updateconfigs.sh30
-rwxr-xr-xlocalserver/updatedb.sh103
-rwxr-xr-xlocalserver/updater.sh44
-rwxr-xr-xmanaplus/connect_local_server.sh13
-rw-r--r--manaplus/evol.manaplus18
-rwxr-xr-xmanaplus/force_localmanaplus.sh5
-rw-r--r--manaplus/loginwallpaper_800x600.pngbin0 -> 988427 bytes
-rw-r--r--manaplus/tmw2.manaplus18
-rwxr-xr-xtestxml/testxml.py2439
-rw-r--r--testxml/xsd/XMLSchema.xsd2262
-rwxr-xr-xtestxml/xsd/checkfile.sh5
-rw-r--r--testxml/xsd/its.xsd926
-rw-r--r--testxml/xsd/tmw.xsd2117
-rw-r--r--testxml/xsd/xlink.xsd79
-rw-r--r--testxml/xsd/xml.xsd287
-rwxr-xr-xtestxml/xsdcheck.sh52
-rw-r--r--update/TMW2org.zipbin0 -> 42988466 bytes
-rwxr-xr-xupdate/addmods.sh35
-rw-r--r--update/adler32.c79
-rw-r--r--update/commit.txt1
-rw-r--r--update/commit_old.txt1
-rwxr-xr-xupdate/create_music.sh31
-rwxr-xr-xupdate/createnew.sh40
-rwxr-xr-xupdate/deploy.sh7
-rw-r--r--update/musiccommit.txt1
-rw-r--r--update/musiccommit_old.txt1
-rw-r--r--update/news.txt40
-rwxr-xr-xupdate/pseudo_update.sh31
-rwxr-xr-xupdate/update.sh42
-rwxr-xr-xupdate/update_music.sh42
-rw-r--r--web/all_news.bin1
-rwxr-xr-xweb/backupcopy129
-rwxr-xr-xweb/deploy.sh4
-rw-r--r--web/feed.xml1
-rwxr-xr-xweb/fetch.py62
-rwxr-xr-xweb/gameinfo.sh22
-rw-r--r--web/index.bin1
-rw-r--r--web/langs.txt5
-rwxr-xr-xweb/legacy51
-rw-r--r--web/news.html1
-rwxr-xr-xweb/news_to_html.py91
-rwxr-xr-xweb/newsfeed.py101
-rw-r--r--web/nf_main.xml1
-rwxr-xr-xweb/oldupdatelang.py164
-rw-r--r--web/original.po170
-rw-r--r--web/template.po84
-rwxr-xr-xweb/updatelang.py190
-rwxr-xr-xwiki/elegen.py426
-rwxr-xr-xwiki/lanalysis.py394
-rwxr-xr-xwiki/redesign.py1050
-rwxr-xr-xwiki/sedesign.py515
-rw-r--r--wiki/tmp-arm247
-rw-r--r--wiki/tmp-dec8
-rw-r--r--wiki/tmp-etc25
-rw-r--r--wiki/tmp-wpn59
-rwxr-xr-xwiki/webwikigen.py758
-rwxr-xr-xwiki/wikigen.py898
263 files changed, 26066 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a1e538c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,89 @@
+# temp files
+*.xml~
+*.py~
+*.txt~
+*.conf~
+*~
+._*
+
+# updates
+update/files
+update/upload
+!update/adler32.c
+update/adler32
+update/files/Bugfix-*
+update/save*
+
+hercules_update/files
+hercules_update/upload
+!hercules_update/adler32.c
+hercules_update/adler32
+
+# saedit
+saedit/saedit
+saedit/make.log
+
+# langs
+lang/tx
+lang/new
+login.txt
+password.txt
+
+# lang client
+lang_client/tmp
+lang_client/pot
+lang_client/dict
+
+# tx
+tx
+
+# temp files
+*.o
+localserver/.~*
+
+# dyecmd
+dyecmd/dyecmd
+dyecmd/src/dyecmd
+dyecmd/src/CMakeFiles/
+dyecmd/src/Makefile
+dyecmd/src/cmake_install.cmake
+
+# testxml
+testxml/errors.txt
+
+# gitlab merger
+herculeswrapper/herc-map-wrapper-config
+
+# license check
+licensecheck/npcs.txt
+
+# wiki
+wiki/Items.md
+wiki/Monsters.md
+wiki/*.html
+wiki/*.txt
+
+# web
+1index.bin
+backup.bin
+web/po/*
+
+# Proccessed other server data
+hercules/data*
+hercules/osd*
+hercules/old*
+
+# Diffs and Patches related files
+*.diff
+*.patch
+*.orig
+*.rej
+
+# Jesusalva's personal stuff
+misc/*
+update/*back*
+lang/in/JSave
+
+# Beta Server
+localserver/beta.patch
+localserver/beta.patch2
diff --git a/Rules/castle_indoor/rule_castle_001.tmx b/Rules/castle_indoor/rule_castle_001.tmx
new file mode 100644
index 0000000..6f4af55
--- /dev/null
+++ b/Rules/castle_indoor/rule_castle_001.tmx
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="17" height="17" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <properties>
+ <property name="AutomappingRadius" value="1"/>
+ <property name="DeleteTiles" value="true"/>
+ </properties>
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_castle_indoor.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <tileset firstgid="206" name="cave_x2" tilewidth="32" tileheight="64" tilecount="16" columns="16">
+ <image source="../../graphics/tilesets/cave_x2.png" width="512" height="64"/>
+ </tileset>
+ <tileset firstgid="222" source="../../../client-data/graphics/tilesets/woodland_indoor.tsx"/>
+ <tileset firstgid="478" source="../../../client-data/graphics/tilesets/inn2.tsx"/>
+ <tileset firstgid="734" name="desert_city_indoors" tilewidth="32" tileheight="32" tilecount="150" columns="15">
+ <image source="../../graphics/tilesets/desert_city_indoors.png" width="480" height="320"/>
+ </tileset>
+ <tileset firstgid="884" source="../../../client-data/graphics/tilesets/desert_castle_interior.tsx"/>
+ <tileset firstgid="1140" source="../../../client-data/graphics/tilesets/desert_city_indoors.tsx"/>
+ <layer name="Regions" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,74,0,74,0,74,0,74,0,74,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,74,0,74,0,74,0,74,0,74,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,74,0,0,0,74,0,74,0,74,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,74,0,0,0,0,0,74,0,74,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,74,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,10,0,11,0,0,0,0,0,14,0,15,0,0,0,17,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,19,0,0,0,0,0,22,0,23,0,0,0,25,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,30,0,31,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,38,0,39,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,42,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,36,0,37,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,41,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,1218,0,1186,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,1217,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,313,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,575,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Fringe" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Collision" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Over" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Error" width="17" height="17" visible="0">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/castle_indoor/rule_castle_002.tmx b/Rules/castle_indoor/rule_castle_002.tmx
new file mode 100644
index 0000000..a98bc77
--- /dev/null
+++ b/Rules/castle_indoor/rule_castle_002.tmx
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_castle_indoor.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <tileset firstgid="206" source="../../../client-data/graphics/tilesets/desert_castle_interior.tsx"/>
+ <tileset firstgid="462" source="../../../client-data/graphics/tilesets/inn2.tsx"/>
+ <layer name="Regions" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,74,74,0,
+0,74,0,74,0,0,0,0,
+0,74,0,74,0,74,74,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,14,0,
+0,14,0,0,0,0,0,0,
+0,0,0,14,0,14,0,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="8" height="6" visible="0">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,14,0,0,
+0,0,0,14,0,0,0,0,
+0,14,0,0,0,0,14,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Over" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,240,0,
+0,370,0,0,0,0,0,0,
+0,0,0,370,0,238,0,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Collision" width="8" height="6" visible="0">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/castle_indoor/rule_castle_003.tmx b/Rules/castle_indoor/rule_castle_003.tmx
new file mode 100644
index 0000000..a534afb
--- /dev/null
+++ b/Rules/castle_indoor/rule_castle_003.tmx
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="9" height="9" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_castle_indoor.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <tileset firstgid="206" source="../../../client-data/graphics/tilesets/desert_castle_interior.tsx"/>
+ <layer name="Regions" width="9" height="9">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,
+0,74,74,74,0,74,74,74,0,
+0,74,74,74,0,74,74,74,0,
+0,74,74,74,0,74,74,74,0,
+0,0,0,0,0,0,0,0,0,
+0,74,74,74,0,74,74,74,0,
+0,74,74,74,0,74,74,74,0,
+0,74,74,74,0,74,74,74,0,
+0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="9" height="9">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,
+0,0,0,14,0,14,0,0,0,
+0,0,0,0,0,0,0,0,0,
+0,0,0,14,0,14,0,0,0,
+0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="9" height="9">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,
+0,0,0,14,0,14,0,0,0,
+0,0,0,14,0,14,0,0,0,
+0,14,14,0,0,0,14,14,0,
+0,0,0,0,0,0,0,0,0,
+0,14,14,0,0,0,14,14,0,
+0,0,0,14,0,14,0,0,0,
+0,0,0,14,0,14,0,0,0,
+0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Over" width="9" height="9">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,
+0,0,0,209,0,210,0,0,0,
+0,0,0,0,0,0,0,0,0,
+0,0,0,338,0,339,0,0,0,
+0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/castle_indoor/rule_castle_009.tmx b/Rules/castle_indoor/rule_castle_009.tmx
new file mode 100644
index 0000000..680d64c
--- /dev/null
+++ b/Rules/castle_indoor/rule_castle_009.tmx
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="11" height="18" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set_ci" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_castle_indoor.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <tileset firstgid="206" source="../../../client-data/graphics/tilesets/inn2.tsx"/>
+ <tileset firstgid="462" source="../../../client-data/graphics/tilesets/inn.tsx"/>
+ <tileset firstgid="718" source="../../../client-data/graphics/tilesets/woodland_indoor.tsx"/>
+ <layer name="Regions" width="11" height="18">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,74,74,0,74,0,74,74,0,
+0,74,0,0,0,0,74,0,0,0,0,
+0,0,0,0,74,0,0,0,0,74,0,
+0,74,74,0,74,0,74,74,0,74,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,74,74,0,0,0,0,0,0,
+0,74,0,0,0,0,0,0,0,0,0,
+0,0,0,0,74,0,0,0,0,0,0,
+0,74,74,0,74,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,74,74,0,0,0,0,0,0,
+0,74,0,0,0,0,0,0,0,0,0,
+0,0,0,0,74,0,0,0,0,0,0,
+0,74,74,0,74,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="11" height="18">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,
+0,10,0,42,10,0,0,0,44,0,0,
+0,42,0,0,0,0,44,0,0,0,0,
+0,0,0,0,42,0,0,0,0,44,0,
+0,10,42,0,10,0,0,44,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,11,0,42,11,0,0,0,0,0,0,
+0,42,0,0,0,0,0,0,0,0,0,
+0,0,0,0,42,0,0,0,0,0,0,
+0,11,42,0,11,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,19,0,42,19,0,0,0,0,0,0,
+0,42,0,0,0,0,0,0,0,0,0,
+0,0,0,0,42,0,0,0,0,0,0,
+0,19,42,0,19,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="11" height="18">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,10,0,0,0,0,0,0,0,
+0,10,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,0,0,0,0,0,0,
+0,0,10,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,11,0,0,0,0,0,0,0,
+0,11,0,0,0,0,0,0,0,0,0,
+0,0,0,0,11,0,0,0,0,0,0,
+0,0,11,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,19,0,0,0,0,0,0,0,
+0,19,0,0,0,0,0,0,0,0,0,
+0,0,0,0,19,0,0,0,0,0,0,
+0,0,19,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground2" width="11" height="18">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,810,0,0,0,0,304,0,0,
+0,793,0,0,0,0,287,0,0,0,0,
+0,0,0,0,825,0,0,0,0,319,0,
+0,0,808,0,0,0,0,302,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,810,0,0,0,0,0,0,0,
+0,793,0,0,0,0,0,0,0,0,0,
+0,0,0,0,825,0,0,0,0,0,0,
+0,0,808,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,810,0,0,0,0,0,0,0,
+0,793,0,0,0,0,0,0,0,0,0,
+0,0,0,0,825,0,0,0,0,0,0,
+0,0,808,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/castle_indoor/rule_castle_010.tmx b/Rules/castle_indoor/rule_castle_010.tmx
new file mode 100644
index 0000000..5629a23
--- /dev/null
+++ b/Rules/castle_indoor/rule_castle_010.tmx
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="20" height="19" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set_ci" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_castle_indoor.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <tileset firstgid="206" source="../../../client-data/graphics/tilesets/woodland_indoor.tsx"/>
+ <tileset firstgid="462" name="set_castle_indoor" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_castle_indoor.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="526" source="../../../client-data/graphics/tilesets/inn2.tsx"/>
+ <tileset firstgid="782" source="../../../client-data/graphics/tilesets/desert_city_indoors.tsx"/>
+ <layer name="Regions" width="20" height="19">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,74,74,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="20" height="19">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,11,11,0,11,11,0,42,42,0,42,42,0,44,44,0,44,44,0,0,
+0,11,42,0,42,11,0,42,11,0,11,42,0,44,0,0,0,44,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,11,42,0,42,11,0,42,11,0,11,42,0,44,0,0,0,44,0,0,
+0,11,11,0,11,11,0,42,42,0,42,42,0,44,44,0,44,44,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,10,10,0,10,10,0,42,42,0,42,42,0,0,0,0,0,0,0,0,
+0,10,42,0,42,10,0,42,10,0,10,42,0,0,44,0,44,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,10,42,0,42,10,0,42,10,0,10,42,0,0,44,0,44,0,0,0,
+0,10,10,0,10,10,0,42,42,0,42,42,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,19,19,0,19,19,0,42,42,0,42,42,0,0,0,0,0,0,0,0,
+0,19,42,0,42,19,0,42,19,0,19,42,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,19,42,0,42,19,0,42,19,0,19,42,0,0,0,0,0,0,0,0,
+0,19,19,0,19,19,0,42,42,0,42,42,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="20" height="19">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,11,11,0,11,11,0,11,11,0,11,11,0,0,0,0,0,0,0,0,
+0,11,11,0,11,11,0,11,11,0,11,11,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,11,11,0,11,11,0,11,11,0,11,11,0,0,0,0,0,0,0,0,
+0,11,11,0,11,11,0,11,11,0,11,11,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,10,10,0,10,10,0,10,10,0,10,10,0,0,0,0,0,0,0,0,
+0,10,10,0,10,10,0,10,10,0,10,10,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,10,10,0,10,10,0,10,10,0,10,10,0,0,0,0,0,0,0,0,
+0,10,10,0,10,10,0,10,10,0,10,10,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,19,19,0,19,19,0,19,19,0,19,19,0,0,0,0,0,0,0,0,
+0,19,19,0,19,19,0,19,19,0,19,19,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,19,19,0,19,19,0,19,19,0,19,19,0,0,0,0,0,0,0,0,
+0,19,19,0,19,19,0,19,19,0,19,19,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground2" width="20" height="19">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,294,0,0,0,295,0,654,0,0,0,655,0,0,
+0,0,280,0,282,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,312,0,314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,310,0,0,0,311,0,670,0,0,0,671,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,294,0,0,0,295,0,0,0,0,0,0,0,0,
+0,0,280,0,282,0,0,0,0,0,0,0,0,0,606,0,608,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,312,0,314,0,0,0,0,0,0,0,0,0,638,0,640,0,0,0,
+0,0,0,0,0,0,0,310,0,0,0,311,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,294,0,0,0,295,0,0,0,0,0,0,0,0,
+0,0,280,0,282,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,312,0,314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,310,0,0,0,311,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/castle_indoor/rule_castle_indoor.txt b/Rules/castle_indoor/rule_castle_indoor.txt
new file mode 100644
index 0000000..4ce7553
--- /dev/null
+++ b/Rules/castle_indoor/rule_castle_indoor.txt
@@ -0,0 +1,23 @@
+// lines starting with // or // are comments
+// all other lines will be parsed and treated as filenames.
+
+// Rules for castles indoors
+
+// basic stuff: each rule-tile gets a default tile
+// here is a map attribute set: "DeleteTiles := true" This means to delete all
+// tiles in all touched tileslayers first.
+./rule_castle_001.tmx
+
+// straight walls:
+./rule_castle_002.tmx
+
+// corners at walls
+./rule_castle_003.tmx
+
+// Carpets are bad in overall, so do not place one near the other
+// straight carpets
+./rule_castle_009.tmx
+
+// corners in carpets
+./rule_castle_010.tmx
+
diff --git a/Rules/cave/rule_cave.txt b/Rules/cave/rule_cave.txt
new file mode 100644
index 0000000..080ae6b
--- /dev/null
+++ b/Rules/cave/rule_cave.txt
@@ -0,0 +1,41 @@
+// lines starting with // or // are comments
+// all other lines will be parsed and treated as filenames.
+
+// Rules for caves
+
+// basic stuff: each rule-tile gets a default tile
+// here is a map attribute set: "DeleteTiles := true" This means to delete all
+// tiles in all touched tileslayers first.
+./rule_cave_001.tmx
+
+// straight walls:
+./rule_cave_002.tmx
+
+// corners at walls
+./rule_cave_003.tmx
+./rule_cave_004.tmx
+./rule_cave_005.tmx
+
+// water in caves:
+./rule_cave_006.tmx
+./rule_cave_007.tmx
+
+// rules for entrances, has bad exception handling,
+// so mind where to put entrances. (So avoid putting them near corners)
+./rule_cave_008.tmx
+
+// straight carpets
+./rule_cave_009.tmx
+
+// corners in carpets
+./rule_cave_010.tmx
+
+// all tiles, which are walkable, but have unwalkable neighbors,
+// should be unwalkable
+// example: a decorative stone in a sea
+
+./rule_cave_050.tmx
+
+// error handling: some situations cannot be handled properly with this ruleset:
+// so indicate these situations with the error-tile
+./rule_cave_100.tmx
diff --git a/Rules/cave/rule_cave_001.tmx b/Rules/cave/rule_cave_001.tmx
new file mode 100644
index 0000000..065ccce
--- /dev/null
+++ b/Rules/cave/rule_cave_001.tmx
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.2" tiledversion="1.3.2" orientation="orthogonal" renderorder="right-down" compressionlevel="0" width="17" height="17" tilewidth="32" tileheight="32" infinite="0" nextlayerid="8" nextobjectid="1">
+ <properties>
+ <property name="AutomappingRadius" value="1"/>
+ <property name="DeleteTiles" value="true"/>
+ </properties>
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_cave.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <tileset firstgid="206" name="cave_x2" tilewidth="32" tileheight="64" tilecount="16" columns="16">
+ <image source="../../graphics/tilesets/cave_x2.png" width="512" height="64"/>
+ </tileset>
+ <layer id="1" name="Regions" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,74,0,74,0,74,0,74,0,74,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,74,0,74,0,74,0,74,0,74,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,74,0,0,0,74,0,74,0,74,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,74,0,0,0,0,0,74,0,74,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,74,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="2" name="Input_set" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,10,0,11,0,12,0,13,0,14,0,15,0,0,0,17,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,18,0,19,0,20,0,21,0,22,0,23,0,0,0,25,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,26,0,27,0,0,0,29,0,30,0,31,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,34,0,35,0,0,0,0,0,38,0,39,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,42,0,43,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,36,0,37,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,41,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="3" name="Output_Ground" width="17" height="17" visible="0">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,82,0,198,0,188,0,188,0,98,0,75,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,201,0,196,0,188,0,188,0,75,0,75,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,184,0,82,0,0,0,188,0,75,0,75,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,82,0,82,0,0,0,0,0,75,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,105,0,105,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="4" name="Output_Fringe" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,177,0,0,0,0,0,136,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,152,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,103,0,0,0,168,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,200,0,206,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,103,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="5" name="Output_Collision" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,2,0,2,0,0,0,0,0,2,0,2,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,2,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="6" name="Output_Over" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="7" name="Output_Error" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/cave/rule_cave_002.tmx b/Rules/cave/rule_cave_002.tmx
new file mode 100644
index 0000000..74feece
--- /dev/null
+++ b/Rules/cave/rule_cave_002.tmx
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_cave.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,74,0,0,0,0,0,0,
+0,74,0,0,0,74,74,0,
+0,74,0,74,0,0,0,0,
+0,74,0,74,0,74,74,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,14,0,
+0,14,0,0,0,0,0,0,
+0,0,0,14,0,14,0,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,14,0,0,
+0,0,0,14,0,0,0,0,
+0,14,0,0,0,0,14,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,114,0,0,0,0,0,0,
+0,130,0,0,0,0,97,0,
+0,146,0,0,0,0,0,0,
+0,0,0,0,0,99,0,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Over" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,101,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Collision" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,2,0,0,0,0,0,0,
+0,2,0,0,0,0,2,0,
+0,2,0,0,0,0,0,0,
+0,0,0,2,0,2,0,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/cave/rule_cave_003.tmx b/Rules/cave/rule_cave_003.tmx
new file mode 100644
index 0000000..6656ef6
--- /dev/null
+++ b/Rules/cave/rule_cave_003.tmx
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="13" height="4" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_cave.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="13" height="4">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="4">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,14,0,0,0,14,0,0,0,0,0,0,0,
+0,14,14,0,14,14,0,0,14,0,14,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="4">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,14,0,14,0,0,14,14,0,14,14,0,
+0,0,0,0,0,0,0,14,0,0,0,14,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Over" width="13" height="4">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,86,0,0,0,87,0,0,100,0,102,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/cave/rule_cave_004.tmx b/Rules/cave/rule_cave_004.tmx
new file mode 100644
index 0000000..3238bc7
--- /dev/null
+++ b/Rules/cave/rule_cave_004.tmx
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="7" height="7" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_cave.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="7" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,
+0,0,0,0,0,0,0,
+0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="7" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,
+0,14,14,0,14,14,0,
+0,0,14,0,14,0,0,
+0,0,0,0,0,0,0,
+0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="7" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,
+0,14,0,0,0,14,0,
+0,0,0,0,0,0,0,
+0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="7" height="7" visible="0">
+ <data encoding="csv">
+0,0,0,0,0,0,0,
+0,0,118,0,116,0,0,
+0,0,134,0,132,0,0,
+0,0,150,0,148,0,0,
+0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,
+0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Collision" width="7" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,
+0,2,2,0,2,2,0,
+0,2,2,0,2,2,0,
+0,2,2,0,2,2,0,
+0,0,2,0,2,0,0,
+0,0,0,0,0,0,0,
+0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/cave/rule_cave_005.tmx b/Rules/cave/rule_cave_005.tmx
new file mode 100644
index 0000000..a325b0a
--- /dev/null
+++ b/Rules/cave/rule_cave_005.tmx
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="13" height="7" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_cave.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,74,0,74,0,0,0,0,0,0,0,0,
+0,0,74,0,74,0,0,0,74,0,74,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,14,0,14,0,0,
+0,14,14,0,14,14,0,0,14,0,14,0,0,
+0,0,14,0,14,0,0,0,14,0,14,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,14,0,0,0,14,0,
+0,14,0,0,0,14,0,14,0,0,0,14,0,
+0,14,14,0,14,14,0,14,14,0,14,14,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="13" height="7" visible="0">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,95,0,96,0,0,0,0,0,0,0,0,
+0,0,111,0,112,0,0,0,113,0,115,0,0,
+0,0,127,0,128,0,0,0,129,0,131,0,0,
+0,0,143,0,144,0,0,0,145,0,147,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Collision" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,2,0,2,0,0,0,0,0,0,0,0,
+0,0,2,0,2,0,0,0,2,0,2,0,0,
+0,0,2,0,2,0,0,0,2,0,2,0,0,
+0,0,2,0,2,0,0,0,2,0,2,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/cave/rule_cave_006.tmx b/Rules/cave/rule_cave_006.tmx
new file mode 100644
index 0000000..622082d
--- /dev/null
+++ b/Rules/cave/rule_cave_006.tmx
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="6" height="6" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_cave.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,74,0,74,74,0,
+0,74,0,0,0,0,
+0,0,0,0,74,0,
+0,74,74,0,74,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,29,0,0,
+0,29,0,0,0,0,
+0,0,0,0,29,0,
+0,0,29,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,21,0,0,
+0,21,0,0,0,0,
+0,0,0,0,21,0,
+0,0,21,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,13,0,0,
+0,13,0,0,0,0,
+0,0,0,0,13,0,
+0,0,13,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,20,0,0,
+0,20,0,0,0,0,
+0,0,0,0,20,0,
+0,0,20,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,12,0,0,
+0,12,0,0,0,0,
+0,0,0,0,12,0,
+0,0,12,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6" opacity="0.06">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,29,0,0,29,0,
+0,0,0,0,0,0,
+0,0,0,0,0,0,
+0,29,0,0,29,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6" opacity="0.06">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,21,0,0,21,0,
+0,0,0,0,0,0,
+0,0,0,0,0,0,
+0,21,0,0,21,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6" opacity="0.05">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,13,0,0,13,0,
+0,0,0,0,0,0,
+0,0,0,0,0,0,
+0,13,0,0,13,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6" opacity="0.08">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,20,0,0,20,0,
+0,0,0,0,0,0,
+0,0,0,0,0,0,
+0,20,0,0,20,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6" opacity="0.09">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,12,0,0,12,0,
+0,0,0,0,0,0,
+0,0,0,0,0,0,
+0,12,0,0,12,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,189,0,0,
+0,172,0,0,0,0,
+0,0,0,0,204,0,
+0,0,187,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/cave/rule_cave_007.tmx b/Rules/cave/rule_cave_007.tmx
new file mode 100644
index 0000000..aeeae3c
--- /dev/null
+++ b/Rules/cave/rule_cave_007.tmx
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="13" height="7" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_cave.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,29,29,0,29,29,0,
+0,0,29,0,29,0,0,29,0,0,0,29,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,29,0,29,0,0,29,0,0,0,29,0,
+0,0,0,0,0,0,0,29,29,0,29,29,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,21,21,0,21,21,0,
+0,0,21,0,21,0,0,21,0,0,0,21,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,21,0,21,0,0,21,0,0,0,21,0,
+0,0,0,0,0,0,0,21,21,0,21,21,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,13,13,0,13,13,0,
+0,0,13,0,13,0,0,13,0,0,0,13,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,13,0,13,0,0,13,0,0,0,13,0,
+0,0,0,0,0,0,0,13,13,0,13,13,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,20,20,0,20,20,0,
+0,0,20,0,20,0,0,20,0,0,0,20,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,20,0,20,0,0,20,0,0,0,20,0,
+0,0,0,0,0,0,0,20,20,0,20,20,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,12,12,0,12,12,0,
+0,0,12,0,12,0,0,12,0,0,0,12,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,12,0,12,0,0,12,0,0,0,12,0,
+0,0,0,0,0,0,0,12,12,0,12,12,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,29,29,0,29,29,0,0,0,0,0,0,0,
+0,29,0,0,0,29,0,0,29,0,29,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,29,0,0,0,29,0,0,29,0,29,0,0,
+0,29,29,0,29,29,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,21,21,0,21,21,0,0,0,0,0,0,0,
+0,21,0,0,0,21,0,0,21,0,21,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,21,0,0,0,21,0,0,21,0,21,0,0,
+0,21,21,0,21,21,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,13,13,0,13,13,0,0,0,0,0,0,0,
+0,13,0,0,0,13,0,0,13,0,13,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,13,0,0,0,13,0,0,13,0,13,0,0,
+0,13,13,0,13,13,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,20,20,0,20,20,0,0,0,0,0,0,0,
+0,20,0,0,0,20,0,0,20,0,20,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,20,0,0,0,20,0,0,20,0,20,0,0,
+0,20,20,0,20,20,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,12,12,0,12,12,0,0,0,0,0,0,0,
+0,12,0,0,0,12,0,0,12,0,12,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,12,0,0,0,12,0,0,12,0,12,0,0,
+0,12,12,0,12,12,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,169,0,0,0,170,0,
+0,0,171,0,173,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,203,0,205,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,185,0,0,0,186,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/cave/rule_cave_008.tmx b/Rules/cave/rule_cave_008.tmx
new file mode 100644
index 0000000..00a7a06
--- /dev/null
+++ b/Rules/cave/rule_cave_008.tmx
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.2" tiledversion="1.2.2" orientation="orthogonal" renderorder="right-down" width="13" height="17" tilewidth="32" tileheight="32" infinite="0" nextlayerid="7" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_cave.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <tileset firstgid="206" name="cave_x2" tilewidth="32" tileheight="64" tilecount="16" columns="16">
+ <image source="../../graphics/tilesets/cave_x2.png" width="512" height="64"/>
+ </tileset>
+ <layer id="1" name="Regions" width="13" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,74,0,74,74,74,0,74,74,74,0,
+0,74,74,74,0,74,74,74,0,74,74,74,0,
+0,74,74,74,0,74,74,74,0,74,74,74,0,
+0,74,74,74,0,74,74,74,0,74,74,74,0,
+0,74,74,74,0,74,74,74,0,74,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,74,74,74,0,0,0,0,0,
+0,0,0,0,0,74,74,74,0,0,0,0,0,
+0,0,0,0,0,74,74,74,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="2" name="Input_set" width="13" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,14,14,14,0,14,14,14,0,14,14,14,0,
+0,14,14,14,0,14,14,14,0,14,14,14,0,
+0,14,14,14,0,14,14,14,0,14,14,14,0,
+0,14,31,14,0,14,38,14,0,14,30,14,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,14,22,14,0,0,0,0,0,
+0,0,0,0,0,14,14,14,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="3" name="Input_set" width="13" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,23,0,0,0,15,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="4" name="InputNot_set" width="13" height="17" opacity="0.36">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,14,14,14,0,14,14,14,0,14,14,14,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,14,14,14,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="5" name="Output_Ground" width="13" height="17" visible="0">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,98,98,98,0,98,98,98,0,98,98,98,0,
+0,114,114,114,0,114,114,114,0,114,114,114,0,
+0,164,165,166,0,139,140,141,0,91,92,93,0,
+0,180,181,182,0,155,156,157,0,107,108,109,0,
+0,0,0,0,0,0,0,0,0,123,124,125,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,82,82,82,0,0,0,0,0,
+0,0,0,0,0,82,82,82,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="6" name="Output_Over" width="13" height="17" visible="0">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,101,101,101,0,0,0,0,0,
+0,0,0,0,0,195,195,195,0,0,0,0,0,
+0,0,0,0,0,210,211,212,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/cave/rule_cave_009.tmx b/Rules/cave/rule_cave_009.tmx
new file mode 100644
index 0000000..bb1e8ec
--- /dev/null
+++ b/Rules/cave/rule_cave_009.tmx
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="6" height="6" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_cave.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,74,0,74,74,0,
+0,74,0,0,0,0,
+0,0,0,0,74,0,
+0,74,74,0,74,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,43,0,0,
+0,43,0,0,0,0,
+0,0,0,0,43,0,
+0,0,43,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,42,0,0,
+0,42,0,0,0,0,
+0,0,0,0,42,0,
+0,0,42,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6" opacity="0.36">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,43,0,0,43,0,
+0,0,0,0,0,0,
+0,0,0,0,0,0,
+0,43,0,0,43,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6" opacity="0.33">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,42,0,0,42,0,
+0,0,0,0,0,0,
+0,0,0,0,0,0,
+0,42,0,0,42,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,106,0,0,
+0,89,0,0,0,0,
+0,0,0,0,121,0,
+0,0,104,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/cave/rule_cave_010.tmx b/Rules/cave/rule_cave_010.tmx
new file mode 100644
index 0000000..3c9256b
--- /dev/null
+++ b/Rules/cave/rule_cave_010.tmx
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="13" height="7" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_cave.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,43,43,0,43,43,0,
+0,0,43,0,43,0,0,43,0,0,0,43,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,43,0,43,0,0,43,0,0,0,43,0,
+0,0,0,0,0,0,0,43,43,0,43,43,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,42,42,0,42,42,0,
+0,0,42,0,42,0,0,42,0,0,0,42,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,42,0,42,0,0,42,0,0,0,42,0,
+0,0,0,0,0,0,0,42,42,0,42,42,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.33">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,43,0,43,0,0,0,0,0,0,0,0,
+0,43,0,0,0,43,0,0,43,0,43,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,43,0,0,0,43,0,0,43,0,43,0,0,
+0,0,43,0,43,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.36">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,42,0,42,0,0,0,0,0,0,0,0,
+0,42,0,0,0,42,0,0,42,0,42,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,42,0,0,0,42,0,0,42,0,42,0,0,
+0,0,42,0,42,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="13" height="7" visible="0">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,137,0,0,0,138,0,
+0,0,88,0,90,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,120,0,122,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,153,0,0,0,154,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Collision" width="13" height="7" visible="0">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/cave/rule_cave_050.tmx b/Rules/cave/rule_cave_050.tmx
new file mode 100644
index 0000000..ff04f7f
--- /dev/null
+++ b/Rules/cave/rule_cave_050.tmx
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_cave.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <layer name="Regions" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,74,0,0,
+0,74,74,74,0,
+0,0,74,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,18,0,0,
+0,18,0,18,0,
+0,0,18,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,26,0,0,
+0,26,0,26,0,
+0,0,26,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,27,0,0,
+0,27,0,27,0,
+0,0,27,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,35,0,0,
+0,35,0,35,0,
+0,0,35,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,43,0,0,
+0,43,0,43,0,
+0,0,43,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,12,0,0,
+0,12,0,12,0,
+0,0,12,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,14,0,0,
+0,14,0,14,0,
+0,0,14,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,30,0,0,
+0,30,0,30,0,
+0,0,30,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,38,0,0,
+0,38,0,38,0,
+0,0,38,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,31,0,0,
+0,31,0,31,0,
+0,0,31,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,39,0,0,
+0,39,0,39,0,
+0,0,39,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Collision" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,0,0,0,
+0,0,2,0,0,
+0,0,0,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/cave/rule_cave_100.tmx b/Rules/cave/rule_cave_100.tmx
new file mode 100644
index 0000000..b115cf8
--- /dev/null
+++ b/Rules/cave/rule_cave_100.tmx
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="" orientation="orthogonal" renderorder="right-down" width="13" height="9" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="2" columns="2">
+ <image source="../../graphics/tilesets/collision.png" width="64" height="32"/>
+ </tileset>
+ <tileset firstgid="3" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_cave.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="67" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="71" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <tileset firstgid="199" name="cave_x2" tilewidth="32" tileheight="64" tilecount="16" columns="16">
+ <image source="../../graphics/tilesets/cave_x2.png" width="512" height="64"/>
+ </tileset>
+ <layer name="Regions" width="13" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA84kyiOLOeMhPGJk2sPIfupbR42d1PTHlIBAHq5GFfUAQAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoB9gxcFmx8HGB4hVR0ugjIM90AAA6DCU7NQBAAA=
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoB/gxcEeqkAFB3ugAQAsLTFH1AEAAA==
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoB9gw8EeBdQFAM2+xmvUAQAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoB/gw8EeBdQFAEag6ZXUAQAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoB8Qw8EeBdQFAJqF6UzUAQAA
+ </data>
+ </layer>
+ <layer name="Output_Error" width="13" height="9" visible="0">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoB9wIZFNrFkDBchxNz0AAJUJJNPUAQAA
+ </data>
+ </layer>
+</map>
diff --git a/Rules/cave/rule_cave_110.tmx b/Rules/cave/rule_cave_110.tmx
new file mode 100644
index 0000000..6656ef6
--- /dev/null
+++ b/Rules/cave/rule_cave_110.tmx
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="13" height="4" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_cave.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="13" height="4">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="4">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,14,0,0,0,14,0,0,0,0,0,0,0,
+0,14,14,0,14,14,0,0,14,0,14,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="4">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,14,0,14,0,0,14,14,0,14,14,0,
+0,0,0,0,0,0,0,14,0,0,0,14,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Over" width="13" height="4">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,86,0,0,0,87,0,0,100,0,102,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/caverules.txt b/Rules/caverules.txt
new file mode 100644
index 0000000..d25355b
--- /dev/null
+++ b/Rules/caverules.txt
@@ -0,0 +1,6 @@
+rule_cave.tmx
+
+// NOTE: Map with Ground only, black - base ground - water. Automap after done.
+// AFTERMAP: Remove grey over tiles, fix narrow transistions (especially wall-water).
+// RANDOMIZE: By adding randomization to rule_cave.tmx AFTER first automap.
+// Add details manually.
diff --git a/Rules/desertcliffs/rule_cave.txt b/Rules/desertcliffs/rule_cave.txt
new file mode 100644
index 0000000..8695922
--- /dev/null
+++ b/Rules/desertcliffs/rule_cave.txt
@@ -0,0 +1,32 @@
+// lines starting with // or // are comments
+// all other lines will be parsed and treated as filenames.
+
+// Rules for desert cliffs
+
+// basic stuff: each rule-tile gets a default tile
+// here is a map attribute set: "DeleteTiles := true" This means to delete all
+// tiles in all touched tileslayers first.
+./rule_cave_001.tmx
+
+// straight walls:
+./rule_cave_002.tmx
+
+// corners at walls
+./rule_cave_003.tmx
+./rule_cave_03.tmx
+//./rule_cave_004.tmx
+//./rule_cave_005.tmx
+
+// rules for entrances, has bad exception handling,
+// so mind where to put entrances. (So avoid putting them near corners)
+//./rule_cave_008.tmx
+
+// all tiles, which are walkable, but have unwalkable neighbors,
+// should be unwalkable
+// example: a decorative stone in a sea
+
+./rule_cave_050.tmx
+
+// error handling: some situations cannot be handled properly with this ruleset:
+// so indicate these situations with the error-tile
+//./rule_cave_100.tmx
diff --git a/Rules/desertcliffs/rule_cave_001.tmx b/Rules/desertcliffs/rule_cave_001.tmx
new file mode 100644
index 0000000..403a7a4
--- /dev/null
+++ b/Rules/desertcliffs/rule_cave_001.tmx
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="1.0.3" orientation="orthogonal" renderorder="right-down" width="17" height="17" tilewidth="32" tileheight="32" nextobjectid="1">
+ <properties>
+ <property name="AutomappingRadius" value="1"/>
+ <property name="DeleteTiles" value="true"/>
+ </properties>
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="14" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_desertcliff.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="78" name="desert1" tilewidth="32" tileheight="32" tilecount="256" columns="16">
+ <image source="../../graphics/tilesets/desert1.png" width="512" height="512"/>
+ </tileset>
+ <layer name="Regions" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,10,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,14,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,43,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,78,0,0,0,0,0,0,0,78,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Fringe" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Over" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Collision" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Error" width="17" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/desertcliffs/rule_cave_002.tmx b/Rules/desertcliffs/rule_cave_002.tmx
new file mode 100644
index 0000000..e7c1140
--- /dev/null
+++ b/Rules/desertcliffs/rule_cave_002.tmx
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="1.0.3" orientation="orthogonal" renderorder="right-down" width="8" height="6" tilewidth="32" tileheight="32" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_desertcliff.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="desert1" tilewidth="32" tileheight="32" tilecount="256" columns="16">
+ <image source="../../graphics/tilesets/desert1.png" width="512" height="512"/>
+ </tileset>
+ <layer name="Regions" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,74,0,0,0,74,74,0,
+0,74,0,74,0,0,0,0,
+0,0,0,74,0,74,74,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,10,0,
+0,10,0,10,0,0,0,0,
+0,0,0,0,0,10,0,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="8" height="6" opacity="0.4">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,10,0,0,0,10,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,10,0,0,10,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground1" width="8" height="6" opacity="0.8">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,176,0,
+0,161,0,113,0,0,0,0,
+0,0,0,129,0,181,0,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Over" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Collision" width="8" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/desertcliffs/rule_cave_003.tmx b/Rules/desertcliffs/rule_cave_003.tmx
new file mode 100644
index 0000000..27e52d8
--- /dev/null
+++ b/Rules/desertcliffs/rule_cave_003.tmx
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="1.0.3" orientation="orthogonal" renderorder="right-down" width="13" height="4" tilewidth="32" tileheight="32" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_desertcliff.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="desert1" tilewidth="32" tileheight="32" tilecount="256" columns="16">
+ <image source="../../graphics/tilesets/desert1.png" width="512" height="512"/>
+ </tileset>
+ <layer name="Regions" width="13" height="4">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="4">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,10,0,10,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,0,10,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="4" opacity="0.7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,10,0,0,0,10,0,10,10,0,10,10,0,
+0,10,10,0,10,10,0,10,0,0,0,10,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground1" width="13" height="4">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,112,0,114,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,96,0,98,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/desertcliffs/rule_cave_004.tmx b/Rules/desertcliffs/rule_cave_004.tmx
new file mode 100644
index 0000000..f4616ee
--- /dev/null
+++ b/Rules/desertcliffs/rule_cave_004.tmx
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="1.0.3" orientation="orthogonal" renderorder="right-down" width="13" height="11" tilewidth="32" tileheight="32" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_desertcliff.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="desert1" tilewidth="32" tileheight="32" tilecount="256" columns="16">
+ <image source="../../graphics/tilesets/desert1.png" width="512" height="512"/>
+ </tileset>
+ <tileset firstgid="334" name="desert2" tilewidth="32" tileheight="32" tilecount="256" columns="16">
+ <image source="../../graphics/tilesets/desert2.png" width="512" height="512"/>
+ </tileset>
+ <layer name="Regions" width="13" height="11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,74,74,0,0,0,74,74,74,74,0,
+0,74,74,74,74,0,0,0,74,74,74,74,0,
+0,74,74,74,74,0,0,0,74,74,74,74,0,
+0,74,74,74,74,0,0,0,74,74,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,74,74,0,0,0,74,74,74,74,0,
+0,74,74,74,74,0,0,0,74,74,74,74,0,
+0,74,74,74,74,0,0,0,74,74,74,74,0,
+0,74,74,74,74,0,0,0,74,74,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,10,10,0,0,0,0,0,10,10,0,0,
+0,0,10,0,0,0,0,0,0,0,10,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,10,0,0,0,0,0,0,0,10,0,0,
+0,0,10,10,0,0,0,0,0,10,10,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="11" opacity="0.7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,10,0,0,0,0,0,10,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,10,0,0,0,0,0,10,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="13" height="11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,195,0,0,
+0,0,0,0,0,0,0,0,0,210,211,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Collision" width="13" height="11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/desertcliffs/rule_cave_005.tmx b/Rules/desertcliffs/rule_cave_005.tmx
new file mode 100644
index 0000000..97cf76d
--- /dev/null
+++ b/Rules/desertcliffs/rule_cave_005.tmx
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="13" height="7" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_desertcliff.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="desert1" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/desert1.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,74,0,74,0,0,0,0,0,0,0,0,
+0,0,74,0,74,0,0,0,74,0,74,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,14,0,14,0,0,
+0,14,14,0,14,14,0,0,14,0,14,0,0,
+0,0,14,0,14,0,0,0,14,0,14,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,14,0,0,0,14,0,
+0,14,0,0,0,14,0,14,0,0,0,14,0,
+0,14,14,0,14,14,0,14,14,0,14,14,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="13" height="7" visible="0">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,95,0,96,0,0,0,0,0,0,0,0,
+0,0,111,0,112,0,0,0,113,0,115,0,0,
+0,0,127,0,128,0,0,0,129,0,131,0,0,
+0,0,143,0,144,0,0,0,145,0,147,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Collision" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,2,0,2,0,0,0,0,0,0,0,0,
+0,0,2,0,2,0,0,0,2,0,2,0,0,
+0,0,2,0,2,0,0,0,2,0,2,0,0,
+0,0,2,0,2,0,0,0,2,0,2,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/desertcliffs/rule_cave_006.tmx b/Rules/desertcliffs/rule_cave_006.tmx
new file mode 100644
index 0000000..9b1cf57
--- /dev/null
+++ b/Rules/desertcliffs/rule_cave_006.tmx
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="6" height="6" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_desertcliff.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="desert1" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/desert1.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,74,0,74,74,0,
+0,74,0,0,0,0,
+0,0,0,0,74,0,
+0,74,74,0,74,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,29,0,0,
+0,29,0,0,0,0,
+0,0,0,0,29,0,
+0,0,29,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,21,0,0,
+0,21,0,0,0,0,
+0,0,0,0,21,0,
+0,0,21,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,13,0,0,
+0,13,0,0,0,0,
+0,0,0,0,13,0,
+0,0,13,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,20,0,0,
+0,20,0,0,0,0,
+0,0,0,0,20,0,
+0,0,20,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,12,0,0,
+0,12,0,0,0,0,
+0,0,0,0,12,0,
+0,0,12,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6" opacity="0.06">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,29,0,0,29,0,
+0,0,0,0,0,0,
+0,0,0,0,0,0,
+0,29,0,0,29,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6" opacity="0.06">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,21,0,0,21,0,
+0,0,0,0,0,0,
+0,0,0,0,0,0,
+0,21,0,0,21,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6" opacity="0.05">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,13,0,0,13,0,
+0,0,0,0,0,0,
+0,0,0,0,0,0,
+0,13,0,0,13,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6" opacity="0.08">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,20,0,0,20,0,
+0,0,0,0,0,0,
+0,0,0,0,0,0,
+0,20,0,0,20,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6" opacity="0.09">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,12,0,0,12,0,
+0,0,0,0,0,0,
+0,0,0,0,0,0,
+0,12,0,0,12,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="6" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,
+0,0,0,189,0,0,
+0,172,0,0,0,0,
+0,0,0,0,204,0,
+0,0,187,0,0,0,
+0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/desertcliffs/rule_cave_007.tmx b/Rules/desertcliffs/rule_cave_007.tmx
new file mode 100644
index 0000000..9171718
--- /dev/null
+++ b/Rules/desertcliffs/rule_cave_007.tmx
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="13" height="7" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_desertcliff.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,74,74,0,74,74,0,74,74,0,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,29,29,0,29,29,0,
+0,0,29,0,29,0,0,29,0,0,0,29,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,29,0,29,0,0,29,0,0,0,29,0,
+0,0,0,0,0,0,0,29,29,0,29,29,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,21,21,0,21,21,0,
+0,0,21,0,21,0,0,21,0,0,0,21,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,21,0,21,0,0,21,0,0,0,21,0,
+0,0,0,0,0,0,0,21,21,0,21,21,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,13,13,0,13,13,0,
+0,0,13,0,13,0,0,13,0,0,0,13,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,13,0,13,0,0,13,0,0,0,13,0,
+0,0,0,0,0,0,0,13,13,0,13,13,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,20,20,0,20,20,0,
+0,0,20,0,20,0,0,20,0,0,0,20,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,20,0,20,0,0,20,0,0,0,20,0,
+0,0,0,0,0,0,0,20,20,0,20,20,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,12,12,0,12,12,0,
+0,0,12,0,12,0,0,12,0,0,0,12,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,12,0,12,0,0,12,0,0,0,12,0,
+0,0,0,0,0,0,0,12,12,0,12,12,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,29,29,0,29,29,0,0,0,0,0,0,0,
+0,29,0,0,0,29,0,0,29,0,29,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,29,0,0,0,29,0,0,29,0,29,0,0,
+0,29,29,0,29,29,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,21,21,0,21,21,0,0,0,0,0,0,0,
+0,21,0,0,0,21,0,0,21,0,21,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,21,0,0,0,21,0,0,21,0,21,0,0,
+0,21,21,0,21,21,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,13,13,0,13,13,0,0,0,0,0,0,0,
+0,13,0,0,0,13,0,0,13,0,13,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,13,0,0,0,13,0,0,13,0,13,0,0,
+0,13,13,0,13,13,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,20,20,0,20,20,0,0,0,0,0,0,0,
+0,20,0,0,0,20,0,0,20,0,20,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,20,0,0,0,20,0,0,20,0,20,0,0,
+0,20,20,0,20,20,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7" opacity="0.11">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,12,12,0,12,12,0,0,0,0,0,0,0,
+0,12,0,0,0,12,0,0,12,0,12,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,12,0,0,0,12,0,0,12,0,12,0,0,
+0,12,12,0,12,12,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="13" height="7">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,169,0,0,0,170,0,
+0,0,171,0,173,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,203,0,205,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,185,0,0,0,186,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/desertcliffs/rule_cave_008.tmx b/Rules/desertcliffs/rule_cave_008.tmx
new file mode 100644
index 0000000..14a69e3
--- /dev/null
+++ b/Rules/desertcliffs/rule_cave_008.tmx
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="2018.02.14" orientation="orthogonal" renderorder="right-down" width="13" height="17" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_desertcliff.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/cave.png" width="512" height="256"/>
+ </tileset>
+ <tileset firstgid="206" name="cave_x2" tilewidth="32" tileheight="64" tilecount="16" columns="16">
+ <image source="../../graphics/tilesets/cave_x2.png" width="512" height="64"/>
+ </tileset>
+ <layer name="Regions" width="13" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,74,74,0,74,74,74,0,74,74,74,0,
+0,74,74,74,0,74,74,74,0,74,74,74,0,
+0,74,74,74,0,74,74,74,0,74,74,74,0,
+0,74,74,74,0,74,74,74,0,74,74,74,0,
+0,74,74,74,0,74,74,74,0,74,74,74,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,74,74,74,0,0,0,0,0,
+0,0,0,0,0,74,74,74,0,0,0,0,0,
+0,0,0,0,0,74,74,74,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,14,14,14,0,14,14,14,0,14,14,14,0,
+0,14,14,14,0,14,14,14,0,14,14,14,0,
+0,14,14,14,0,14,14,14,0,14,14,14,0,
+0,14,31,14,0,14,38,14,0,14,30,14,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,14,22,14,0,0,0,0,0,
+0,0,0,0,0,14,14,14,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="17">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,23,0,0,0,15,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="17" opacity="0.36">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,14,14,14,0,14,14,14,0,14,14,14,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,14,14,14,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="13" height="17" visible="0">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,98,98,98,0,98,98,98,0,98,98,98,0,
+0,114,114,114,0,114,114,114,0,114,114,114,0,
+0,164,165,166,0,139,140,141,0,91,92,93,0,
+0,180,181,182,0,155,156,157,0,107,108,109,0,
+0,0,0,0,0,0,0,0,0,123,124,125,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,82,82,82,0,0,0,0,0,
+0,0,0,0,0,82,82,82,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Over" width="13" height="17" visible="0">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,101,101,101,0,0,0,0,0,
+0,0,0,0,0,78,78,78,0,0,0,0,0,
+0,0,0,0,0,210,211,212,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/desertcliffs/rule_cave_03.tmx b/Rules/desertcliffs/rule_cave_03.tmx
new file mode 100644
index 0000000..b7ddb3a
--- /dev/null
+++ b/Rules/desertcliffs/rule_cave_03.tmx
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="1.0.3" orientation="orthogonal" renderorder="right-down" width="13" height="6" tilewidth="32" tileheight="32" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_desertcliff.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="desert1" tilewidth="32" tileheight="32" tilecount="256" columns="16">
+ <image source="../../graphics/tilesets/desert1.png" width="512" height="512"/>
+ </tileset>
+ <layer name="Regions" width="13" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,74,0,0,74,74,0,74,74,0,0,0,0,
+0,74,0,0,74,74,0,74,74,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="13" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,14,0,0,14,0,0,0,14,0,0,0,0,
+0,14,0,0,14,0,0,0,14,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="6" opacity="0.6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,14,0,14,0,0,0,0,0,
+0,0,0,0,0,14,0,14,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground" width="13" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,78,0,0,0,78,0,0,0,0,
+0,0,0,0,78,0,0,0,78,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Ground1" width="13" height="6" opacity="0.8">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,129,0,0,130,0,0,0,128,0,0,0,0,
+0,129,0,0,130,0,0,0,128,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Over" width="13" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Collision" width="13" height="6">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,2,0,0,2,0,0,0,2,0,0,0,0,
+0,2,0,0,2,0,0,0,2,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/desertcliffs/rule_cave_050.tmx b/Rules/desertcliffs/rule_cave_050.tmx
new file mode 100644
index 0000000..aabf7fe
--- /dev/null
+++ b/Rules/desertcliffs/rule_cave_050.tmx
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="1.0.3" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_desertcliff.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <layer name="Regions" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,74,0,0,
+0,74,74,74,0,
+0,0,74,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Input_set" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,14,0,0,
+0,14,0,14,0,
+0,0,14,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Output_Collision" width="5" height="5">
+ <data encoding="csv">
+0,0,0,0,0,
+0,0,0,0,0,
+0,0,2,0,0,
+0,0,0,0,0,
+0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/desertcliffs/rule_cave_100.tmx b/Rules/desertcliffs/rule_cave_100.tmx
new file mode 100644
index 0000000..bd25bb0
--- /dev/null
+++ b/Rules/desertcliffs/rule_cave_100.tmx
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="1.0.3" orientation="orthogonal" renderorder="right-down" width="13" height="9" tilewidth="32" tileheight="32" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="10" name="set" tilewidth="32" tileheight="32" tilecount="64" columns="8">
+ <image source="../tilesets/set_desertcliff.png" width="256" height="256"/>
+ </tileset>
+ <tileset firstgid="74" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="78" name="desert1" tilewidth="32" tileheight="32" tilecount="256" columns="16">
+ <image source="../../graphics/tilesets/desert1.png" width="512" height="512"/>
+ </tileset>
+ <tileset firstgid="334" name="desert2" tilewidth="32" tileheight="64" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/desert2.png" width="512" height="512"/>
+ </tileset>
+ <layer name="Regions" width="13" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoC7wokCPFxKmpT3DHQAAJSFKadQBAAA=
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgGHjAh4NNrJ5RgAoA0841FdQBAAA=
+ </data>
+ </layer>
+ <layer name="Output_Error" width="13" height="9" visible="0">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgGHjgjYNNrJ5RgAoAKzmZutQBAAA=
+ </data>
+ </layer>
+</map>
diff --git a/Rules/icecave/rule_icecave.txt b/Rules/icecave/rule_icecave.txt
new file mode 100644
index 0000000..c4580a9
--- /dev/null
+++ b/Rules/icecave/rule_icecave.txt
@@ -0,0 +1,34 @@
+# lines starting with # or // are comments
+# all other lines will be parsed and treated as filenames.
+
+# ice caves... similar to usual caves
+
+# basic stuff: each rule-tile gets a default tile
+./rule_icecave_001.tmx
+
+# straight walls:
+./rule_icecave_002.tmx
+
+# corners at walls
+./rule_icecave_003.tmx
+./rule_icecave_004.tmx
+./rule_icecave_005.tmx
+
+# frozen water straight waterside
+./rule_icecave_006.tmx
+
+# frozen water: corners at waterside
+./rule_icecave_007.tmx
+
+# carpet straight
+./rule_icecave_009.tmx
+
+# carpet corners
+./rule_icecave_010.tmx
+
+# entrances and exits
+./rule_icecave_020.tmx
+
+# error handling: some situations cannot be handled properly with this ruleset:
+# so indicate these situations with the error-tile
+./rule_icecave_100.tmx
diff --git a/Rules/icecave/rule_icecave_001.tmx b/Rules/icecave/rule_icecave_001.tmx
new file mode 100644
index 0000000..c7c31f9
--- /dev/null
+++ b/Rules/icecave/rule_icecave_001.tmx
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="" orientation="orthogonal" renderorder="right-down" width="9" height="9" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <properties>
+ <property name="AutoMappingRadius" value="1"/>
+ <property name="DeleteTiles" value="true"/>
+ </properties>
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="2" columns="2">
+ <image source="../../graphics/tilesets/collision.png" width="64" height="32"/>
+ </tileset>
+ <tileset firstgid="3" name="set_icecave" tilewidth="32" tileheight="32" tilecount="16" columns="4">
+ <image source="../tilesets/set_icecave.png" width="128" height="128"/>
+ </tileset>
+ <tileset firstgid="19" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="23" name="icecave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/icecave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="9" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA4IE6CJBbj0DRZzyPEXALXctVNEAQAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="9" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA4wQ2kWKM0KpdmI1A8D7FCaA02ci0RzuKE0D5o4H4nm8ENpASgtCKWFSDADALg2bV1EAQAA
+ </data>
+ </layer>
+ <layer name="Output_Ground" width="9" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA5oQ2lpNLqVSP0wYASlJdDEyTVHkkrmwPwjgkYTAwCSHrZpRAEAAA==
+ </data>
+ </layer>
+ <layer name="Output_Collision" width="9" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA4wEeBTCkg1D5d6cs2hxD8ARG/fIUQBAAA=
+ </data>
+ </layer>
+ <layer name="Output_Fringe" width="9" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA8YkKmPWubhUh9AojkBaDQ5AACqeTmWRAEAAA==
+ </data>
+ </layer>
+ <layer name="Output_Error" width="9" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgGAWUAgAV+RUNRAEAAA==
+ </data>
+ </layer>
+ <layer name="Output_Over" width="9" height="9">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgGAWUAgAV+RUNRAEAAA==
+ </data>
+ </layer>
+</map>
diff --git a/Rules/icecave/rule_icecave_002.tmx b/Rules/icecave/rule_icecave_002.tmx
new file mode 100644
index 0000000..0df731d
--- /dev/null
+++ b/Rules/icecave/rule_icecave_002.tmx
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="" orientation="orthogonal" renderorder="right-down" width="9" height="6" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="2" columns="2">
+ <image source="../../graphics/tilesets/collision.png" width="64" height="32"/>
+ </tileset>
+ <tileset firstgid="3" name="set_icecave" tilewidth="32" tileheight="32" tilecount="16" columns="4">
+ <image source="../tilesets/set_icecave.png" width="128" height="128"/>
+ </tileset>
+ <tileset firstgid="19" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="23" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/icecave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="9" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA4Ik6FGGAmjq8FnHroabOYQAgAtyHRU2AAAAA==
+ </data>
+ </layer>
+ <layer name="Input_set" width="9" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA4wk6mGGU2cGHPQ1aKbQQwAAE/ZWtvYAAAA
+ </data>
+ </layer>
+ <layer name="InputNot_set" width="9" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoB1gprIcPj3oAAAVANpz2AAAAA==
+ </data>
+ </layer>
+ <layer name="Output_Ground" width="9" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA5YE6HGG4uYFho/mkj70IEOieoBwW8e8tgAAAA=
+ </data>
+ </layer>
+ <layer name="Output_Over" width="9" height="6" opacity="0.51">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgGBigR2PzAWLr5ITYAAAA
+ </data>
+ </layer>
+ <layer name="Output_Collision" width="9" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA4wkamGCU2cGHPQ1aKbQQwAAFG7H+7YAAAA
+ </data>
+ </layer>
+</map>
diff --git a/Rules/icecave/rule_icecave_003.tmx b/Rules/icecave/rule_icecave_003.tmx
new file mode 100644
index 0000000..3dda319
--- /dev/null
+++ b/Rules/icecave/rule_icecave_003.tmx
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="" orientation="orthogonal" renderorder="right-down" width="13" height="4" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="2" columns="2">
+ <image source="../../graphics/tilesets/collision.png" width="64" height="32"/>
+ </tileset>
+ <tileset firstgid="3" name="set_icecave" tilewidth="32" tileheight="32" tilecount="16" columns="4">
+ <image source="../tilesets/set_icecave.png" width="128" height="128"/>
+ </tileset>
+ <tileset firstgid="19" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="23" name="icecave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/icecave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="13" height="4">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA8IQzGxbHL1kAoA+YlsANAAAAA=
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="4">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA8wE+Dj0sOMhc2AJk4JAAB87igk0AAAAA==
+ </data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="4">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIB8wo9EwNjMWNjHm4OKTAgA8hSQr0AAAAA==
+ </data>
+ </layer>
+ <layer name="Output_Over" width="13" height="4">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA/Io/EVkNi6UFqfTLMpAQCgAAh70AAAAA==
+ </data>
+ </layer>
+</map>
diff --git a/Rules/icecave/rule_icecave_004.tmx b/Rules/icecave/rule_icecave_004.tmx
new file mode 100644
index 0000000..d7c406b
--- /dev/null
+++ b/Rules/icecave/rule_icecave_004.tmx
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="" orientation="orthogonal" renderorder="right-down" width="7" height="6" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="2" columns="2">
+ <image source="../../graphics/tilesets/collision.png" width="64" height="32"/>
+ </tileset>
+ <tileset firstgid="3" name="set_icecave" tilewidth="32" tileheight="32" tilecount="16" columns="4">
+ <image source="../tilesets/set_icecave.png" width="128" height="128"/>
+ </tileset>
+ <tileset firstgid="19" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="23" name="cave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/icecave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="7" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgwA+EoRidTSs5XAAAY11lnagAAAA=
+ </data>
+ </layer>
+ <layer name="Input_set" width="7" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgwA+YoRidTYkcA5ocMQAANSMN1qgAAAA=
+ </data>
+ </layer>
+ <layer name="InputNot_set" width="7" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoD9gJsDHBgB9Q1zQqAAAAA==
+ </data>
+ </layer>
+ <layer name="Output_Ground" width="7" height="6" visible="0">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIAzsobQtFjl/KO2LRS4eSscSYQchAAB9+9lsqAAAAA==
+ </data>
+ </layer>
+ <layer name="Output_Collision" width="7" height="6" visible="0">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgwA+YoBidTYkcA5ocMQAAqilRnqgAAAA=
+ </data>
+ </layer>
+</map>
diff --git a/Rules/icecave/rule_icecave_005.tmx b/Rules/icecave/rule_icecave_005.tmx
new file mode 100644
index 0000000..b365906
--- /dev/null
+++ b/Rules/icecave/rule_icecave_005.tmx
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="" orientation="orthogonal" renderorder="right-down" width="13" height="7" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="2" columns="2">
+ <image source="../../graphics/tilesets/collision.png" width="64" height="32"/>
+ </tileset>
+ <tileset firstgid="3" name="set_icecave" tilewidth="32" tileheight="32" tilecount="16" columns="4">
+ <image source="../tilesets/set_icecave.png" width="128" height="128"/>
+ </tileset>
+ <tileset firstgid="19" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="23" name="icecave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/icecave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIB8Io9GU6MEnLkwCm556SAUAWbn4FWwBAAA=
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIB8wo9GU6MEnzoyFTch+Uu2hBwAAZhB9h2wBAAA=
+ </data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgGJqAGQ8flxwhPcwksMkBAHO5eppsAQAA
+ </data>
+ </layer>
+ <layer name="Output_Ground" width="13" height="7" visible="0">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIB9oQGlNEvRYQGlLNHErKG2DRY8HlPZEE/eC0j5Y9ERA6Ug08SgoHYPHjbQCAKQ1RdVsAQAA
+ </data>
+ </layer>
+ <layer name="Output_Collision" width="13" height="7" visible="0">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIB8wodGU6MFnFr300BoAAErWG9FsAQAA
+ </data>
+ </layer>
+</map>
diff --git a/Rules/icecave/rule_icecave_006.tmx b/Rules/icecave/rule_icecave_006.tmx
new file mode 100644
index 0000000..d796ca3
--- /dev/null
+++ b/Rules/icecave/rule_icecave_006.tmx
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="" orientation="orthogonal" renderorder="right-down" width="6" height="6" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="2" columns="2">
+ <image source="../../graphics/tilesets/collision.png" width="64" height="32"/>
+ </tileset>
+ <tileset firstgid="3" name="set_icecave" tilewidth="32" tileheight="32" tilecount="16" columns="4">
+ <image source="../tilesets/set_icecave.png" width="128" height="128"/>
+ </tileset>
+ <tileset firstgid="19" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="23" name="icecave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/icecave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="6" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgwA2EkWhhLOL4ALp6YSzi2AAALrUFg5AAAAA=
+ </data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIAzYCPCppQcEAItPfPCQAAAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIAy4CPCppQcEAOTd4SuQAAAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIAz4CPCppQcEAMFTlWKQAAAA
+ </data>
+ </layer>
+ <layer name="Output_Ground" width="6" height="6" visible="0">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIAza0PilROiZisZvIUIPCAAASt1u8JAAAAA=
+ </data>
+ </layer>
+</map>
diff --git a/Rules/icecave/rule_icecave_007.tmx b/Rules/icecave/rule_icecave_007.tmx
new file mode 100644
index 0000000..a1670d4
--- /dev/null
+++ b/Rules/icecave/rule_icecave_007.tmx
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="" orientation="orthogonal" renderorder="right-down" width="13" height="7" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="2" columns="2">
+ <image source="../../graphics/tilesets/collision.png" width="64" height="32"/>
+ </tileset>
+ <tileset firstgid="3" name="set_icecave" tilewidth="32" tileheight="32" tilecount="16" columns="4">
+ <image source="../tilesets/set_icecave.png" width="128" height="128"/>
+ </tileset>
+ <tileset firstgid="19" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="23" name="icecave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/icecave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA8IQzGxbHL1DCe3AQDj6TXRbAEAAA==
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoC5gg2J0NrI8A5o4LjWUuoMa9hDyDykAANyNKXlsAQAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoC7ggmJ0NrI8A5o4LjWUuoMa9hDyDykAAN2upiFsAQAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoC7gg2J0NrI8A5o4LjWUuoMa9hDyDykAACKwIxZsAQAA
+ </data>
+ </layer>
+ <layer name="Output_Ground" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoC4oQuMXo/FLoHQZle1FB1Og9DQKzWlC4zdTYBYAacXSPGwBAAA=
+ </data>
+ </layer>
+</map>
diff --git a/Rules/icecave/rule_icecave_009.tmx b/Rules/icecave/rule_icecave_009.tmx
new file mode 100644
index 0000000..31c7132
--- /dev/null
+++ b/Rules/icecave/rule_icecave_009.tmx
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="" orientation="orthogonal" renderorder="right-down" width="6" height="6" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="2" columns="2">
+ <image source="../../graphics/tilesets/collision.png" width="64" height="32"/>
+ </tileset>
+ <tileset firstgid="3" name="set_icecave" tilewidth="32" tileheight="32" tilecount="16" columns="4">
+ <image source="../tilesets/set_icecave.png" width="128" height="128"/>
+ </tileset>
+ <tileset firstgid="19" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="23" name="icecave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/icecave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="6" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgwA2EkWhhLOL4ALp6YSzi2AAALrUFg5AAAAA=
+ </data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIAzYCfCppQcEAJIt/ZSQAAAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIAy4CfCppQcEAP2/YE+QAAAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="6" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIAz4CfCppQcEANgxFAaQAAAA
+ </data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgwA3YcbBJBaSYAwA8zLY9kAAAAA==
+ </data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgwA24cbBJBaSYAwCnbhfQkAAAAA==
+ </data>
+ </layer>
+ <layer name="InputNot_set" width="6" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgwA34cbBJBaSYAwARDac9kAAAAA==
+ </data>
+ </layer>
+ <layer name="Output_Ground" width="6" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIAyM0fhKROhxQuMbEqEHBABmYFlZkAAAAA==
+ </data>
+ </layer>
+</map>
diff --git a/Rules/icecave/rule_icecave_010.tmx b/Rules/icecave/rule_icecave_010.tmx
new file mode 100644
index 0000000..13b9120
--- /dev/null
+++ b/Rules/icecave/rule_icecave_010.tmx
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="" orientation="orthogonal" renderorder="right-down" width="13" height="7" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="2" columns="2">
+ <image source="../../graphics/tilesets/collision.png" width="64" height="32"/>
+ </tileset>
+ <tileset firstgid="3" name="set_icecave" tilewidth="32" tileheight="32" tilecount="16" columns="4">
+ <image source="../tilesets/set_icecave.png" width="128" height="128"/>
+ </tileset>
+ <tileset firstgid="19" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="23" name="icecave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/icecave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA8IQzGxbHL1DCe3AQDj6TXRbAEAAA==
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoC5gh2J0NrI8A5o4LjWUuoMa9hDyDykAANMIrO9sAQAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoC7ghmJ0NrI8A5o4LjWUuoMa9hDyDykAANIrI7dsAQAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoC7gh2J0NrI8A5o4LjWUuoMa9hDyDykAAC01poBsAQAA
+ </data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA+wQzE6m5AeXHx2HGrIAeTYQ45/SAUAzKE5fmwBAAA=
+ </data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA9wQzE6m5AeXHxuHGrIAeTYQ45/SAUATchkcWwBAAA=
+ </data>
+ </layer>
+ <layer name="InputNot_set" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA/wQzE6m5AeXHx+HGrIAeTYQ45/SAUADe2AwmwBAAA=
+ </data>
+ </layer>
+ <layer name="Output_Ground" width="13" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoC4IQuMHo/EVobQyle1FB45Q2plCc5LQ+MkUmAUA1eCGkGwBAAA=
+ </data>
+ </layer>
+</map>
diff --git a/Rules/icecave/rule_icecave_020.tmx b/Rules/icecave/rule_icecave_020.tmx
new file mode 100644
index 0000000..ff74b40
--- /dev/null
+++ b/Rules/icecave/rule_icecave_020.tmx
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="" orientation="orthogonal" renderorder="right-down" width="9" height="7" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="2" columns="2">
+ <image source="../../graphics/tilesets/collision.png" width="64" height="32"/>
+ </tileset>
+ <tileset firstgid="3" name="set_icecave" tilewidth="32" tileheight="32" tilecount="16" columns="4">
+ <image source="../tilesets/set_icecave.png" width="128" height="128"/>
+ </tileset>
+ <tileset firstgid="19" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="23" name="icecave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/icecave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="9" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA4II2F6q8Gmhxg1hAAAKDCfLPwAAAA=
+ </data>
+ </layer>
+ <layer name="Input_set" width="9" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA4wI2F6qBEkoAabHiES9QAA7GlTvPwAAAA=
+ </data>
+ </layer>
+ <layer name="Output_Ground" width="9" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA5oI2FcwBaI7YDYHo+aECAOBeIwPGpSgDgViNOIdBsISCNhYgEAc8NUGPwAAAA=
+ </data>
+ </layer>
+ <layer name="Output_Over" width="9" height="7">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgGPxAH4gTgFgXjxp5INYDYgUSzAUA1uf1CPwAAAA=
+ </data>
+ </layer>
+ <layer name="Output_Collision" width="9" height="7" visible="0">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoC9gQsL41DAQUEMNPQBW4FMC/AAAAA==
+ </data>
+ </layer>
+</map>
diff --git a/Rules/icecave/rule_icecave_100.tmx b/Rules/icecave/rule_icecave_100.tmx
new file mode 100644
index 0000000..a7c9936
--- /dev/null
+++ b/Rules/icecave/rule_icecave_100.tmx
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" tiledversion="" orientation="orthogonal" renderorder="right-down" width="9" height="6" tilewidth="32" tileheight="32" infinite="0" nextobjectid="1">
+ <tileset firstgid="1" name="collision" tilewidth="32" tileheight="32" tilecount="2" columns="2">
+ <image source="../../graphics/tilesets/collision.png" width="64" height="32"/>
+ </tileset>
+ <tileset firstgid="3" name="set_icecave" tilewidth="32" tileheight="32" tilecount="16" columns="4">
+ <image source="../tilesets/set_icecave.png" width="128" height="128"/>
+ </tileset>
+ <tileset firstgid="19" name="set_rules" tilewidth="32" tileheight="32" tilecount="4" columns="2">
+ <image source="../tilesets/set_rules.png" width="64" height="64"/>
+ </tileset>
+ <tileset firstgid="23" name="icecave" tilewidth="32" tileheight="32" tilecount="128" columns="16">
+ <image source="../../graphics/tilesets/icecave.png" width="512" height="256"/>
+ </tileset>
+ <layer name="Regions" width="9" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgIA4IEyEujEQLo/FJMQdZDJc5hAAAgNct+9gAAAA=
+ </data>
+ </layer>
+ <layer name="Input_set" width="9" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoB5gw8FmJkIvLjXE6MUGAC5qS5vYAAAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="9" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoB7gwsFmJkIvLjXE6MUGAFQuCaTYAAAA
+ </data>
+ </layer>
+ <layer name="Input_set" width="9" height="6">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoB7gw8FmJkIvLjXE6MUGAL3v5wfYAAAA
+ </data>
+ </layer>
+ <layer name="Output_Error" width="9" height="6" visible="0">
+ <data encoding="base64" compression="gzip">
+ H4sIAAAAAAAAA2NgoB4QIYJNjF5ixAkBAOvVVsfYAAAA
+ </data>
+ </layer>
+</map>
diff --git a/Rules/rule_cave.tmx b/Rules/rule_cave.tmx
new file mode 100644
index 0000000..e134398
--- /dev/null
+++ b/Rules/rule_cave.tmx
@@ -0,0 +1,375 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.2" tiledversion="1.2.2" orientation="orthogonal" renderorder="right-down" width="32" height="32" tilewidth="32" tileheight="32" infinite="0" nextlayerid="243" nextobjectid="1">
+ <tileset firstgid="1" name="hurns-cave" tilewidth="32" tileheight="32" tilecount="256" columns="16">
+ <image source="../graphics/tilesets/evol/cave.png" width="512" height="512"/>
+ </tileset>
+ <tileset firstgid="257" name="artis-indoor" tilewidth="32" tileheight="32" tilecount="256" columns="16">
+ <image source="../graphics/tilesets/artis-indoor.png" width="512" height="512"/>
+ </tileset>
+ <tileset firstgid="513" name="collision" tilewidth="32" tileheight="32" tilecount="9" columns="3">
+ <image source="../graphics/tilesets/collision.png" width="96" height="96"/>
+ </tileset>
+ <tileset firstgid="522" name="cave" tilewidth="32" tileheight="32" tilecount="256" columns="16">
+ <image source="../graphics/tilesets/evol/cave.png" width="512" height="512"/>
+ </tileset>
+ <layer id="15" name="regions_input" width="32" height="32">
+ <data encoding="csv">
+31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,0,31,0,31,0,0,0,0,0,0,0,0,0,0,
+31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,31,0,31,70,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,31,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,70,0,0,70,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+75,0,0,0,0,0,0,0,70,70,0,70,70,0,0,0,0,0,0,70,70,70,0,0,70,70,0,0,0,0,0,0,
+75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,70,70,0,0,70,70,0,0,0,0,0,0,
+75,0,70,70,0,70,70,0,70,0,0,70,70,0,0,0,0,0,0,70,0,0,0,0,70,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,70,70,0,0,70,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,70,70,0,0,70,70,0,0,0,0,0,0,
+70,0,70,0,70,70,0,70,70,0,0,0,0,0,0,0,0,0,0,70,70,70,0,0,70,70,0,0,0,0,0,0,
+70,0,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,0,0,0,70,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+70,70,0,70,70,0,70,70,0,70,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+70,70,0,70,70,0,70,70,0,70,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+70,70,0,70,70,0,70,70,0,70,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+70,70,0,70,70,0,70,70,0,70,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,0,0,70,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="40" name="regions_output" width="32" height="32">
+ <data encoding="csv">
+31,0,0,0,31,0,31,0,0,0,31,0,31,0,0,0,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,70,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+70,0,0,70,0,70,0,0,0,70,0,0,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+70,0,0,70,0,70,0,0,0,70,0,0,0,0,0,0,0,0,0,0,70,0,0,0,0,185,0,0,0,0,0,0,
+70,0,0,70,0,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+70,0,0,70,0,70,0,0,70,0,0,0,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,0,0,0,70,0,0,0,0,0,0,0,
+0,0,70,0,70,0,0,0,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,70,0,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,70,0,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+70,0,0,0,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,70,0,0,0,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,0,0,70,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="11" name="input_Ground 1" width="32" height="32">
+ <data encoding="csv">
+2,2,0,2,2,0,2,2,0,2,2,0,2,146,0,146,2,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,
+2,146,0,146,2,0,146,146,0,146,146,0,146,146,0,146,146,0,146,2,0,2,146,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,11,0,0,146,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+146,0,0,0,0,0,0,0,146,50,0,146,11,0,0,0,0,0,0,146,146,146,0,0,146,2,0,0,0,0,0,0,
+146,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,146,11,11,0,0,11,2,0,0,0,0,0,0,
+2,0,50,2,0,2,50,0,11,0,0,2,146,0,0,0,0,0,0,11,0,0,0,0,11,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,50,146,0,0,11,146,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,146,146,146,0,0,2,146,0,0,0,0,0,0,
+2,0,114,0,114,2,0,2,114,0,0,0,0,0,0,0,0,0,0,11,11,146,0,0,2,11,0,0,0,0,0,0,
+114,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,11,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+2,113,0,115,2,0,130,114,0,114,130,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+98,114,0,114,98,0,2,113,0,115,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+130,2,0,2,113,0,2,2,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+2,2,0,2,2,0,115,2,0,2,98,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,0,0,146,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="226" name="input_Ground 1" width="32" height="32">
+ <data encoding="csv">
+114,114,0,114,114,0,114,114,0,114,114,0,114,0,0,0,114,0,0,114,0,114,0,0,0,0,0,0,0,0,0,0,
+114,0,0,0,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,0,114,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,0,114,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,0,0,0,0,0,0,
+50,0,0,0,0,49,0,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+51,97,0,99,49,0,129,114,0,114,131,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+97,114,0,114,99,0,0,129,0,131,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+115,0,0,0,130,0,49,50,0,50,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,98,49,0,51,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="227" name="input_Ground 1" width="32" height="32">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,146,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,146,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,
+49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,98,0,98,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="232" name="input_Ground 1" width="32" height="32">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="12" name="output_Ground 1" width="32" height="32">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+50,0,0,51,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,130,0,115,0,0,0,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+98,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,101,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,117,0,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+131,0,0,0,129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,99,0,0,0,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,0,0,146,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="13" name="output_Ground 2" width="32" height="32">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,59,0,58,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="44" name="output_Over 1" width="32" height="32">
+ <data encoding="csv">
+177,0,0,0,180,0,179,0,0,0,178,0,152,0,0,0,153,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,147,0,145,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+162,0,0,163,0,161,0,0,0,147,0,0,149,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,43,0,42,0,0,0,147,0,0,0,0,0,0,0,0,0,0,149,0,0,0,149,163,0,0,0,0,0,0,
+0,0,0,70,0,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,145,0,0,0,148,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,145,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,148,0,0,0,161,148,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="242" name="output_Collision" width="32" height="32">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,514,0,514,0,0,0,514,0,514,0,0,0,0,0,0,0,0,0,0,
+0,514,0,514,0,0,514,514,0,514,514,0,514,514,0,514,514,0,514,514,0,514,514,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,514,0,514,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+514,0,0,514,0,514,0,0,0,514,0,0,514,0,0,0,0,0,0,514,514,514,0,0,514,514,0,0,0,0,0,0,
+514,0,0,514,0,514,0,0,0,0,0,0,0,0,0,0,0,0,0,514,514,514,0,0,514,514,0,0,0,0,0,0,
+514,0,514,514,0,514,514,0,514,0,0,0,0,0,0,0,0,0,0,514,0,0,0,0,514,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,514,0,0,0,514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,514,514,514,0,0,514,514,0,0,0,0,0,0,
+0,0,0,0,516,0,0,0,516,0,0,0,0,0,0,0,0,0,0,514,514,514,0,0,514,514,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,514,0,0,0,514,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,516,0,516,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,516,0,516,516,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,516,0,0,514,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/Rules/rules.txt b/Rules/rules.txt
new file mode 100644
index 0000000..4bd5986
--- /dev/null
+++ b/Rules/rules.txt
@@ -0,0 +1,17 @@
+# Comment the rules which are not in use
+
+# Micksha Cave
+#caverules.txt
+
+# Cave Rules
+cave/rule_cave.txt
+
+# Castle Rules (bad tileset)
+#castle_indoor/rule_castle_indoor.txt
+
+# Ice Cave Rules
+#icecave/rule_icecave.txt
+
+# Desert Cliffs Rules
+#desertcliffs/rule_cave.txt
+
diff --git a/Rules/tilesets/set_castle_indoor.png b/Rules/tilesets/set_castle_indoor.png
new file mode 100644
index 0000000..7f02def
--- /dev/null
+++ b/Rules/tilesets/set_castle_indoor.png
Binary files differ
diff --git a/Rules/tilesets/set_cave.png b/Rules/tilesets/set_cave.png
new file mode 100644
index 0000000..aa6e1bf
--- /dev/null
+++ b/Rules/tilesets/set_cave.png
Binary files differ
diff --git a/Rules/tilesets/set_desertcliff.png b/Rules/tilesets/set_desertcliff.png
new file mode 100644
index 0000000..3ead83d
--- /dev/null
+++ b/Rules/tilesets/set_desertcliff.png
Binary files differ
diff --git a/Rules/tilesets/set_hlw.png b/Rules/tilesets/set_hlw.png
new file mode 100644
index 0000000..ec17f1e
--- /dev/null
+++ b/Rules/tilesets/set_hlw.png
Binary files differ
diff --git a/Rules/tilesets/set_icecave.png b/Rules/tilesets/set_icecave.png
new file mode 100644
index 0000000..8a1319b
--- /dev/null
+++ b/Rules/tilesets/set_icecave.png
Binary files differ
diff --git a/Rules/tilesets/set_icemountain.png b/Rules/tilesets/set_icemountain.png
new file mode 100644
index 0000000..7a8613e
--- /dev/null
+++ b/Rules/tilesets/set_icemountain.png
Binary files differ
diff --git a/Rules/tilesets/set_rules.png b/Rules/tilesets/set_rules.png
new file mode 100644
index 0000000..bf7f93c
--- /dev/null
+++ b/Rules/tilesets/set_rules.png
Binary files differ
diff --git a/Rules/tilesets/set_woodland.png b/Rules/tilesets/set_woodland.png
new file mode 100644
index 0000000..9661f07
--- /dev/null
+++ b/Rules/tilesets/set_woodland.png
Binary files differ
diff --git a/adler32/Makefile b/adler32/Makefile
new file mode 100644
index 0000000..42f101a
--- /dev/null
+++ b/adler32/Makefile
@@ -0,0 +1,16 @@
+prefix=/usr/local
+bindir=${prefix}/bin
+
+LDLIBS=-lz
+
+all: adler32
+
+adler32: adler32.c
+
+clean:
+ rm -f adler32
+
+install:
+ install -D adler32 ${bindir}/
+
+.PHONY: clean install
diff --git a/adler32/adler32 b/adler32/adler32
new file mode 100755
index 0000000..a934ff7
--- /dev/null
+++ b/adler32/adler32
Binary files differ
diff --git a/adler32/adler32.c b/adler32/adler32.c
new file mode 100644
index 0000000..5dd7e4c
--- /dev/null
+++ b/adler32/adler32.c
@@ -0,0 +1,68 @@
+/*
+ * adler32.c (c) 2006 Bjorn Lindeijer
+ * License: GPL, v2 or later
+ *
+ * Calculates Adler-32 checksums for all files passed as argument.
+ *
+ * Usage: adler32 [file]...
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <zlib.h>
+
+/**
+ * Calculates the Adler-32 checksum for the given file.
+ */
+unsigned long fadler32(FILE *file)
+{
+ // Obtain file size
+ fseek(file, 0, SEEK_END);
+ long fileSize = ftell(file);
+ rewind(file);
+
+ // Calculate Adler-32 checksum
+ char *buffer = (char*) malloc(fileSize);
+ fread(buffer, 1, fileSize, file);
+ unsigned long adler = adler32(0L, Z_NULL, 0);
+ adler = adler32(adler, (Bytef*) buffer, fileSize);
+ free(buffer);
+
+ return adler;
+}
+
+/**
+ * Prints out usage and exists.
+ */
+void print_usage()
+{
+ printf("Usage: adler32 [file]...\n");
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ int i; /**< Loops through arguments. */
+
+ if (argc == 1)
+ {
+ print_usage();
+ }
+
+ for (i = 1; i < argc; ++i)
+ {
+ FILE *file = fopen(argv[i], "r");
+
+ if (!file)
+ {
+ printf("Error while opening '%s' for reading!\n", argv[i]);
+ exit(1);
+ }
+
+ unsigned long adler = fadler32(file);
+ printf("%s %lx\n", argv[i], adler);
+ fclose(file);
+ }
+
+ return 0;
+}
diff --git a/client/aurora.py b/client/aurora.py
new file mode 100755
index 0000000..38ed174
--- /dev/null
+++ b/client/aurora.py
@@ -0,0 +1,37 @@
+#!/usr/bin/python2.7
+
+# Setup
+events=["Expo", "Fishing", "Kamelot", "Regnum", "Raid", "Tower", "Mining",
+ "Anniversary", "Christmas", "Easter", "Patrick", "Thanksgiving", "Valentine",
+ "Olympics", "Candor", "Worker"]
+
+# Functions
+def headers(val):
+ return '\n\t<dialog name="aurora_%s" hideText="true">\n\t\t<menu>\n' % val
+
+def navigation():
+ nav=""
+ nav+='\t\t\t<button x="300" y="20" name="Next" value="Ok" />\n'
+ return nav
+
+def tail():
+ return '\t\t</menu>\n\t</dialog>\n'
+
+def data(val):
+ bf='\t\t\t<image x="0" y="0" image="graphics/images/aurora/%s.png" />\n' % val
+ return bf
+
+# Begin
+f=open("aurora.tmp", "w")
+
+f.write('<?xml version="1.0" encoding="utf-8"?>\n<!-- This file is generated automatically, editing it will have no effect.\n Aurora Event Framework\n (C) Jesusalva, 2020 -->\n<dialogs>')
+
+for evtc in sorted(events):
+ f.write(headers(evtc))
+ f.write(data(evtc))
+ f.write(navigation())
+ f.write(tail())
+
+f.write('\n</dialogs>')
+f.close()
+
diff --git a/client/dailylogin.py b/client/dailylogin.py
new file mode 100755
index 0000000..52c945d
--- /dev/null
+++ b/client/dailylogin.py
@@ -0,0 +1,86 @@
+#!/usr/bin/python2.7
+
+# Setup
+x=y=0
+i=j=0
+
+# Functions
+def headers(val):
+ return '\n\t<dialog name="daily_%d" hideText="true">\n\t\t<menu>\n\t\t\t<text x="45" y="0" width="310" height="30" text="##BDaily Login Rewards##b" />\n <button x="157" y="280" name="Claim" value="Ok" />\n\n' % val
+
+def tail():
+ return '\n\t\t</menu>\n\t</dialog>\n'
+
+def override_check(vl, over, text1, text2):
+ global j
+ if not over:
+ j=0
+ return '\t\t\t<image %s image="graphics/images/%s.png" />\n' % (text1, text2)
+ else:
+ j+=1
+ if (j == vl):
+ return '\t\t\t<image %s image="graphics/images/done.png" />\n' % (text1)
+ elif (j < vl):
+ return '\t\t\t<image %s image="graphics/images/ok.png" />\n' % (text1)
+ else:
+ return ""
+
+def spammer(val, override=False):
+ bf=""
+
+ bf+=override_check(val, override, 'x="35" y="35"', 'jexp')
+ bf+=override_check(val, override, 'x="70" y="35"', 'bexp')
+ bf+=override_check(val, override, 'x="105" y="35"', 'sc')
+ bf+=override_check(val, override, 'x="140" y="35"', 'jexp')
+ bf+=override_check(val, override, 'x="175" y="35"', 'bexp')
+
+ bf+=override_check(val, override, 'x="35" y="70"', 'gp')
+ bf+=override_check(val, override, 'x="70" y="70"', 'sc')
+ bf+=override_check(val, override, 'x="105" y="70"', 'bexp')
+ bf+=override_check(val, override, 'x="140" y="70"', 'gp')
+ bf+=override_check(val, override, 'x="175" y="70"', 'jexp')
+
+ bf+=override_check(val, override, 'x="35" y="105"', 'bexp')
+ bf+=override_check(val, override, 'x="70" y="105"', 'gp')
+ bf+=override_check(val, override, 'x="105" y="105"', 'jexp')
+ bf+=override_check(val, override, 'x="140" y="105"', 'gift')
+ bf+=override_check(val, override, 'x="175" y="105"', 'gp')
+
+ bf+=override_check(val, override, 'x="35" y="140"', 'jexp')
+ bf+=override_check(val, override, 'x="70" y="140"', 'bexp')
+ bf+=override_check(val, override, 'x="105" y="140"', 'gp')
+ bf+=override_check(val, override, 'x="140" y="140"', 'jexp')
+ bf+=override_check(val, override, 'x="175" y="140"', 'bexp')
+
+ bf+=override_check(val, override, 'x="35" y="175"', 'gift')
+ bf+=override_check(val, override, 'x="70" y="175"', 'jexp')
+ bf+=override_check(val, override, 'x="105" y="175"', 'bexp')
+ bf+=override_check(val, override, 'x="140" y="175"', 'gp')
+ bf+=override_check(val, override, 'x="175" y="175"', 'jexp')
+
+ bf+=override_check(val, override, 'x="35" y="210"', 'bexp')
+ bf+=override_check(val, override, 'x="70" y="210"', 'last')
+ bf+=override_check(val, override, 'x="105" y="210"', 'sc')
+ bf+=override_check(val, override, 'x="140" y="210"', 'sc')
+ bf+=override_check(val, override, 'x="175" y="210"', 'sc')
+
+ bf+=override_check(val, override, 'x="35" y="245"', 'sc')
+
+ return bf
+
+# Begin
+f=open("daily.tmp", "w")
+
+f.write('<?xml version="1.0" encoding="utf-8"?>\n<!-- This file is generated automatically, editing it will have no effect.\n (C) Jesusalva, 2019 -->\n<dialogs>')
+
+while (i < 31):
+ i+=1
+ f.write(headers(i))
+ f.write(spammer(i, False))
+ f.write('\n\t\t\t<image x="245" y="52" image="graphics/images/final.png" />\n\n\t\t\t<!-- Complete -->\n')
+ f.write(spammer(i, True))
+ f.write(tail())
+
+f.write('\n</dialogs>')
+f.close()
+
diff --git a/client/magicacademy.py b/client/magicacademy.py
new file mode 100755
index 0000000..c6ee44c
--- /dev/null
+++ b/client/magicacademy.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python2.7
+
+# Setup
+class dlist(list):
+ def __setitem__(self, index, value):
+ size = len(self)
+ if index >= size:
+ self.extend(None for _ in range(size, index + 1))
+ list.__setitem__(self, index, value)
+
+class Skill:
+ def __init__(self, ID, NAME, DESC):
+ self.id=ID
+ self.name=NAME
+ self.desc=DESC
+
+def skill_tree(sk1=None, sk2=None, sk3=None, sk4=None, sk5=None):
+ return [sk1, sk2, sk3, sk4, sk5]
+
+academy={}
+
+# Skill shortcuts
+SK_HEAL=Skill("heal", "Healing", "Heals target")
+SK_ABHEAL=Skill("abheal", "High Healing", "Supremely heals target")
+
+# Scholarship
+academy["scholarship"]=dlist()
+academy["scholarship"].append(skill_tree(SK_HEAL, None, SK_ABHEAL, None, None))
+
+
+# Functions
+def headers(classe, val):
+ return '\n\t<dialog name="mga_%s_%s" hideText="true">\n\t\t<menu>\n' % (classe, val)
+
+def navigation():
+ nav=""
+ nav+='\t\t\t<button x="300" y="20" name="Next" value="Ok" />\n'
+ return nav
+
+def tail():
+ return '\t\t</menu>\n\t</dialog>\n'
+
+def data(val):
+ bf='\t\t\t<image x="0" y="0" image="graphics/images/aurora/%s.png" />\n' % val
+ return bf
+
+# Begin
+f=open("aurora.tmp", "w")
+
+f.write('<?xml version="1.0" encoding="utf-8"?>\n<!-- This file is generated automatically, editing it will have no effect.\n Magic Academy Learning Interface\n (C) Jesusalva, 2020 -->\n<dialogs>')
+
+for evtc in sorted(events):
+ f.write(headers("none", evtc))
+ f.write(data(evtc))
+ f.write(navigation())
+ f.write(tail())
+
+f.write('\n</dialogs>')
+f.close()
+
diff --git a/client/minimap-dyecmd.py b/client/minimap-dyecmd.py
new file mode 100755
index 0000000..c43a4e4
--- /dev/null
+++ b/client/minimap-dyecmd.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+
+import os
+f=open("minimap-dyecmd.sh", "w");
+CLIENT_DATA_ROOT="../../clientdata"
+
+map_names = sorted([os.path.splitext(p)[0] for p in os.listdir(os.path.join(CLIENT_DATA_ROOT, u'graphics', u'minimaps'))])
+
+f.write('cd '+os.path.join(CLIENT_DATA_ROOT, u'graphics', u'minimaps')+'\n')
+for c in map_names:
+ if os.path.exists(os.path.join(CLIENT_DATA_ROOT, u'graphics', u'minimaps', c+u'.png')):
+ i=c+u'.png'
+ f.write("dyecmd %s tmp ; mv tmp %s\n" % (i, i))
+ f.write("echo \"Converted "+i+" successfully.\"\n")
+
+f.close()
diff --git a/client/minimap-dyecmd.sh b/client/minimap-dyecmd.sh
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/client/minimap-dyecmd.sh
diff --git a/client/minimap-override/none.png b/client/minimap-override/none.png
new file mode 100644
index 0000000..539e992
--- /dev/null
+++ b/client/minimap-override/none.png
Binary files differ
diff --git a/client/minimap-render.py b/client/minimap-render.py
new file mode 100755
index 0000000..9d4bac1
--- /dev/null
+++ b/client/minimap-render.py
@@ -0,0 +1,171 @@
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+
+import sys
+import os
+import subprocess
+import tempfile
+
+CLIENT_DATA_ROOT = os.path.realpath(
+ os.path.join(
+ os.path.dirname(__file__),
+ u'../../clientdata',
+ )
+)
+
+SKIP_DOMAIN="030-"
+
+class MinimapRenderer(object):
+
+ PROGRAMS = {
+ 'default': {
+ 'tmxrasterizer': 'tmxrasterizer',
+ 'im_convert': 'convert',
+ },
+ 'win32': {
+ 'tmxrasterizer': 'tmxrasterizer.exe',
+ 'im_convert': 'convert.exe',
+ },
+ }
+
+ def __init__(self, map_name, tilesize, useAntiAliasing):
+ self.map_name = map_name
+ self.tilesize = tilesize
+ self.useAntiAliasing = useAntiAliasing
+
+ def render(self):
+ """
+ Processes a map
+ """
+ if not os.path.exists(os.path.join(CLIENT_DATA_ROOT, u'maps', self.map_name+u'.tmx')):
+ sys.stderr.write(u'Invalid map name: %s. Skipping.\n' % self.map_name)
+ return 1
+ if not self.map_name.endswith(u'.tmx'):
+ self.map_name = self.map_name+u'.tmx'
+ if SKIP_DOMAIN in self.map_name:
+ print(u'Skipping %s (regex)...\n' % self.map_name)
+ return 0
+
+ map_number = os.path.splitext(os.path.basename(self.map_name))[0]
+ tmx_file = os.path.join(CLIENT_DATA_ROOT, u'maps', self.map_name)
+ minimap_file = os.path.join(CLIENT_DATA_ROOT, u'graphics', u'minimaps', map_number+u'.png')
+
+ prefix = os.path.commonprefix((tmx_file, minimap_file))
+ sys.stdout.write(u'%s -> %s\n' % (os.path.relpath(tmx_file, prefix), os.path.relpath(minimap_file, prefix)))
+
+ try:
+ self.do_render(tmx_file, minimap_file)
+ except Exception as e:
+ sys.stderr.write(u'\x1b[31m\x1b[1mError while rendering %s: %s\x1b[0m\n' % (self.map_name, e))
+ return 1
+ else:
+ return 0
+
+ def do_render(self, tmx_file, bitmap_file):
+ """
+ The map rendering implementation
+ """
+ platform_programs = MinimapRenderer.PROGRAMS.get(sys.platform, MinimapRenderer.PROGRAMS.get('default'))
+ # tmx rasterize
+ mrf, map_raster = tempfile.mkstemp(suffix='.png')
+ tmxrasterizer_cmd = [
+ #platform_programs.get('tmxrasterizer'),
+ '../../tiled/default/install-root/usr/local/bin/tmxrasterizer',
+ '--tilesize', str(self.tilesize), '--hide-layer', 'Collision','--hide-layer', 'Height','--hide-layer', 'Collisions','--hide-layer', 'collision','--hide-layer', 'height','--hide-layer', 'Heights', '--hide-layer', 'height', '--ignore-visibility', '--hide-object-layers',
+ ]
+ if self.useAntiAliasing:
+ tmxrasterizer_cmd.append('--anti-aliasing')
+ tmxrasterizer_cmd += [tmx_file, map_raster]
+ subprocess.check_call(tmxrasterizer_cmd)
+ if os.stat(map_raster).st_size == 0:
+ raise Exception('A problem was encountered when rendering a map')
+ # add cell-shading to the minimap to improve readability
+ ebf, edges_bitmap = tempfile.mkstemp(suffix='.png')
+ subprocess.check_call([
+ platform_programs.get('im_convert'), map_raster,
+ '-set', 'option:convolve:scale', '-1!',
+ '-morphology', 'Convolve', 'Laplacian:0',
+ '-colorspace', 'gray',
+ '-auto-level',
+ '-threshold', '2.8%',
+ '-negate',
+ '-transparent', 'white',
+ edges_bitmap
+ ])
+ subprocess.check_call([
+ platform_programs.get('im_convert'), map_raster, edges_bitmap,
+ '-compose', 'Dissolve',
+ '-define', 'compose:args=35',
+ '-composite',
+ bitmap_file
+ ])
+ os.unlink(map_raster)
+ os.unlink(edges_bitmap)
+
+ @staticmethod
+ def check_programs():
+ """
+ Checks the require programs are available
+ """
+ def which(program):
+ import os
+ def is_exe(fpath):
+ return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+ fpath, fname = os.path.split(program)
+ if fpath:
+ if is_exe(program):
+ return program
+ else:
+ for path in os.environ["PATH"].split(os.pathsep):
+ exe_file = os.path.join(path, program)
+ if is_exe(exe_file):
+ return exe_file
+ return None
+
+ platform_programs = MinimapRenderer.PROGRAMS.get(sys.platform, MinimapRenderer.PROGRAMS.get('default'))
+ for program in platform_programs.values():
+ if not which(program):
+ raise Exception('The required "%s" program is missing from your PATH.' % program)
+
+def usage():
+ sys.stderr.write(u'''Usage: %s MAP_NAME...
+
+ Example:
+ $ ./minimap-render.py 007-1
+ will render the map at maps/007-1.tmx in the graphics/minimaps directory.
+ $ ./minimap-render.py all
+ will render all existing maps found in the maps directory.
+ $ ./minimap-render.py update
+ will update all existing minimaps found in the graphics/minimaps directory.
+
+ For convenience,
+ $ ./minimap-render.py 007-1.tmx
+ is also accepted.
+ \n''' % sys.argv[0])
+
+def main():
+ if not len(sys.argv) > 1:
+ usage()
+ return 127
+ try:
+ MinimapRenderer.check_programs()
+ except Exception as e:
+ sys.stderr.write(u'%s\n' % e)
+ return 126
+
+ status = 0
+ if sys.argv[1].lower() == 'all':
+ map_names = sorted([os.path.splitext(p)[0] for p in os.listdir(os.path.join(CLIENT_DATA_ROOT, u'maps'))])
+ elif sys.argv[1].lower() == 'update':
+ map_names = sorted([os.path.splitext(p)[0] for p in os.listdir(os.path.join(CLIENT_DATA_ROOT, u'graphics', u'minimaps'))])
+ else:
+ map_names = sys.argv[1:]
+
+ for map_name in map_names:
+ # Render tiles at 1 pixel size
+ map_renderer = MinimapRenderer(map_name, 1, True)
+ status += map_renderer.render()
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/client/skills.py b/client/skills.py
new file mode 100755
index 0000000..bfe59f3
--- /dev/null
+++ b/client/skills.py
@@ -0,0 +1,137 @@
+#!/usr/bin/python2.7
+
+# Setup
+x=y=0
+i=j=0
+
+skills=[]
+
+class Skill:
+ def __init__(self, sid, name, icon, desc, bmp, amp, cmd, maxlv):
+ self.id=sid
+ self.lv=maxlv
+ self.name=name
+ self.icon=icon
+ self.cmd=cmd
+
+ self.desc=desc
+ self.bmp=bmp
+ self.amp=amp
+
+def fillskill(sk, lv):
+ sid=sk.id
+ name=sk.name
+ icon=sk.icon
+ desc=sk.desc
+ bmp=sk.bmp
+ amp=sk.amp
+ cmd=sk.cmd
+
+ if lv > 0:
+ lvstr='\t\t\tlevel="%d"\n' % lv
+ mpstr='%d MP. ' % int(bmp+(amp*(lv-1)))
+ cmdstr='\t\t\tinvokeCmd="@sk-%s"\n' % cmd
+ else:
+ lvstr=''
+ lv=1
+ mpstr=''
+ cmdstr=''
+
+ msg='\
+\t\t<skill\n\
+\t\t\tid="%d"\n\
+\t\t\tname="%s"\n\
+\t\t\ticon="graphics/skills/%s.png"\n\
+\t\t\tdescription="%s%s"\n\
+%s\
+%s\
+\t\t/>\n' % (sid, name, icon, mpstr, desc, cmdstr, lvstr)
+ return msg
+
+
+
+
+
+
+
+
+
+
+
+# Declare the skills
+
+#########################
+### Transmutation Skills
+#########################
+#skills.append(Skill(20024, "Parum", "other/parum", "Transmutate wood into stuff.",
+#50, 0, "parum", 0))
+#skills.append(Skill(20027, "Transmutation", "transmutation", "Transmute stuff into other stuff.",
+#215, -5, "trans", 10))
+
+#########################
+### Summon Skills
+#########################
+skills.append(Skill(20025, "Summon Maggots", "other/kalmurk", "2x Maggot Slime.",
+40, 5, "kalmurk", 0))
+skills.append(Skill(20029, "Summon Dragon", "none", "4x Dragon Scale.",
+50, 4, "dragokin", 0))
+skills.append(Skill(20030, "Summon Slimes", "none", "15x Maggot Slime.",
+30, 3, "limerizer", 0))
+skills.append(Skill(20043, "Summon Fluffies", "none", "1x White Fur.",
+25, 4, "cuteheart", 0))
+skills.append(Skill(20042, "Summon Spiky", "none", "1x Mushroom Spores.",
+25, 5, "kalspike", 0))
+skills.append(Skill(20041, "Summon Mouboo", "none", "1x Mouboo Figurine.",
+25, 5, "kalboo", 0))
+skills.append(Skill(20036, "Summon Snakes", "none", "1x Snake Egg.",
+35, 6, "halhiss", 0))
+skills.append(Skill(20037, "Summon Wolverns", "none", "5x White Fur.",
+45, 5, "kalwulf", 0))
+skills.append(Skill(20038, "Summon Fairies", "none", "1x Fluo Powder.",
+40, 4, "fairykingdom", 0))
+skills.append(Skill(20039, "Summon Yetis", "none", "1x Frozen Yeti Tear.",
+37, 5, "frozenheart", 0))
+skills.append(Skill(20040, "Summon Terranite", "none", "1x Terranite Ore.",
+47, 5, "stoneheart", 0))
+skills.append(Skill(20044, "Summon Plants", "none", "2x Root.",
+30, 3, "plantkingdom", 5))
+skills.append(Skill(20047, "Summon Ducks", "none", "1x Cherry Cake. Req. Rubber Ducky.",
+40, 7, "ducky", 0))
+skills.append(Skill(20049, "Summon Pixies", "none", "3x Fluo Powder.",
+40, 4, "fairyempire", 0))
+
+skills.append(Skill(20023, "Summon Cave Maggot", "none", "Req. Zarkor Scroll.",
+40, 7, "zarkor", 0))
+
+
+
+
+
+
+
+
+
+
+
+
+# Begin
+f=open("skills.tmp", "w")
+
+f.write('<?xml version="1.0" encoding="utf-8"?>\n<!-- This file is generated automatically, editing it will have no effect.\n (C) Jesusalva, 2019-2020 -->\n<skills>\n\t<set name="Summon">\n')
+
+for sk in skills:
+ i=0
+ while (i < sk.lv):
+ i+=1
+ f.write(fillskill(sk, i))
+
+ # Fill the fallback
+ if (int(sk.lv)):
+ sk.desc="MP + "+str(sk.amp)+"/lv. "+sk.desc
+ f.write(fillskill(sk, -1))
+ f.write("\n")
+
+# We're done
+f.write('\n\t</set>\n</skills>')
+f.close()
+
diff --git a/client/tmxrasterizer b/client/tmxrasterizer
new file mode 100755
index 0000000..f079132
--- /dev/null
+++ b/client/tmxrasterizer
Binary files differ
diff --git a/client/weapons.py b/client/weapons.py
new file mode 100755
index 0000000..cc45d01
--- /dev/null
+++ b/client/weapons.py
@@ -0,0 +1,89 @@
+#!/usr/bin/python2.7
+
+class Item:
+ def __init__(self, xid):
+ self.id=xid
+ self.lvl=0
+
+a=open("../../clientdata/items.xml", "r")
+
+swords=[]
+bows=[]
+shields=[]
+
+gid="0"
+rid=0
+ctx=Item(0)
+mem=[]
+
+for l in a:
+ if "<item id=" in l:
+ if ctx.id > 0:
+ mem.append(ctx)
+
+ gid=l.replace('\t', '').replace(' ','').replace('<itemid=', '').replace('"', '').replace("'", "")
+ rid=0
+ if "-" in gid:
+ gid="0"
+ continue
+ try:
+ rid=int(gid)
+ except:
+ print "[CRITICAL] Invalid item ID format: " + l
+ exit(1)
+
+ ctx=Item(rid)
+
+ if "\tlevel=" in l or " level=" in l:
+ gid=l.replace('\t', '').replace(' ','').replace('level=', '').replace('"', '').replace("'", "")
+ try:
+ rid=int(gid)
+ except:
+ print "[CRITICAL] Invalid item level format: " + l
+ exit(1)
+ ctx.lvl=0+rid
+
+mem=sorted(mem, key=lambda xcv: xcv.lvl, reverse=True)
+
+for r in mem:
+ rid=r.id
+ if rid >= 2700 and rid <= 2899:
+ shields.append(rid)
+ elif rid >= 3500 and rid <= 3999:
+ swords.append(rid)
+ elif rid >= 6000 and rid <= 6499:
+ bows.append(rid)
+
+a.close()
+
+#shields=sorted(shields, reverse=True)
+#bows=sorted(bows, reverse=True)
+#swords=sorted(swords, reverse=True)
+
+b=open("weapons.tmp", "w")
+
+b.write('<?xml version="1.0" encoding="utf-8"?>\n\
+<!-- Author: 4144, Jesusalva\n\
+Copyright (C) 2015 Evol Online\n\
+Copyright (C) 2018 TMW2: Moubootaur Legends\n -->\n\
+\n\
+<weapons>\n')
+
+b.write(' <swords>\n')
+
+for i in swords:
+ b.write(' <item id="%d"/>\n' % i)
+
+b.write(' </swords>\n <bows>\n')
+
+for i in bows:
+ b.write(' <item id="%d"/>\n' % i)
+
+b.write(' </bows>\n <shields>\n')
+
+for i in shields:
+ b.write(' <item id="%d"/>\n' % i)
+
+b.write(' </shields>\n</weapons>')
+
+b.close()
diff --git a/hercules/.gitignore b/hercules/.gitignore
new file mode 100644
index 0000000..7eeffc2
--- /dev/null
+++ b/hercules/.gitignore
@@ -0,0 +1,12 @@
+clientdata
+maps
+serverdata
+oldserverdata
+newserverdata
+mapcache
+olddb
+newdb
+rodata
+tools/luadec
+
+*.pyc
diff --git a/hercules/code/__init__.py b/hercules/code/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hercules/code/__init__.py
diff --git a/hercules/code/clienttoserver/__init__.py b/hercules/code/clienttoserver/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hercules/code/clienttoserver/__init__.py
diff --git a/hercules/code/clienttoserver/maps.py b/hercules/code/clienttoserver/maps.py
new file mode 100644
index 0000000..69ef4ef
--- /dev/null
+++ b/hercules/code/clienttoserver/maps.py
@@ -0,0 +1,190 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import array
+import csv
+import hashlib
+import os
+import zlib
+import struct
+import StringIO
+from xml.dom import minidom
+
+from code.fileutils import makeDir, writeInt16, writeInt32, writeData, removeDir
+
+def getTmxFiles(srcDir):
+ names = []
+ for name in os.listdir(srcDir):
+ names.append(name)
+ names.sort()
+ for name in names:
+ fileName = srcDir + name
+ if os.path.isfile(fileName) == False:
+ continue
+ if name.endswith(".tmx") == False:
+ continue
+ yield (fileName, name)
+
+def findFirstGid(tilesets, tile):
+ found = -1
+ for t in tilesets:
+ if t <= tile:
+ found = t
+ break
+ return found
+
+# client
+# 0 - walkable ground
+# 1 - non walkable wall
+# 2 - air allowed shootable too
+# 3 - water allowed water, shootable too
+# 4 - sit, walkable ground
+# 5 - player walk not allowed
+# 6 - monster walk not allowed
+
+# server
+# 0 - walkable ground
+# 1 - non walkable wall
+# 2 - air allowed shootable too
+# 3 - water allowed water, shootable too
+# 4 - sit, walkable ground
+# 5 - none
+# 6 - monster walk not allowed
+def convertTileType(tile, idx, width, height):
+ if tile == 5:
+ tile = 0;
+ if tile > 6 or tile < 0:
+ y = int(idx / width)
+ x = idx - y * width
+ print "Error: wrong tile: ({0}, {1}) = {2}".format(x, y, tile)
+ tile = 1
+ return tile
+
+# map file format
+#
+# int16 version;
+# uint8 md5_checksum[16];
+# int16 xs;
+# int16 ys;
+# int32 len;
+# ...data...
+
+def recreateMap(names):
+ collisionLayerName = "collision"
+ destDir = "../../serverdata/maps/re/"
+ tmxName = names[0]
+ mCaheName = destDir + names[1][:-3] + "mcache"
+ with open(mCaheName, "wb") as w:
+ print tmxName
+ dom = minidom.parse(tmxName)
+ root = dom.documentElement
+ tilesets = []
+ for tileset in root.getElementsByTagName("tileset"):
+ tilesets.append(int(tileset.attributes["firstgid"].value))
+ tilesets.sort(reverse = True)
+ found = False
+ for layer in root.getElementsByTagName("layer"):
+ if layer.attributes["name"].value.lower() == collisionLayerName:
+ data = layer.getElementsByTagName("data")
+ if data is None or len(data) != 1:
+ continue
+ data = data[0]
+ width = int(layer.attributes["width"].value)
+ height = int(layer.attributes["height"].value)
+ encoding = data.attributes["encoding"].value
+ try:
+ compression = data.attributes["compression"].value
+ except:
+ compression = ""
+
+ tiles = []
+ if encoding == "base64":
+ binData = data.childNodes[0].data.strip()
+ binData = binData.decode('base64')
+ if compression == "gzip":
+ dc = zlib.decompressobj(16 + zlib.MAX_WBITS)
+ else:
+ dc = zlib.decompressobj()
+ layerData = dc.decompress(binData)
+ arr = array.array("I")
+ arr.fromstring(layerData)
+ idx = 0
+ for tile in arr:
+ if tile == 0:
+ tileType = 0
+ else:
+ firstgid = findFirstGid(tilesets, tile)
+ tileType = convertTileType(tile - firstgid, idx, width, height);
+ tiles.append(tileType)
+ idx = idx + 1
+ elif encoding == "csv":
+ binData = data.childNodes[0].data.strip()
+ f = StringIO.StringIO(binData)
+ arr = list(csv.reader(f, delimiter=',', quotechar='|'))
+ idx = 0
+ for row in arr:
+ for item in row:
+ if item != "":
+ tile = int(item)
+ if tile == 0:
+ tileType = 0
+ else:
+ firstgid = findFirstGid(tilesets, tile)
+ tileType = convertTileType(tile - firstgid, idx, width, height);
+ # tmx collision format
+ # 0 - walkable ground
+ # 1 - non walkable wall
+ # 2 - air allowed shootable too
+ # 3 - water allowed water, shootable too
+ # 4 - sit, walkable ground
+ # 5 - none
+
+ # server collision format
+ # 000 0 - walkable, shootable
+ # 001 1 - wall
+ # 010 2 - same with 0
+ # 011 3 - walkable, shootable, water
+ # 100 4 - same with 0
+ # 101 5 - shootable
+ # 110 6 - same with 0
+ # 111 7 - none
+ if tileType > 128 or tileType < 0:
+ tileType = 1
+ tiles.append(tileType)
+ idx = idx + 1
+ f.close()
+ else:
+ print "map format not supported: " + tmxName
+ continue
+
+ #comp = zlib.compressobj()
+ binData = struct.pack(str(len(tiles))+"B", *tiles)
+ binData = zlib.compress(binData)
+ writeInt16(w, 1)
+ writeData(w, hashlib.md5(binData).digest()) # 16 bytes
+ writeInt16(w, width)
+ writeInt16(w, height)
+ writeInt32(w, len(binData))
+ writeData(w, binData)
+ print tmxName
+ found = True
+ break
+ if found == False:
+ print "Error: missing collision layer in file: {0}".format(tmxName)
+ return
+ return
+ with open(mCaheName + ".debug", "wb") as w:
+ writeInt16(w, width)
+ writeInt16(w, height)
+ writeData(w, struct.pack(str(len(tiles))+"B", *tiles))
+
+
+def recreateMapCache():
+ destDir = "../../serverdata/maps/re/"
+ srcDir = "../../clientdata/maps/"
+ removeDir(destDir)
+ makeDir(destDir)
+ for names in getTmxFiles(srcDir):
+ recreateMap(names)
diff --git a/hercules/code/configutils.py b/hercules/code/configutils.py
new file mode 100644
index 0000000..6d9ddb2
--- /dev/null
+++ b/hercules/code/configutils.py
@@ -0,0 +1,51 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+def writeIntField(w, name, value):
+ if value == "":
+ value = "0"
+ w.write(" {0}: {1}\n".format(name, value))
+
+def writeIntField2(w, name, value):
+ if value == "":
+ value = "0"
+ w.write(" {0}: {1}\n".format(name, value))
+
+def writeStrField(w, name, value):
+ w.write(" {0}: \"{1}\"\n".format(name, value))
+
+def writeCondField2(w, cond, name):
+ if cond != 0:
+ w.write(" {0}: true\n".format(name))
+
+def writeSubField(w, name, value):
+ w.write(" {0}: {1}\n".format(name, value))
+
+def writeFieldArr(w, name, value, value2):
+ w.write(" {0}: [{1}, {2}]\n".format(name, value, value2))
+
+def writeFieldList(w, name, value, value2):
+ w.write(" {0}: ({1}, {2})\n".format(name, value, value2))
+
+def writeStartBlock(w, text):
+ w.write(" {0}: {{\n".format(text))
+
+def writeEndBlock(w):
+ w.write(" }\n")
+
+def writeStartScript(w, name):
+ w.write(" {0}: <\"\n".format(name))
+
+def writeEndScript(w):
+ w.write(" \">\n")
+
+def isHaveData(fields, start, cnt):
+ for f in range(0, cnt):
+ value = fields[start + f * 2]
+ chance = fields[start + f * 2]
+ if value == "" or value == "0" or chance == "" or chance == "0":
+ continue
+ return True
+ return False
diff --git a/hercules/code/fileutils.py b/hercules/code/fileutils.py
new file mode 100644
index 0000000..a0635d4
--- /dev/null
+++ b/hercules/code/fileutils.py
@@ -0,0 +1,86 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import array
+import os
+import struct
+import shutil
+
+def readInt32(f):
+ data = f.read(4)
+ arr = array.array("I")
+ arr.fromstring(data)
+ return arr[0]
+
+def readInt16(f):
+ data = f.read(2)
+ arr = array.array("H")
+ arr.fromstring(data)
+ return arr[0]
+
+def readInt8(f):
+ data = f.read(1)
+ arr = array.array("B")
+ arr.fromstring(data)
+ return arr[0]
+
+def readMapName(f):
+ data = f.read(12)
+ data = str(data)
+ while data[-1] == '\x00':
+ data = data[:-1]
+ return data
+
+def skipData(f, sz):
+ f.read(sz)
+
+def readData(f, sz):
+ return f.read(sz)
+
+def readFile(path):
+ with open(path, "r") as f:
+ return f.read()
+
+def writeInt32(f, i):
+ f.write(struct.pack("I", i))
+
+def writeInt16(f, i):
+ f.write(struct.pack("H", i))
+
+def writeMapName(f, name):
+ if len(name) > 12:
+ name = name[:12]
+ while len(name) < 12:
+ name = name + '\x00'
+ f.write(struct.pack("12s", name))
+
+def writeData(f, data):
+ f.write(data)
+
+def copyFile(src, dst, name):
+ shutil.copyfile(src + name, dst + name)
+
+def saveFile(fileName, data):
+ with open(fileName, "w") as w:
+ w.write(data)
+
+def makeDir(path):
+ if not os.path.exists(path):
+ os.makedirs(path)
+
+def removeDir(path):
+ if os.path.exists(path):
+ shutil.rmtree(path)
+
+def removeAllFiles(path):
+ if os.path.exists(path):
+ shutil.rmtree(path)
+
+def findFileIn(names, dirs):
+ for name in names:
+ for path in dirs:
+ if os.path.exists(path + name):
+ return path
+ return None
diff --git a/hercules/code/server/__init__.py b/hercules/code/server/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hercules/code/server/__init__.py
diff --git a/hercules/code/server/account.py b/hercules/code/server/account.py
new file mode 100644
index 0000000..7763766
--- /dev/null
+++ b/hercules/code/server/account.py
@@ -0,0 +1,63 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.fileutils import readFile
+from code.stringutils import stripNewLine, escapeSqlStr
+
+def convertSex(sex):
+ if sex == "M" or sex == "F" or sex == "S":
+ return sex
+ return "M"
+
+def convertAccount():
+ srcFile = "olddb/account.txt"
+ dstFile = "newdb/login.sql"
+ fieldsSplit = re.compile("\t")
+ tpl = readFile("templates/login.sql")
+ firstLine = True
+ with open(dstFile, "w") as w:
+ w.write(tpl)
+ w.write("INSERT INTO `login` VALUES ")
+ with open(srcFile, "r") as r:
+ for line in r:
+ if line[:2] == "//":
+ continue
+ rows = fieldsSplit.split(line)
+ if len(rows) == 2:
+ continue
+ if len(rows) != 14:
+ print "wrong account.txt line: " + stripNewLine(line)
+ continue
+
+ if firstLine == False:
+ w.write(",\n")
+ else:
+ firstLine = False
+
+ w.write(("({account_id},'{userid}','{user_pass}','{sex}'," +
+ "'{email}',{group_id},{state},{unban_time}," +
+ "{expiration_time},{logincount},'{lastlogin}'," +
+ "'{last_ip}','{birthdate}',{character_slots}," +
+ "'{pincode}',{pincode_change})").format(
+ account_id = rows[0],
+ userid = escapeSqlStr(rows[1]),
+ user_pass = escapeSqlStr(rows[2]),
+ sex = convertSex(rows[4]),
+ email = escapeSqlStr(rows[7]),
+ group_id = 0,
+ state = 0,
+ unban_time = rows[12],
+ expiration_time = rows[9],
+ logincount = rows[5],
+ lastlogin = rows[3],
+ last_ip = rows[10],
+ birthdate = '0000-00-00',
+ character_slots = 0,
+ pincode = '',
+ pincode_change = 0
+ ))
+ w.write("\n")
diff --git a/hercules/code/server/accreg.py b/hercules/code/server/accreg.py
new file mode 100644
index 0000000..b69910f
--- /dev/null
+++ b/hercules/code/server/accreg.py
@@ -0,0 +1,52 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.fileutils import readFile
+from code.stringutils import stripNewLine
+
+def convertAccReg():
+ srcFile = "olddb/accreg.txt"
+ dstFile = "newdb/acc_reg_num_db.sql"
+ fieldsSplit = re.compile("\t")
+ comaSplit = re.compile(",")
+ spaceSplit = re.compile(" ")
+ tpl = readFile("templates/acc_reg_num_db.sql")
+ firstLine = True
+ with open(dstFile, "w") as w:
+ w.write(tpl)
+ w.write("INSERT INTO `acc_reg_num_db` VALUES ")
+ with open(srcFile, "r") as r:
+ for line in r:
+ if line[:2] == "//":
+ continue
+ line = stripNewLine(line)
+ rows = fieldsSplit.split(line)
+ if len(rows) != 2:
+ print "wrong accreg.txt line: " + line
+ continue
+
+ accountId = rows[0]
+
+ data = spaceSplit.split(rows[1])
+ for varStr in data:
+ if varStr == "":
+ continue
+
+ tmp = comaSplit.split(varStr)
+
+ if firstLine == False:
+ w.write(",\n")
+ else:
+ firstLine = False
+
+ w.write(("({account_id},'{key}',{index},{value})").format(
+ account_id = accountId,
+ key = tmp[0],
+ index = "0",
+ value = tmp[1]
+ ))
+ w.write("\n")
diff --git a/hercules/code/server/db/__init__.py b/hercules/code/server/db/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hercules/code/server/db/__init__.py
diff --git a/hercules/code/server/db/char.py b/hercules/code/server/db/char.py
new file mode 100644
index 0000000..cd8e0cc
--- /dev/null
+++ b/hercules/code/server/db/char.py
@@ -0,0 +1,187 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2015 Evol Online
+# Author: Andrei Karas (4144)
+
+from code.fileutils import readFile
+from code.stringutils import escapeSqlStr
+
+def saveCharTable(users):
+ dstFile = "newdb/char.sql"
+ firstLine = True
+ tpl = readFile("templates/char.sql")
+ with open(dstFile, "w") as w:
+ w.write(tpl)
+ w.write("INSERT INTO `char` VALUES ")
+ for userId in users:
+ user = users[userId]
+
+ if firstLine == False:
+ w.write(",\n")
+ else:
+ firstLine = False
+
+ w.write(("({char_id},{account_id},{char_num},'{name}',{CLASS}," +
+ "{base_level},{job_level},{base_exp},{job_exp},{zeny}," +
+ "{str},{agi},{vit},{INT},{dex},{luk},{max_hp},{hp},{max_sp}," +
+ "{sp},{status_point},{skill_point},{option},{karma},{manner}," +
+ "{party_id},{guild_id},{pet_id},{homun_id},{elemental_id}," +
+ "{hair},{hair_color},{clothes_color},{body},{weapon},{shield}," +
+ "{head_top},{head_mid},{head_bottom},{robe}," +
+ "'{last_map}',{last_x},{last_y},'{save_map}',{save_x},{save_y}," +
+ "{partner_id},{online},{father},{mother},{child},{fame}," +
+ "{rename},{delete_date},{slotchange},{char_opt},{font}," +
+ "{unban_time},{uniqueitem_counter},'{sex}',{hotkey_rowshift})").format(
+ char_id = user.char_id,
+ account_id = user.account_id,
+ char_num = user.char_num,
+ name = escapeSqlStr(user.char_name),
+ CLASS = user.char_class,
+ base_level = user.base_level,
+ job_level = user.job_level,
+ base_exp = user.base_exp,
+ job_exp = user.job_exp,
+ zeny = user.zeny,
+ str = user.stat_str,
+ agi = user.stat_agi,
+ vit = user.stat_vit,
+ INT = user.stat_int,
+ dex = user.stat_dex,
+ luk = user.stat_luk,
+ max_hp = user.max_hp,
+ hp = user.hp,
+ max_sp = user.max_sp,
+ sp = user.sp,
+ status_point = user.status_point,
+ skill_point = user.skill_point,
+ option = user.option,
+ karma = user.karma,
+ manner = user.manner,
+ party_id = user.party_id,
+ guild_id = user.guild_id,
+ pet_id = user.pet_id,
+ homun_id = "0",
+ elemental_id = "0",
+ hair = user.hair,
+ hair_color = user.hair_color,
+ clothes_color = user.clothes_color,
+ body = 0,
+ weapon = user.weapon,
+ shield = user.shield,
+ head_top = user.head_top,
+ head_mid = user.head_mid,
+ head_bottom = user.head_bottom,
+ robe = "0",
+ last_map = escapeSqlStr(user.last_map),
+ last_x = user.last_x,
+ last_y = user.last_y,
+ save_map = escapeSqlStr(user.save_map),
+ save_x = user.save_x,
+ save_y = user.save_y,
+ partner_id = user.partner_id,
+ online = "0",
+ father = "0",
+ mother = "0",
+ child = "0",
+ fame = "0",
+ rename = "0",
+ delete_date = "0",
+ slotchange = "0",
+ char_opt = "0",
+ font = "0",
+ unban_time = "0",
+ uniqueitem_counter = len(user.inventory),
+ sex = "U",
+ hotkey_rowshift = 0
+ ))
+ w.write("\n")
+
+def saveCharTableCustom(users):
+ dstFile = "newdb/char.sql"
+ firstLine = True
+ tpl = readFile("templates/char.sql")
+ with open(dstFile, "w") as w:
+ w.write(tpl)
+ w.write("INSERT INTO `char` VALUES ")
+ for userId in users:
+ user = users[userId]
+
+ if firstLine == False:
+ w.write(",\n")
+ else:
+ firstLine = False
+
+ w.write(("({char_id},{account_id},{char_num},'{name}',{CLASS}," +
+ "{base_level},{job_level},{base_exp},{job_exp},{zeny}," +
+ "{str},{agi},{vit},{INT},{dex},{luk},{max_hp},{hp},{max_sp}," +
+ "{sp},{status_point},{skill_point},{option},{karma},{manner}," +
+ "{party_id},{guild_id},{pet_id},{homun_id},{elemental_id}," +
+ "{hair},{hair_color},{clothes_color},{body},{weapon},{shield}," +
+ "{head_top},{head_mid},{head_bottom},{robe}," +
+ "'{last_map}',{last_x},{last_y},'{save_map}',{save_x},{save_y}," +
+ "{partner_id},{online},{father},{mother},{child},{fame}," +
+ "{rename},{delete_date},{slotchange},{char_opt},{font}," +
+ "{unban_time},{uniqueitem_counter},'{sex}',{hotkey_rowshift})").format(
+ char_id = user.char_id,
+ account_id = user.account_id,
+ char_num = user.char_num,
+ name = escapeSqlStr(user.char_name),
+ CLASS = 0,
+ base_level = user.base_level,
+ job_level = user.job_level,
+ base_exp = user.base_exp,
+ job_exp = user.job_exp,
+ zeny = user.zeny,
+ str = user.stat_str,
+ agi = user.stat_agi,
+ vit = user.stat_vit,
+ INT = user.stat_int,
+ dex = user.stat_dex,
+ luk = user.stat_luk,
+ max_hp = user.max_hp,
+ hp = user.hp,
+ max_sp = user.max_sp,
+ sp = user.sp,
+ status_point = user.status_point,
+ skill_point = user.skill_point,
+ option = user.option,
+ karma = user.karma,
+ manner = user.manner,
+ party_id = 0,
+ guild_id = 0,
+ pet_id = 0,
+ homun_id = "0",
+ elemental_id = "0",
+ hair = user.hair,
+ hair_color = user.hair_color,
+ clothes_color = user.clothes_color,
+ body = 0,
+ weapon = 0,
+ shield = 0,
+ head_top = 0,
+ head_mid = 0,
+ head_bottom = 0,
+ robe = "0",
+ last_map = "000-2-1",
+ last_x = 50,
+ last_y = 37,
+ save_map = "00-2-1",
+ save_x = 50,
+ save_y = 37,
+ partner_id = user.partner_id,
+ online = "0",
+ father = "0",
+ mother = "0",
+ child = "0",
+ fame = "0",
+ rename = "0",
+ delete_date = "0",
+ slotchange = "0",
+ char_opt = "0",
+ font = "0",
+ unban_time = "0",
+ uniqueitem_counter = len(user.inventory),
+ sex = "U",
+ hotkey_rowshift = 0
+ ))
+ w.write("\n")
diff --git a/hercules/code/server/db/charregnumdb.py b/hercules/code/server/db/charregnumdb.py
new file mode 100644
index 0000000..1617c13
--- /dev/null
+++ b/hercules/code/server/db/charregnumdb.py
@@ -0,0 +1,34 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2015 Evol Online
+# Author: Andrei Karas (4144)
+
+from code.fileutils import readFile
+
+def saveCharRegNumDbTable(users):
+ dstFile = "newdb/char_reg_num_db.sql"
+ firstLine = True
+ tpl = readFile("templates/char_reg_num_db.sql")
+ with open(dstFile, "w") as w:
+ w.write(tpl)
+ w.write("INSERT INTO `char_reg_num_db` VALUES ")
+ for userId in users:
+ user = users[userId]
+ for varKey in user.variables:
+ if varKey == "ShipQuests":
+ continue
+
+ varValue = user.variables[varKey]
+
+ if firstLine == False:
+ w.write(",\n")
+ else:
+ firstLine = False
+
+ w.write(("({char_id},'{key}',{index},{value})").format(
+ char_id = user.char_id,
+ key = varKey,
+ index = "0",
+ value = varValue
+ ))
+ w.write("\n")
diff --git a/hercules/code/server/db/inventory.py b/hercules/code/server/db/inventory.py
new file mode 100644
index 0000000..e5c1e6a
--- /dev/null
+++ b/hercules/code/server/db/inventory.py
@@ -0,0 +1,44 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2015 Evol Online
+# Author: Andrei Karas (4144)
+
+from code.fileutils import readFile
+
+def saveInventoryTable(users):
+ dstFile = "newdb/inventory.sql"
+ firstLine = True
+ tpl = readFile("templates/inventory.sql")
+ with open(dstFile, "w") as w:
+ w.write(tpl)
+ w.write("INSERT INTO `inventory` VALUES ")
+ for userId in users:
+ user = users[userId]
+ for item in user.inventory:
+
+ if firstLine == False:
+ w.write(",\n")
+ else:
+ firstLine = False
+
+ w.write(("({id},{char_id},{nameid},{amount},{equip},{identify}," +
+ "{refine},{attribute},{card0},{card1},{card2},{card3}," +
+ "{expire_time},{favorite},{bound},{unique_id})").format(
+ id = 0,
+ char_id = user.char_id,
+ nameid = item.itemId,
+ amount = item.amount,
+ equip = item.equip,
+ identify = "1",
+ refine = item.refine,
+ attribute = item.attribute,
+ card0 = "0",
+ card1 = "0",
+ card2 = "0",
+ card3 = "0",
+ expire_time = "0",
+ favorite = "0",
+ bound = "0",
+ unique_id = "0"
+ ))
+ w.write("\n")
diff --git a/hercules/code/server/db/skill.py b/hercules/code/server/db/skill.py
new file mode 100644
index 0000000..d21eb3d
--- /dev/null
+++ b/hercules/code/server/db/skill.py
@@ -0,0 +1,31 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2015 Evol Online
+# Author: Andrei Karas (4144)
+
+from code.fileutils import readFile
+
+def saveSkillTable(users):
+ dstFile = "newdb/skill.sql"
+ firstLine = True
+ tpl = readFile("templates/skill.sql")
+ with open(dstFile, "w") as w:
+ w.write(tpl)
+ w.write("INSERT INTO `skill` VALUES ")
+ for userId in users:
+ user = users[userId]
+ for skill in user.skills:
+
+ if firstLine == False:
+ w.write(",\n")
+ else:
+ firstLine = False
+
+ w.write(("({char_id},{id},{lv},{flag})").format(
+ char_id = user.char_id,
+ id = skill.skillId,
+ lv = skill.level,
+ flag = 0
+# flag = skill.flags
+ ))
+ w.write("\n")
diff --git a/hercules/code/server/dbitem.py b/hercules/code/server/dbitem.py
new file mode 100644
index 0000000..8534ad4
--- /dev/null
+++ b/hercules/code/server/dbitem.py
@@ -0,0 +1,7 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2015 Evol Online
+# Author: Andrei Karas (4144)
+
+class Item:
+ pass
diff --git a/hercules/code/server/dbskill.py b/hercules/code/server/dbskill.py
new file mode 100644
index 0000000..343f3af
--- /dev/null
+++ b/hercules/code/server/dbskill.py
@@ -0,0 +1,7 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2015 Evol Online
+# Author: Andrei Karas (4144)
+
+class Skill:
+ pass
diff --git a/hercules/code/server/dbuser.py b/hercules/code/server/dbuser.py
new file mode 100644
index 0000000..95ad55d
--- /dev/null
+++ b/hercules/code/server/dbuser.py
@@ -0,0 +1,7 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2015 Evol Online
+# Author: Andrei Karas (4144)
+
+class User:
+ pass
diff --git a/hercules/code/server/evol/__init__.py b/hercules/code/server/evol/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hercules/code/server/evol/__init__.py
diff --git a/hercules/code/server/evol/athena.py b/hercules/code/server/evol/athena.py
new file mode 100644
index 0000000..bad35da
--- /dev/null
+++ b/hercules/code/server/evol/athena.py
@@ -0,0 +1,207 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2015 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.stringutils import stripNewLine, removeGat
+from code.server.dbitem import Item
+from code.server.dbskill import Skill
+from code.server.dbuser import User
+
+comaSplit = re.compile(",")
+spaceSplit = re.compile(" ")
+
+def parseInventory(line, data):
+ items = []
+
+ if data == "":
+ return items
+
+ rows = spaceSplit.split(data)
+ if len(rows) < 1:
+ return items
+
+ for data2 in rows:
+ if data2 == "":
+ continue
+
+ rows2 = comaSplit.split(data2)
+
+ if len(rows2) != 12:
+ print "wrong inventory in account.txt line: " + stripNewLine(line)
+ continue
+
+ item = Item()
+
+ item.uknownId = rows2[0]
+ item.itemId = rows2[1]
+ item.amount = rows2[2]
+ item.equip = rows2[3]
+ item.color = rows2[4]
+ item.refine = rows2[5]
+ item.attribute = rows2[6]
+ item.card0 = rows2[7]
+ item.card1 = rows2[8]
+ item.card2 = rows2[9]
+ item.card3 = rows2[10]
+ item.broken = rows2[11]
+
+ items.append(item)
+
+ return items
+
+def parseSkills(line, data):
+ skills = []
+
+ if data == "":
+ return skills
+
+ rows = spaceSplit.split(data)
+ if len(rows) < 1:
+ return skills
+
+ for data2 in rows:
+ if data2 == "":
+ continue
+
+ rows2 = comaSplit.split(data2)
+
+ if len(rows2) != 2:
+ print "wrong skills in account.txt line: " + stripNewLine(line)
+ continue
+
+ skill = Skill()
+ skill.skillId = rows2[0]
+ skill.level = int(rows2[1]) & 0xffff
+ skill.flags = (int(rows2[1]) * 0xffff) & 0xffff
+
+ skills.append(skill)
+
+ return skills
+
+def parseVars(line, data):
+ variables = {}
+
+ if data == "":
+ return variables
+
+ rows = spaceSplit.split(data)
+ if len(rows) < 1:
+ return variables
+
+ for data2 in rows:
+ if data2 == "":
+ continue
+
+ rows2 = comaSplit.split(data2)
+
+ if len(rows2) != 2:
+ print "wrong variables in account.txt line: " + stripNewLine(line)
+ continue
+
+ variables[rows2[0]] = rows2[1]
+
+ return variables
+
+def readAthena():
+ srcFile = "olddb/athena.txt"
+ fieldsSplit = re.compile("\t")
+
+ users = {}
+ with open(srcFile, "r") as r:
+ for line in r:
+ if line[:2] == "//":
+ continue
+ rows = fieldsSplit.split(line)
+ if len(rows) == 2:
+ continue
+ if len(rows) != 20:
+ print "wrong account.txt line: " + stripNewLine(line)
+ continue
+
+ user = User()
+
+ user.char_id = rows[0]
+ tmp = comaSplit.split(rows[1])
+ user.account_id = tmp[0]
+ user.char_num = tmp[1]
+ user.char_name = rows[2]
+
+ tmp = comaSplit.split(rows[3])
+ user.char_class = tmp[0]
+ user.base_level = tmp[1]
+ user.job_level = tmp[2]
+
+ tmp = comaSplit.split(rows[4])
+ user.base_exp = tmp[0]
+ user.job_exp = tmp[1]
+ user.zeny = tmp[2]
+
+ tmp = comaSplit.split(rows[5])
+ user.hp = tmp[0]
+ user.max_hp = tmp[1]
+ user.sp = tmp[2]
+ user.max_sp = tmp[3]
+
+ tmp = comaSplit.split(rows[6])
+ user.stat_str = tmp[0]
+ user.stat_agi = tmp[1]
+ user.stat_vit = tmp[2]
+ user.stat_int = tmp[3]
+ user.stat_dex = tmp[4]
+ user.stat_luk = tmp[5]
+
+ tmp = comaSplit.split(rows[7])
+ user.status_point = tmp[0]
+ user.skill_point = tmp[1]
+
+ tmp = comaSplit.split(rows[8])
+ user.option = tmp[0]
+ user.karma = tmp[1]
+ user.manner = tmp[2]
+
+ tmp = comaSplit.split(rows[9])
+ user.party_id = tmp[0]
+ user.guild_id = tmp[1]
+ user.pet_id = tmp[2]
+
+ tmp = comaSplit.split(rows[10])
+ user.hair = tmp[0]
+ user.hair_color = tmp[1]
+ user.clothes_color = tmp[2]
+
+ tmp = comaSplit.split(rows[11])
+ user.weapon = tmp[0]
+ user.shield = tmp[1]
+ user.head_top = tmp[2]
+ user.head_mid = tmp[3]
+ user.head_bottom = tmp[4]
+
+ tmp = comaSplit.split(rows[12])
+ user.last_map = removeGat(tmp[0])
+ user.last_x = tmp[1]
+ user.last_y = tmp[2]
+
+ tmp = comaSplit.split(rows[13])
+ user.save_map = removeGat(tmp[0])
+ user.save_x = tmp[1]
+ user.save_y = tmp[2]
+ user.partner_id = tmp[3]
+ user.language_id = tmp[4]
+
+ # skip 14
+
+ user.inventory = parseInventory(line, rows[15])
+
+ # skip 16
+
+ user.skills = parseSkills(line, rows[17])
+
+ user.variables = parseVars(line, rows[18])
+ user.variables["Lang"] = user.language_id
+
+ users[user.char_id] = user
+
+ return users
diff --git a/hercules/code/server/evol/consts.py b/hercules/code/server/evol/consts.py
new file mode 100644
index 0000000..c63f559
--- /dev/null
+++ b/hercules/code/server/evol/consts.py
@@ -0,0 +1,49 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+def convertConsts(quests):
+ dstFile = "newserverdata/db/const.txt"
+ fieldsSplit = re.compile("\t+")
+ fields = dict()
+ with open(dstFile, "w") as w:
+ srcFile = "serverdata/db/const.txt"
+ with open(srcFile, "r") as r:
+ for line in r:
+ if len(line) < 2 or line[0:2] == "//":
+ continue
+ line = line.replace(" ", "\t")
+ rows = fieldsSplit.split(line)
+ sz = len(rows)
+ if sz < 2 or sz > 3:
+ continue
+
+ fields[rows[0]] = rows[1][:-1]
+ if sz == 2:
+ w.write("{0}\t{1}".format(rows[0], rows[1]))
+ else:
+ w.write("{0}\t{1}\t{2}".format(rows[0], rows[1], rows[2]))
+ # build in parameters
+ w.write("ClientVersion\t10000\t1\n");
+
+ srcFile = "oldserverdata/db/const.txt"
+ w.write("// evol constants\n")
+ with open(srcFile, "r") as r:
+ for line in r:
+ if len(line) < 2 or line[0:2] == "//":
+ continue
+ line = line.replace(" ", "\t")
+ rows = fieldsSplit.split(line)
+ if len(rows) != 2:
+ continue
+
+ if rows[0] in quests:
+ rows[1] = str(quests[rows[0]]) + "\n"
+ if rows[0] in fields:
+ if fields[rows[0]] != rows[1][:-1]:
+ print "warning: different const values for {0} ({1}, {2})".format(rows[0], rows[1][:-1], fields[rows[0]])
+ else:
+ w.write("{0}\t{1}".format(rows[0], rows[1]))
diff --git a/hercules/code/server/evol/itemdb.py b/hercules/code/server/evol/itemdb.py
new file mode 100644
index 0000000..a60e57d
--- /dev/null
+++ b/hercules/code/server/evol/itemdb.py
@@ -0,0 +1,104 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.configutils import writeIntField, writeStartBlock, writeEndBlock, writeStartScript, writeEndScript, writeStrField, writeSubField
+from code.fileutils import readFile
+
+def convertItemDb():
+ srcFile = "oldserverdata/db/item_db.txt"
+ dstFile = "newserverdata/db/re/item_db.conf"
+ constsFile = "newserverdata/db/const.txt"
+ fieldsSplit = re.compile(",")
+ scriptsSplit = re.compile("{")
+ items = dict()
+ with open(srcFile, "r") as r:
+ with open(dstFile, "w") as w:
+ with open(constsFile, "a") as c:
+ c.write("// items\n");
+ tpl = readFile("templates/item_db.tpl")
+ w.write(tpl)
+ for line in r:
+ if len(line) < 2 or line[0] == "#" or line[0:2] == "//":
+ continue
+ rows = fieldsSplit.split(line)
+ if len(rows) < 5 or rows[0] == "0":
+ continue
+
+ sz = len(rows)
+ if sz > 19:
+ sz = 19
+ for f in xrange(0, sz):
+ rows[f] = rows[f].strip()
+ if rows[4] == "2":
+ rows[4] = "0"
+ rows[3] = str(int(rows[3]) | 4)
+
+ items[rows[1]] = rows[0]
+ w.write("{\n")
+ c.write("{0}\t{1}\n".format(rows[1], rows[0]))
+ writeIntField(w, "Id", rows[0])
+ writeStrField(w, "AegisName", rows[1])
+ writeStrField(w, "Name", rows[2])
+ writeIntField(w, "Type", rows[4])
+ writeIntField(w, "Buy", rows[5])
+ writeIntField(w, "Sell", rows[6])
+ writeIntField(w, "Weight", rows[7])
+ writeIntField(w, "Atk", rows[8])
+ writeIntField(w, "Matk", "0")
+ writeIntField(w, "Def", rows[9])
+ writeIntField(w, "Range", rows[10])
+ writeIntField(w, "Slots", "0")
+ writeIntField(w, "Job", "0xFFFFFFFF")
+ writeIntField(w, "Upper", "0x3F")
+ writeIntField(w, "Gender", rows[13])
+ writeIntField(w, "Loc", rows[14])
+ writeIntField(w, "WeaponLv", rows[15])
+ writeIntField(w, "EquipLv", rows[16])
+ writeIntField(w, "Refine", "false")
+ if rows[14] == "2":
+ writeIntField(w, "View", "1")
+ else:
+ writeIntField(w, "View", rows[0])
+ writeIntField(w, "BindOnEquip", "false")
+ writeIntField(w, "BuyingStore", "false")
+ writeIntField(w, "Delay", "0")
+ trade = int(rows[3])
+ if trade != 0:
+ writeStartBlock(w, "Trade")
+ if trade & 1 == 1:
+ writeSubField(w, "nodrop", "true")
+ if trade & 2 == 2:
+ writeSubField(w, "notrade", "true")
+ if trade & 4 == 4:
+ writeSubField(w, "nodelonuse", "true")
+ if trade & 8 == 8:
+ writeSubField(w, "nostorage", "true")
+ if trade & 256 == 256:
+ writeSubField(w, "nogstorage", "true")
+ if trade & 512 == 512:
+ writeSubField(w, "noselltonpc", "true")
+ if trade != 0:
+ writeEndBlock(w)
+ writeIntField(w, "Sprite", "0")
+
+ scripts = ""
+ for f in xrange(sz, len(rows)):
+ scripts = scripts + ", " + rows[f]
+ rows = scriptsSplit.split(scripts)
+ cnt = len(rows)
+ if cnt > 1:
+ text = rows[1].strip()
+ if len(text) > 1:
+ text = text[:-2]
+ if text != "":
+ writeStartScript(w, "Script")
+ w.write(" {0}\n".format(text))
+ writeEndScript(w)
+
+ w.write("},\n")
+ w.write(")\n")
+ return items
diff --git a/hercules/code/server/evol/main.py b/hercules/code/server/evol/main.py
new file mode 100644
index 0000000..be9f0ab
--- /dev/null
+++ b/hercules/code/server/evol/main.py
@@ -0,0 +1,43 @@
+#! /usr/bin/env python
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+from code.server.account import convertAccount
+from code.server.accreg import convertAccReg
+from code.server.party import convertParty
+from code.server.storage import convertStorage
+from code.server.db.char import saveCharTable
+from code.server.db.charregnumdb import saveCharRegNumDbTable
+from code.server.db.inventory import saveInventoryTable
+from code.server.db.skill import saveSkillTable
+from code.server.evol.athena import readAthena
+from code.server.evol.consts import convertConsts
+from code.server.evol.itemdb import convertItemDb
+from code.server.evol.mobdb import convertMobDb
+from code.server.evol.mobskilldb import convertMobSkillDb
+from code.server.evol.npcs import createMainScript, convertNpcs
+from code.server.questsdb import convertQuestsDb
+from code.server.utils import cleanServerData
+
+def serverEvolMain():
+ cleanServerData()
+ createMainScript()
+ items = convertItemDb()
+ convertNpcs(items)
+ convertMobDb()
+ quests = convertQuestsDb()
+ convertConsts(quests)
+ convertMobSkillDb()
+
+def dbEvolMain():
+ convertAccount()
+ users = readAthena()
+ saveCharTable(users)
+ saveCharRegNumDbTable(users)
+ saveSkillTable(users)
+ saveInventoryTable(users)
+ convertStorage()
+ convertAccReg()
+ convertParty(users) \ No newline at end of file
diff --git a/hercules/code/server/evol/mobdb.py b/hercules/code/server/evol/mobdb.py
new file mode 100644
index 0000000..74eda04
--- /dev/null
+++ b/hercules/code/server/evol/mobdb.py
@@ -0,0 +1,89 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+def convertMobDb():
+ srcFile = "oldserverdata/db/mob_db.txt"
+ dstFile = "newserverdata/db/re/mob_db.txt"
+ fieldsSplit = re.compile(",")
+ with open(srcFile, "r") as r:
+ with open(dstFile, "w") as w:
+ for line in r:
+# if len(line) < 2 or line[0] == "#":
+ if len(line) < 2:
+ w.write(line)
+ continue
+ rows = fieldsSplit.split(line)
+ for f in xrange(0, len(rows)):
+ rows[f] = rows[f].strip()
+ w.write("{0:<5} {1:<15} {2:<16} {3:<16} {4:<5} {5:<5} {6:<5} "
+ "{7:<5} {8:<5} {9:<7} {10:<5} {11:<5} {12:<5} {13:<5} "
+ "{14:<5} {15:<5} {16:<5} {17:<5} {18:<5} {19:<5} {20:<7}"
+ " {21:<7} {22:<6} {23:<5} {24:<8} {25:<8} {26:<6} "
+ "{27:<8} {28:<9} {29:<8} {30:<5} {31:<7} {32:<8} {33:<7}"
+ " {34:<8} {35:<7} {36:<8} {37:<8} {38:<9} {39:<8} "
+ "{40:<9} {41:<8} {42:<9} {43:<8} {44:<9} {45:<8} {46:<9}"
+ " {47:<8} {48:<9} {49:<8} {50:<9} {51:<8} {52:<9} "
+ "{53:<8} {54:<9} {55:<8} {56:<8} \n".format(
+ rows[0] + ",",
+ rows[1] + ",",
+ rows[2] + ",",
+ rows[2] + ",",
+ rows[3] + ",",
+ rows[4] + ",",
+ rows[5] + ",",
+ rows[6] + ",",
+ rows[7] + ",",
+ rows[8] + ",",
+ rows[9] + ",",
+ rows[10] + ",",
+ rows[11] + ",",
+ rows[12] + ",",
+ rows[13] + ",",
+ rows[14] + ",",
+ rows[15] + ",",
+ rows[16] + ",",
+ rows[17] + ",",
+ rows[18] + ",",
+ rows[19] + ",",
+ rows[20] + ",",
+ rows[21] + ",",
+ rows[22] + ",",
+ rows[23] + ",",
+ rows[24] + ",",
+ rows[25] + ",",
+ rows[26] + ",",
+ rows[27] + ",",
+ rows[28] + ",",
+ rows[45] + ",",
+ rows[47] + ",",
+ rows[48] + ",",
+ rows[49] + ",",
+ rows[50] + ",",
+ rows[51] + ",",
+ rows[52] + ",",
+ rows[29] + ",",
+ rows[30] + ",",
+ rows[31] + ",",
+ rows[32] + ",",
+ rows[33] + ",",
+ rows[34] + ",",
+ rows[35] + ",",
+ rows[36] + ",",
+ rows[37] + ",",
+ rows[38] + ",",
+ rows[39] + ",",
+ rows[40] + ",",
+ rows[41] + ",",
+ rows[42] + ",",
+ rows[43] + ",",
+ rows[44] + ",",
+ "0,",
+ "0,",
+ "0,",
+ "0"
+ ))
+
diff --git a/hercules/code/server/evol/mobskilldb.py b/hercules/code/server/evol/mobskilldb.py
new file mode 100644
index 0000000..a5a7d5f
--- /dev/null
+++ b/hercules/code/server/evol/mobskilldb.py
@@ -0,0 +1,50 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.fileutils import readFile
+
+def convertMobSkillDb():
+ srcFile = "oldserverdata/db/mob_skill_db.txt"
+ dstFile = "newserverdata/db/re/mob_skill_db.txt"
+ fieldsSplit = re.compile(",")
+ with open(srcFile, "r") as r:
+ with open(dstFile, "w") as w:
+ tpl = readFile("templates/mob_skill_db.tpl")
+ w.write(tpl)
+ for line in r:
+ if len(line) < 2:
+ w.write(line)
+ continue
+ rows = fieldsSplit.split(line)
+ if len(rows) < 10:
+ w.write(line)
+ continue
+
+ for f in xrange(0, len(rows)):
+ rows[f] = rows[f].strip()
+
+ w.write("{0},{1},{2},{3},{4},{5},{6},"
+ "{7},{8},{9},{10},{11},{12},{13},"
+ "{14},{15},,,\n".format(
+ rows[0],
+ rows[1],
+ rows[2],
+ rows[3],
+ rows[4],
+ rows[5],
+ rows[6],
+ rows[7],
+ rows[8],
+ rows[9],
+ rows[10],
+ rows[11],
+ rows[13],
+ rows[14],
+ rows[15],
+ rows[16]
+ ))
+
diff --git a/hercules/code/server/evol/npcs.py b/hercules/code/server/evol/npcs.py
new file mode 100644
index 0000000..6efe811
--- /dev/null
+++ b/hercules/code/server/evol/npcs.py
@@ -0,0 +1,281 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import os
+import re
+
+from code.fileutils import makeDir
+from code.stringutils import stripWindows, stripNewLine
+
+mapsConfFile = "newserverdata/conf/maps.conf"
+mapsIndexFile = "newserverdata/db/map_index.txt"
+npcMainScript = "newserverdata/npc/re/scripts_main.conf"
+mapsIndex = 1
+scriptRe = re.compile("^(?P<map>[^/](.+))[.]gat,([ ]*)(?P<x>[\d]+),([ ]*)(?P<y>[\d]+),([ ]*)(?P<dir>[\d]+)(|,(?P<gender>[\d]+))" +
+ "[\t](?P<tag>script)[\t](?P<name>[\w#' ]+)[\t]"
+ "(?P<class>[\d]+)((,((?P<xs>[\d]+),(?P<ys>[\d]+)))|)(|;(?P<size>[\d]+))(|,|;){$")
+
+shopRe = re.compile("^(?P<map>[^/](.+))[.]gat,([ ]*)(?P<x>[\d]+),([ ]*)(?P<y>[\d]+),([ ]*)(?P<dir>[\d]+)(|,(?P<gender>[\d]+))" +
+ "[\t](?P<tag>shop)[\t](?P<name>[\w#' ]+)[\t]"
+ "(?P<class>[\d]+),(?P<items>(.+))$")
+
+mapFlagRe = re.compile("^(?P<map>[^/](.+))[.]gat" +
+ "[ ](?P<tag>mapflag)[ ](?P<name>[\w#']+)(|[ ](?P<flag>.*))$")
+
+warpRe = re.compile("^(?P<map>[^/](.+))[.]gat,([ ]*)(?P<x>[\d]+),([ ]*)(?P<y>[\d]+)[\t]"
+ "(?P<tag>warp)[\t](?P<name>[^\t]+)[\t](?P<xs>[\d-]+),(?P<ys>[\d-]+),(?P<targetmap>[^/](.+))[.]gat,([ ]*)(?P<targetx>[\d]+),([ ]*)(?P<targety>[\d]+)$")
+
+monsterRe = re.compile("^(?P<map>[^/](.+))[.]gat,([ ]*)(?P<x>[\d]+),([ ]*)(?P<y>[\d]+),([ ]*)(?P<xs>[\d-]+),(?P<ys>[\d-]+)\t"
+ "(?P<tag>monster)[\t](?P<name>[\w#' ]+)[\t]"
+ "(?P<class>[\d]+),(?P<num>[\d]+),(?P<look>[\d-]+),(?P<delay1>[\d]+),(?P<delay2>[\d]+)$")
+
+setRe = re.compile("^(?P<space>[ ]+)set[ ](?P<var>[^,]+),([ ]*)(?P<val>[^;]+);$");
+
+class ScriptTracker:
+ pass
+
+def createMainScript():
+ with open(npcMainScript, "w") as w:
+ w.write("npc: npc/functions/main.txt\n")
+ w.write("import: npc/scripts.conf\n")
+
+def convertNpcs(items):
+ processNpcDir("oldserverdata/npc/", "newserverdata/npc/", items)
+
+def processNpcDir(srcDir, dstDir, items):
+ makeDir(dstDir)
+ files = os.listdir(srcDir)
+ for file1 in files:
+ if file1[0] == '.':
+ continue
+ srcPath = os.path.abspath(srcDir + os.path.sep + file1)
+ dstPath = os.path.abspath(dstDir + os.path.sep + file1)
+ if not os.path.isfile(srcPath):
+ processNpcDir(srcPath, dstPath, items)
+ else:
+ if file1[-5:] == ".conf" or file1[-4:] == ".txt":
+ processNpcFile(srcPath, dstPath, items)
+
+def processNpcFile(srcFile, dstFile, items):
+# print "file: " + srcFile
+ tracker = ScriptTracker()
+ tracker.insideScript = False
+ tracker.items = items
+ with open(srcFile, "r") as r:
+ with open(dstFile, "w") as w:
+ tracker.w = w
+ for line in r:
+ tracker.line = stripWindows(line)
+ res = convertTextLine(tracker)
+ if res:
+ w.write(tracker.line)
+
+def convertTextLine(tracker):
+ line = tracker.line
+ if line[:5] == "map: ":
+ processScriptMapLine(line)
+ return False
+
+ idx = line.find("\tscript\t")
+ if idx >= 0:
+ processScript(tracker)
+ return False
+ idx = line.find("\tshop\t")
+ if idx >= 0:
+ processShop(tracker)
+ return False
+ idx = line.find("\tmonster\t")
+ if idx >= 0:
+ processMonster(tracker)
+ return False
+ idx = line.find("\twarp\t")
+ if idx >= 0:
+ processWarp(tracker)
+ return False
+ idx = line.find(".gat mapflag ")
+ if idx >= 0:
+ processMapFlag(tracker)
+ return False
+ processStrReplace(tracker)
+ return False
+
+def processScriptMapLine(line):
+ global mapsIndex
+ line = stripNewLine(line)
+ if line[-4:] == ".gat":
+ line = line[:-4]
+ with open(mapsConfFile, "a") as w:
+ w.write(line + "\n")
+
+ with open(mapsIndexFile, "a") as w:
+ w.write("{0} {1}\n".format(line[5:], mapsIndex))
+ mapsIndex = mapsIndex + 1
+
+def writeScript(w, m):
+ if m.group("gender") != None:
+ w.write("// Gender = {0}\n".format(m.group("gender")));
+
+ if m.group("x") == 0 and m.group("y") == 0: # float npc
+ w.write("-")
+ else:
+ w.write("{0},{1},{2},{3}".format(m.group("map"), m.group("x"), m.group("y"), m.group("dir")))
+ class_ = m.group("class")
+ if class_ == "0": # hidden npc
+ class_ = "32767"
+ else:
+ class_ = int(class_)
+ if class_ > 125 and class_ <= 400:
+ class_ = class_ + 100
+ w.write("\t{0}\t{1}\t{2}".format(m.group("tag"), m.group("name"), class_));
+
+def processScript(tracker):
+ line = tracker.line
+ w = tracker.w
+ if line[:9] == "function\t":
+ tracker.w.write(line)
+ return
+
+ m = scriptRe.search(line)
+ if m == None:
+ print "error in parsing: " + line
+ w.write("!!!error parsing line")
+ w.write(line)
+ return
+# print "source=" + line[:-1]
+# print "map={0} x={1} y={2} dir={3} gender={4} tag={5} name={6} class={7}, xs={8}, ys={9}, size={10}".format(
+# m.group("map"), m.group("x"), m.group("y"), m.group("dir"), m.group("gender"),
+# m.group("tag"), m.group("name"), m.group("class"), m.group("xs"), m.group("ys"), m.group("size"))
+
+ if m.group("size") != None:
+ w.write("// Size = {0}\n".format(m.group("size")));
+ writeScript(w, m)
+ if m.group("xs") != None:
+ w.write(",{0},{1}".format(m.group("xs"), m.group("ys")));
+ w.write(",{\n");
+
+
+def itemsToShop(tracker, itemsStr):
+ itemsSplit = re.compile(",")
+ itemsSplit2 = re.compile(":")
+ itemsDict = tracker.items
+ outStr = ""
+ items = itemsSplit.split(itemsStr)
+ for str2 in items:
+ parts = itemsSplit2.split(str2)
+ if outStr != "":
+ outStr = outStr + ","
+ outStr = outStr + itemsDict[parts[0].strip()] + ":" + parts[1]
+ return outStr
+
+def processShop(tracker):
+ line = tracker.line
+ w = tracker.w
+
+ m = shopRe.search(line)
+ if m == None:
+ print "error in parsing: " + line
+ w.write("!!!error parsing line")
+ w.write(line)
+ return
+# print "source=" + line[:-1]
+# print "map={0} x={1} y={2} dir={3} gender={4} tag={5} name={6} class={7} items={8}".format(
+# m.group("map"), m.group("x"), m.group("y"), m.group("dir"), m.group("gender"),
+# m.group("tag"), m.group("name"), m.group("class"), m.group("items"))
+
+ writeScript(w, m)
+ w.write("," + itemsToShop(tracker, m.group("items")) + "\n")
+
+def processMapFlag(tracker):
+ line = tracker.line
+ w = tracker.w
+
+ m = mapFlagRe.search(line)
+ if m == None:
+ print "error in parsing: " + line
+ w.write("!!!error parsing line")
+ w.write(line)
+ return
+# print "source=" + line[:-1]
+# print "map={0} tag={1} name={2} flag={3}".format(
+# m.group("map"), m.group("tag"), m.group("name"), m.group("flag"))
+
+ w.write("{0}\t{1}\t{2}".format(m.group("map"), m.group("tag"), m.group("name")))
+ if m.group("flag") == None:
+ w.write("\n")
+ else:
+ w.write("\t{0}\n".format(m.group("flag")))
+
+def processWarp(tracker):
+ line = tracker.line
+ w = tracker.w
+ m = warpRe.search(line)
+ if m == None:
+ print "error in parsing: " + line
+ w.write("!!!error parsing line")
+ w.write(line)
+ return
+
+# print "source=" + line[:-1]
+# print "map={0} xy={1},{2} tag={3} name={4} xs={5} ys={6} target: map={7} xy={8},{9}".format(
+# m.group("map"), m.group("x"), m.group("y"), m.group("tag"), m.group("name"), m.group("xs"), m.group("ys"), m.group("targetmap"), m.group("targetx"), m.group("targety"))
+
+ xs = int(m.group("xs"))
+ ys = int(m.group("ys"))
+ if xs < 0:
+ xs = 0
+ if ys < 0:
+ ys = 0
+ w.write("{0},{1},{2},{3}\t{4}\t{5}\t{6},{7},{8},{9},{10}\n".format(
+ m.group("map"), m.group("x"), m.group("y"), "0", m.group("tag"), m.group("name"),
+ xs, ys, m.group("targetmap"), m.group("targetx"), m.group("targety")))
+
+
+def processMonster(tracker):
+ line = tracker.line
+ w = tracker.w
+ m = monsterRe.search(line)
+ if m == None:
+ print "error in parsing: " + line
+ w.write("!!!error parsing line")
+ w.write(line)
+ return
+
+# print "source=" + line[:-1]
+# print ("map={0} xy={1},{2} xs={3} ys={4} tag={5} name={6} class={7} " +
+# "num={8} look={9} delays={10},{11}").format(
+# m.group("map"), m.group("x"), m.group("y"), m.group("xs"), m.group("ys"),
+# m.group("tag"), m.group("name"), m.group("class"),
+# m.group("num"), m.group("look"), m.group("delay1"), m.group("delay2"))
+ w.write("{0},{1},{2},{3},{4}\t{5}\t{6}\t{7},{8},{9},{10}\n".format(m.group("map"),
+ m.group("x"), m.group("y"), m.group("xs"), m.group("ys"),
+ m.group("tag"), m.group("name"),
+ m.group("class"), m.group("num"), m.group("delay1"), m.group("delay2")))
+
+
+def processStrReplace(tracker):
+ line = tracker.line
+ w = tracker.w
+ line = line.replace("setskill ", "addtoskill ")
+ line = line.replace("zeny", "Zeny")
+ line = line.replace("getclientversion(\"\")", "ClientVersion")
+ line = line.replace("getclientversion()", "ClientVersion")
+ line = line.replace("setlang @", "Lang = @")
+ line = re.sub("([^@^$])@([^@])", "\\1.@\\2", line)
+ line = line.replace(".@menu", "@menu")
+ idx = line.find("getmapmobs(")
+ if idx >= 0:
+ idx2 = line.find("\"", idx + len("getmapmobs(") + 1)
+ idx3 = line.find(")", idx + len("getmapmobs(") + 1)
+ if idx2 + 1 == idx3:
+ line = line[:idx2 + 1] + ",\"all\"" + line[idx2 + 1:]
+
+ line = line.replace("getmapmobs(", "mobcount(")
+
+ m = setRe.search(line);
+ if m != None:
+ line = "{0}{1} = {2};\n".format(m.group("space"), m.group("var"), m.group("val"))
+
+ w.write(line)
+
diff --git a/hercules/code/server/maps.py b/hercules/code/server/maps.py
new file mode 100644
index 0000000..2ef0173
--- /dev/null
+++ b/hercules/code/server/maps.py
@@ -0,0 +1,43 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import array
+import struct
+import zlib
+
+from code.fileutils import readMapName, readInt16, readInt32, readData, makeDir
+
+def listMapCache(f, mapsCount):
+ print "Known maps:"
+ print "{0:12} {1:<4}x {2:<4} {3:<10}".format("Map name", "sx", "sy", "compressed size")
+ for i in xrange(0, mapsCount):
+ name = readMapName(f)
+ sx = readInt16(f)
+ sy = readInt16(f)
+ sz = readInt32(f)
+ print "{0:12} {1:<4}x {2:<4} {3:<10}".format(name, sx, sy, sz)
+ f.seek(sz, 1)
+
+def extractMaps(f, mapsCount):
+ destDir = "maps/"
+ makeDir(destDir)
+ for i in xrange(0, mapsCount):
+ name = readMapName(f)
+ sx = readInt16(f)
+ sy = readInt16(f)
+ sz = readInt32(f)
+ data = readData(f, sz)
+ dc = zlib.decompressobj()
+ data = dc.decompress(data)
+ with open(destDir + name, "wb") as w:
+ w.write(struct.pack("H", sx))
+ w.write(struct.pack("H", sy))
+ w.write(data)
+ with open(destDir + name + ".txt", "wb") as w:
+ arr = array.array("B")
+ arr.fromstring(data)
+ for x in xrange(0, sx):
+ for y in xrange(0, sy):
+ w.write("{0},{1}:{2}\n".format(x, y, arr[x + y * sx]))
diff --git a/hercules/code/server/party.py b/hercules/code/server/party.py
new file mode 100644
index 0000000..c75a541
--- /dev/null
+++ b/hercules/code/server/party.py
@@ -0,0 +1,80 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.fileutils import readFile
+from code.stringutils import stripNewLine, escapeSqlStr
+
+def findLeaderId(name, users):
+ for userId in users:
+ user = users[userId]
+ if user.char_name == name:
+ return user.char_id
+ return 0
+
+def convertParty(users):
+ srcFile = "olddb/party.txt"
+ dstFile = "newdb/party.sql"
+ fieldsSplit = re.compile("\t")
+ comaSplit = re.compile(",")
+ tpl = readFile("templates/party.sql")
+ firstLine = True
+ with open(dstFile, "w") as w:
+ w.write(tpl)
+ w.write("INSERT INTO `party` VALUES ")
+ with open(srcFile, "r") as r:
+ for line in r:
+ if line[:2] == "//":
+ continue
+ line = stripNewLine(line)
+ rows = fieldsSplit.split(line)
+ if len(rows) == 2:
+ continue
+ if len(rows) < 3:
+ print "wrong party.txt line: " + line
+ continue
+
+ partyId = rows[0]
+ partyName = rows[1]
+
+ tmp = comaSplit.split(rows[2])
+ partyExp = tmp[0]
+ partyItem = tmp[1]
+
+ leaderId = 0
+ leaderName = ""
+ accountId = ""
+
+ for f in xrange(3, len(rows), 2):
+
+ if rows[f] == "0,0" or rows[f] == "":
+ continue
+
+ tmp = comaSplit.split(rows[f])
+ accountId = tmp[0]
+ leader = tmp[1]
+ charName = rows[f + 1]
+ if leader == "1":
+ leaderId = accountId
+ leaderName = charName
+
+ if firstLine == False:
+ w.write(",\n")
+ else:
+ firstLine = False
+
+ leaderCharId = findLeaderId(leaderName, users)
+
+ w.write(("({party_id},'{name}',{exp},{item}," +
+ "{leader_id},{leader_char})").format(
+ party_id = partyId,
+ name = escapeSqlStr(partyName),
+ exp = partyExp,
+ item = partyItem,
+ leader_id = leaderId,
+ leader_char = leaderCharId
+ ))
+ w.write("\n")
diff --git a/hercules/code/server/questsdb.py b/hercules/code/server/questsdb.py
new file mode 100644
index 0000000..880a7f9
--- /dev/null
+++ b/hercules/code/server/questsdb.py
@@ -0,0 +1,33 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+from code.fileutils import readFile
+
+def convertQuestsDb():
+ srcFile = "oldserverdata/db/questvars.txt"
+ dstFile = "newserverdata/db/quest_db.txt"
+ quests = dict()
+ with open(srcFile, "r") as r:
+ with open(dstFile, "w") as w:
+ tpl = readFile("templates/quest_db.tpl")
+ w.write(tpl)
+ cnt = 0
+ for line in r:
+ if len(line) < 2 or line[0:2] == "//":
+ continue
+
+ idx = line.find("// ")
+ if idx < 3:
+ continue
+ line = line[idx + 3:]
+ idx = line.find(" ")
+ if idx < 3:
+ continue
+ line = line[:idx]
+
+ w.write("{0},0,0,0,0,0,0,0,\"{1}\"\n".format(cnt, line))
+ quests[line] = cnt
+ cnt = cnt + 1
+ return quests
diff --git a/hercules/code/server/storage.py b/hercules/code/server/storage.py
new file mode 100644
index 0000000..6bddb52
--- /dev/null
+++ b/hercules/code/server/storage.py
@@ -0,0 +1,81 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.fileutils import readFile
+from code.stringutils import stripNewLine
+from code.server.dbitem import Item
+
+def convertStorage():
+ srcFile = "olddb/storage.txt"
+ dstFile = "newdb/storage.sql"
+ fieldsSplit = re.compile("\t")
+ comaSplit = re.compile(",")
+ spaceSplit = re.compile(" ")
+ tpl = readFile("templates/storage.sql")
+ firstLine = True
+ with open(dstFile, "w") as w:
+ w.write(tpl)
+ w.write("INSERT INTO `storage` VALUES ")
+ with open(srcFile, "r") as r:
+ for line in r:
+ if line[:2] == "//":
+ continue
+ rows = fieldsSplit.split(line)
+ if len(rows) == 2:
+ continue
+ if len(rows) != 3:
+ print "wrong storage.txt line: " + stripNewLine(line)
+ continue
+
+ tmp = comaSplit.split(rows[0])
+ accountId = tmp[0]
+# storage_amount = tmp[1]
+
+ data = spaceSplit.split(rows[1])
+ for itemStr in data:
+ if itemStr == "":
+ continue
+
+ tmp = comaSplit.split(itemStr)
+ item = Item()
+ item.unknownId = tmp[0]
+ item.itemId = tmp[1]
+ item.amount = tmp[2]
+ item.equip = tmp[3]
+ item.color = tmp[4]
+ item.refine = tmp[5]
+ item.attribute = tmp[6]
+ item.card0 = tmp[7]
+ item.card1 = tmp[8]
+ item.card2 = tmp[9]
+ item.card3 = "0"
+
+ if firstLine == False:
+ w.write(",\n")
+ else:
+ firstLine = False
+
+ w.write(("({id},{account_id},{nameid},{amount},{equip},{identify}," +
+ "{refine},{attribute},{card0},{card1},{card2},{card3}," +
+ "{expire_time},{bound},{unique_id})").format(
+ id = 0,
+ account_id = accountId,
+ nameid = item.itemId,
+ amount = item.amount,
+ equip = item.equip,
+ identify = "1",
+ refine = item.refine,
+ attribute = item.attribute,
+ card0 = "0",
+ card1 = "0",
+ card2 = "0",
+ card3 = "0",
+ expire_time = "0",
+ bound = "0",
+ unique_id = "0"
+ ))
+ w.write("\n")
diff --git a/hercules/code/server/tmw/__init__.py b/hercules/code/server/tmw/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hercules/code/server/tmw/__init__.py
diff --git a/hercules/code/server/tmw/athena.py b/hercules/code/server/tmw/athena.py
new file mode 100644
index 0000000..6548b2e
--- /dev/null
+++ b/hercules/code/server/tmw/athena.py
@@ -0,0 +1,207 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2015 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.stringutils import stripNewLine
+from code.server.dbitem import Item
+from code.server.dbskill import Skill
+from code.server.dbuser import User
+
+comaSplit = re.compile(",")
+spaceSplit = re.compile(" ")
+
+def parseInventory(line, data):
+ items = []
+
+ if data == "":
+ return items
+
+ rows = spaceSplit.split(data)
+ if len(rows) < 1:
+ return items
+
+ for data2 in rows:
+ if data2 == "":
+ continue
+
+ rows2 = comaSplit.split(data2)
+
+ if len(rows2) != 12:
+ print "wrong inventory in account.txt line: " + stripNewLine(line)
+ continue
+
+ item = Item()
+
+ item.uknownId = rows2[0]
+ item.itemId = rows2[1]
+ item.amount = rows2[2]
+ item.equip = rows2[3]
+ item.color = rows2[4]
+ item.refine = rows2[5]
+ item.attribute = rows2[6]
+ item.card0 = rows2[7]
+ item.card1 = rows2[8]
+ item.card2 = rows2[9]
+ item.card3 = rows2[10]
+ item.broken = rows2[11]
+
+ items.append(item)
+
+ return items
+
+def parseSkills(line, data):
+ skills = []
+
+ if data == "":
+ return skills
+
+ rows = spaceSplit.split(data)
+ if len(rows) < 1:
+ return skills
+
+ for data2 in rows:
+ if data2 == "":
+ continue
+
+ rows2 = comaSplit.split(data2)
+
+ if len(rows2) != 2:
+ print "wrong skills in account.txt line: " + stripNewLine(line)
+ continue
+
+ skill = Skill()
+ skill.skillId = rows2[0]
+ skill.level = int(rows2[1]) & 0xffff
+ skill.flags = (int(rows2[1]) * 0xffff) & 0xffff
+
+ skills.append(skill)
+
+ return skills
+
+def parseVars(line, data):
+ variables = {}
+
+ if data == "":
+ return variables
+
+ rows = spaceSplit.split(data)
+ if len(rows) < 1:
+ return variables
+
+ for data2 in rows:
+ if data2 == "":
+ continue
+
+ rows2 = comaSplit.split(data2)
+
+ if len(rows2) != 2:
+ print "wrong variables in account.txt line: " + stripNewLine(line)
+ continue
+
+ variables[rows2[0]] = rows2[1]
+
+ return variables
+
+def readAthena():
+ srcFile = "olddb/athena.txt"
+ fieldsSplit = re.compile("\t")
+
+ users = {}
+ with open(srcFile, "r") as r:
+ for line in r:
+ if line[:2] == "//":
+ continue
+ rows = fieldsSplit.split(line)
+ if len(rows) == 2:
+ continue
+ if len(rows) != 20:
+ print "wrong account.txt line: " + stripNewLine(line)
+ continue
+
+ user = User()
+
+ user.char_id = rows[0]
+ tmp = comaSplit.split(rows[1])
+ user.account_id = tmp[0]
+ user.char_num = tmp[1]
+ user.char_name = rows[2]
+
+ tmp = comaSplit.split(rows[3])
+ user.char_class = tmp[0]
+ user.base_level = tmp[1]
+ user.job_level = tmp[2]
+
+ tmp = comaSplit.split(rows[4])
+ user.base_exp = tmp[0]
+ user.job_exp = tmp[1]
+ user.zeny = tmp[2]
+
+ tmp = comaSplit.split(rows[5])
+ user.hp = tmp[0]
+ user.max_hp = tmp[1]
+ user.sp = tmp[2]
+ user.max_sp = tmp[3]
+
+ tmp = comaSplit.split(rows[6])
+ user.stat_str = tmp[0]
+ user.stat_agi = tmp[1]
+ user.stat_vit = tmp[2]
+ user.stat_int = tmp[3]
+ user.stat_dex = tmp[4]
+ user.stat_luk = tmp[5]
+
+ tmp = comaSplit.split(rows[7])
+ user.status_point = tmp[0]
+ user.skill_point = tmp[1]
+
+ tmp = comaSplit.split(rows[8])
+ user.option = tmp[0]
+ user.karma = tmp[1]
+ user.manner = tmp[2]
+
+ tmp = comaSplit.split(rows[9])
+ user.party_id = tmp[0]
+ user.guild_id = tmp[1]
+ user.pet_id = tmp[2]
+
+ tmp = comaSplit.split(rows[10])
+ user.hair = tmp[0]
+ user.hair_color = tmp[1]
+ user.clothes_color = tmp[2]
+
+ tmp = comaSplit.split(rows[11])
+ user.weapon = tmp[0]
+ user.shield = tmp[1]
+ user.head_top = tmp[2]
+ user.head_mid = tmp[3]
+ user.head_bottom = tmp[4]
+
+ tmp = comaSplit.split(rows[12])
+ user.last_map = tmp[0]
+ user.last_x = tmp[1]
+ user.last_y = tmp[2]
+
+ tmp = comaSplit.split(rows[13])
+ user.save_map = tmp[0]
+ user.save_x = tmp[1]
+ user.save_y = tmp[2]
+
+ user.partner_id = "0"
+ user.language_id = "0"
+
+ # skip 14
+
+ user.inventory = parseInventory(line, rows[15])
+
+ # skip 16
+
+ user.skills = parseSkills(line, rows[17])
+
+ user.variables = parseVars(line, rows[18])
+
+ users[user.char_id] = user
+
+ return users
diff --git a/hercules/code/server/tmw/consts.py b/hercules/code/server/tmw/consts.py
new file mode 100644
index 0000000..5b8403e
--- /dev/null
+++ b/hercules/code/server/tmw/consts.py
@@ -0,0 +1,129 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import os
+import re
+
+from code.fileutils import readFile
+from code.stringutils import stripNewLine
+
+fieldsSplit = re.compile(":")
+
+def getConstsFile(srcDir):
+ files = os.listdir(srcDir)
+ for srcFile in files:
+ if srcFile.find("const") == 0 and srcFile[-4:] == ".txt":
+ yield srcDir + srcFile
+
+def readOneConst(r, line):
+ key = ""
+ val = ""
+ depr = 0
+ if line.find(": {") > 0:
+ rows = fieldsSplit.split(line)
+ key = rows[0].strip()
+ line = r.next().strip()
+ rows = fieldsSplit.split(line)
+ if len(rows) != 2:
+ print "error"
+ return ("", "", 0)
+ if rows[0] == "Value":
+ val = rows[1]
+ line = r.next().strip()
+ rows = fieldsSplit.split(line)
+ if len(rows) != 2:
+ print "error"
+ return ("", "", 0)
+ rows[1] = rows[1].strip()
+ if rows[0] == "Deprecated" and rows[1].find("true") == 0:
+ depr = 1
+ else:
+ rows = fieldsSplit.split(line)
+ if len(rows) != 2:
+ return ("", "", 0)
+ key = rows[0];
+ val = rows[1]
+ return (key, val, depr)
+
+def writeConst(w, const):
+ if const[2] == 1:
+ w.write("\t{0}: {{\n\t\tValue:{1}\n\t\tDeprecated: true\n\t}}\n".format(const[0], const[1]))
+ else:
+ w.write("\t{0}:{1}\n".format(const[0], const[1]))
+
+def convertConsts(quests, npcIds):
+ dstFile = "newserverdata/db/constants.conf"
+ fields = dict()
+ vals = [("MF_NOTELEPORT", "mf_noteleport"),
+ ("MF_NORETURN", "mf_noreturn"),
+ ("MF_MONSTER_NOTELEPORT", "mf_monster_noteleport"),
+ ("MF_NOSAVE", "mf_nosave"),
+ ("MF_NOPENALTY", "mf_nopenalty"),
+ ("MF_PVP", "mf_pvp"),
+ ("MF_PVP_NOPARTY", "mf_pvp_noparty"),
+ ("MF_PVP_NOCALCRANK", "mf_pvp_nocalcrank"),
+ ("MF_NOWARP", "mf_nowarp"),
+ ("MF_NOWARPTO", "mf_nowarpto"),
+ ("MF_SNOW", "mf_snow"),
+ ("MF_FOG", "mf_fog"),
+ ("MF_SAKURA", "mf_sakura"),
+ ("MF_LEAVES", "mf_leaves"),
+ ("MF_TOWN", "mf_town"),
+ ("sc_poison", "SC_POISON"),
+ ("sc_slowpoison", "SC_SLOWPOISON"),
+ ("sc_adrenaline", "SC_ADRENALINE"),
+ ]
+ with open(dstFile, "w") as w:
+ tpl = readFile("templates/constants.tpl")
+ w.write(tpl);
+ srcFile = "serverdata/db/constants.conf"
+ with open(srcFile, "r") as r:
+ for line in r:
+ if line.find("**************************************************************************/") >= 0:
+ break
+
+ for line in r:
+ line = line.strip()
+ if len(line) < 2 or line[0:2] == "//" or line[0:2] == "/*":
+ continue
+ const = readOneConst(r, line)
+ if const[0] == "comment__":
+ continue
+ fields[const[0]] = const[1].strip()
+ writeConst(w, const)
+
+ srcDir = "oldserverdata/world/map/db/"
+ w.write("// tmw constants\n")
+
+ fieldsSplit = re.compile("\t+")
+
+ for srcFile in getConstsFile(srcDir):
+ with open(srcFile, "r") as r:
+ for line in r:
+ if len(line) < 2 or line[0:2] == "//":
+ continue
+ line = line.replace(" ", "\t")
+ rows = fieldsSplit.split(line)
+ if len(rows) != 2:
+ continue
+
+ for val in vals:
+ if rows[0] == val[0]:
+ rows[0] = val[1]
+ if rows[0] in quests:
+ rows[1] = str(quests[rows[0]]) + "\n"
+ if rows[0] in fields:
+ if fields[rows[0]] != rows[1][:-1]:
+ print "warning: different const values for {0} ({1}, {2})".format(rows[0], rows[1][:-1], fields[rows[0]])
+ else:
+ writeConst(w, (rows[0], stripNewLine(rows[1]), 0))
+ w.write("// tmw npcs\n")
+ for npc in npcIds:
+ if npc == -1:
+ key = "MINUS1"
+ else:
+ key = str(npc)
+ writeConst(w, ("NPC" + key, npc, 0))
+ w.write("}")
diff --git a/hercules/code/server/tmw/itemdb.py b/hercules/code/server/tmw/itemdb.py
new file mode 100644
index 0000000..beccd09
--- /dev/null
+++ b/hercules/code/server/tmw/itemdb.py
@@ -0,0 +1,264 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import os
+import re
+
+from code.configutils import writeStartScript, writeEndScript, writeIntField, writeStrField
+from code.fileutils import readFile
+
+def getItemDbFile(srcDir):
+ files = os.listdir(srcDir)
+ for srcFile in files:
+ if srcFile.find("item_db") >= 0:
+ yield srcFile
+
+def replaceStr(line):
+ vals = [
+ ("setskill ", "addtoskill "),
+ ("zeny", "Zeny"),
+ ("sc_poison", "SC_POISON"),
+ ("sc_slowpoison", "SC_SLOWPOISON"),
+ ("sex", "Sex"),
+ ("SEX", "Sex"),
+
+ (".gat", ""),
+ ("Bugleg", "BugLeg"),
+ ("set BugLeg, 0;", "//set BugLeg, 0;"),
+ ("set CaveSnakeLamp, 0;", "//set CaveSnakeLamp, 0;"),
+ ("set Class, @BaseClass;", "//set Class, @BaseClass;"),
+ ("goto_Exit;", "goto L_Exit;"),
+ ("if @spants_state < 7 goto", "if(@spants_state < 7) goto"),
+ ("isdead()", "ispcdead()"),
+ ("changeSex", "changecharsex()"),
+ ("getpartnerid2()", "getpartnerid()"),
+
+ ("getmap()", "getmapname()"),
+ ("L_end", "L_End"),
+ ("gmcommand", "atcommand"),
+ ("MF_NOSAVE", "mf_nosave"),
+ ("S_update_var", "S_Update_Var"),
+ ("L_teach", "L_Teach"),
+ ("L_focus", "L_Focus"),
+ ("L_unfocus", "L_Unfocus"),
+ ("L_main", "L_Main"),
+ ("L_next", "L_Next"),
+ ("L_close", "L_Close"),
+ ("@NpcName$", "@npcname$"),
+ ("@cost", "@Cost"),
+ ("@TEMP", "@temp"),
+ ("L_Menuitems", "L_MenuItems"),
+ ("L_no_item", "L_No_Item"),
+ ("L_no_water", "L_No_Water"),
+ ("L_NOT_ENOUGH", "L_No_Water"),
+ ("L_bye", "L_Bye"),
+ ("L_NOHELP", "L_NoHelp"),
+ ("L_Eyepatch", "L_EyePatch"),
+ ("@PC_STAMINA", "@pc_stamina"),
+ ("L_magic", "L_Magic"),
+ ("L_cont", "L_Cont"),
+ ("L_accept", "L_Accept"),
+ ("L_no_event", "L_No_Event"),
+ ("L_event_done", "L_Event_Done"),
+ ("L_nobeer", "L_NoBeer"),
+ ("L_iron", "L_Iron"),
+ ("L_sulphur", "L_Sulphur"),
+ ("L_red", "L_Red"),
+ ("L_yellow", "L_Yellow"),
+ ("L_green", "L_Green"),
+ ("L_orange", "L_Orange"),
+ ("L_pink", "L_Pink"),
+ ("L_purple", "L_Purple"),
+ ("L_question", "L_Question"),
+ ("L_quest", "L_Quest"),
+ ("L_dead", "L_Dead"),
+ ("L_menu", "L_Menu"),
+ ("L_give", "L_Give"),
+ ("@Items$", "@items$"),
+ ("@menuItems$", "@menuitems$"),
+ ("L_Teach_initial", "L_Teach_Initial"),
+ ("L_finish", "L_Finish"),
+ ("L_No_ash", "L_No_Ash"),
+ ("L_No_water", "L_No_Water"),
+ ("L_cave", "L_Cave"),
+ ("L_farewell", "L_Farewell"),
+ ("@Q_forestbow_", "@Q_Forestbow_"),
+ ("L_game", "L_Game"),
+ ("L_good", "L_Good"),
+ ("L_abort", "L_Abort"),
+ ("@menuID", "@menuid"),
+ ("L_NO_ITEM", "L_No_Item"),
+ ("L_HELP", "L_Help"),
+ ("L_Noitem", "L_NoItem"),
+ ("L_No_fur", "L_No_Fur"),
+ ("@EXP", "@Exp"),
+ ("L_water", "L_Water"),
+ ("L_get", "L_Get"),
+ ("L_happy", "L_Happy"),
+ ("L_cheat", "L_Cheat"),
+ ("@Reward_EXP", "@Reward_Exp"),
+ ("@REWARD_EXP", "@Reward_Exp"),
+ ("L_no_money", "L_No_Money"),
+ ("@MinLevel", "@minLevel"),
+ ("L_return", "L_Return"),
+ ("L_intro", "L_Intro"),
+ ("L_full", "L_Full"),
+ ("@minlevel", "@minLevel"),
+ ("@MinLevel", "@minLevel"),
+ ("L_Cleanup", "L_CleanUp"),
+ ("L_Alreadystarted", "L_AlreadyStarted"),
+ ("@amount", "@Amount"),
+ ("L_again", "L_Again"),
+ ("L_no_potion", "L_No_Potion"),
+ ("L_wizard_hat", "L_Wizard_Hat"),
+ ("L_notenough", "L_NotEnough"),
+ ("L_offer", "L_Offer"),
+ ("L_giveup", "L_GiveUp"),
+ ("L_not_ready", "L_Not_Ready"),
+ ("@MobID", "@mobId"),
+ ("@mobID", "@mobId"),
+ ("L_naked", "L_Naked"),
+ ("L_shortcut", "L_Shortcut"),
+ ("L_shirt", "L_Shirt"),
+ ("L_goodjob", "L_GoodJob"),
+ ("L_kill", "L_Kill"),
+ ("L_nothing", "L_Nothing"),
+ ("L_lowlevel", "L_LowLevel"),
+ ("@mask", "@Mask"),
+ ("Foice", "FoiceItem"),
+ ("LanternaJack", "LanternaJackItem"),
+ # fix at same time usage with same name function and variable
+ ("\"DailyQuestPoints\"", "\"DailyQuestPointsFunc\""),
+ ("sc_adrenaline", "SC_ADRENALINE"),
+ ];
+
+ for val in vals:
+ line = line.replace(val[0], val[1]);
+ return line
+
+def convertItemDb(isNew):
+ srcDir = "oldserverdata/world/map/db/"
+ dstFile = "newserverdata/db/re/item_db.conf"
+ if os.path.isfile(dstFile):
+ os.remove(dstFile)
+ constsFile = "newserverdata/db/const.txt"
+ if os.path.isfile(constsFile):
+ os.remove(constsFile)
+ fieldsSplit = re.compile(",")
+ scriptsSplit = re.compile("},")
+ scriptsTextClean = re.compile('([{}])')
+ scriptsTextComma = re.compile('^,')
+ scriptsTextColon = re.compile('; ')
+ items = dict()
+
+ tpl = readFile("templates/item_db.tpl")
+ with open(dstFile, "w") as w:
+ w.write(tpl)
+ with open(constsFile, "a") as c:
+ c.write("// items\n");
+ for srcFile in getItemDbFile(srcDir):
+ with open(srcDir + srcFile, "r") as r:
+ for line in r:
+ if len(line) < 2 or line[0] == "#" or line[0:2] == "//":
+ continue
+ line = replaceStr(line)
+ rows = fieldsSplit.split(line)
+ if len(rows) < 5 or rows[0] == "0":
+ continue
+
+ sz = len(rows)
+ if sz > 19:
+ sz = 19
+ for f in xrange(0, sz):
+ rows[f] = rows[f].strip()
+
+ items[rows[1]] = {'id':rows[0],'buy':rows[4],'name':rows[1]}
+ items[rows[0]] = {'id':rows[0],'buy':rows[4],'name':rows[1]}
+ items[int(rows[0])] = {'id':rows[0],'buy':rows[4],'name':rows[1]}
+ # set all values then write
+ w.write("{\n")
+ c.write("{0}\t{1}\n".format(rows[1], rows[0]))
+ writeIntField(w, "Id", rows[0])
+ writeStrField(w, "AegisName", rows[1])
+ if isNew == True:
+ offset = -1
+ else:
+ offset = 0
+ writeStrField(w, "Name", rows[offset + 2])
+ if rows[offset + 3] == "0":
+ itemType = "2"
+ else:
+ itemType = rows[offset + 3]
+ writeIntField(w, "Type", itemType)
+
+ writeIntField(w, "Buy", rows[offset + 4])
+ if int(rows[offset + 4])*.75 <= int(rows[offset + 5])*1.24:
+ writeIntField(w, "Sell", str(int(rows[offset + 4])*.75))
+ else:
+ writeIntField(w, "Sell", rows[offset + 5])
+ writeIntField(w, "Weight", rows[offset + 6])
+ writeIntField(w, "Atk", rows[offset + 7])
+ writeIntField(w, "Matk", "0")
+ writeIntField(w, "Def", rows[offset + 8])
+ writeIntField(w, "Range", rows[offset + 9])
+ writeIntField(w, "Slots", "0")
+ writeIntField(w, "Job", "0xFFFFFFFF")
+ writeIntField(w, "Upper", "0x3F")
+ writeIntField(w, "Gender", rows[offset + 12])
+ writeIntField(w, "Loc", rows[offset + 13])
+ writeIntField(w, "WeaponLv", rows[offset + 14])
+ writeIntField(w, "EquipLv", rows[offset + 15])
+ writeIntField(w, "Refine", "false")
+ if isNew == True:
+ offset = 2
+ else:
+ offset = 0
+ if rows[offset + 13] == "2":
+ writeIntField(w, "View", "1")
+ elif rows[offset + 13] == "34":
+ writeIntField(w, "View", "11")
+ elif rows[offset + 13] == "32768":
+ writeIntField(w, "View", "1")
+ elif itemType == "4": # weapon
+ writeIntField(w, "View", "1")
+ else:
+ writeIntField(w, "View", rows[0])
+ writeIntField(w, "BindOnEquip", "false")
+ writeIntField(w, "BuyingStore", "false")
+ writeIntField(w, "Delay", "0")
+ writeIntField(w, "Sprite", "0")
+
+ scripts = ""
+ if isNew == True:
+ offset = -1
+ else:
+ offset = 0
+ for f in xrange(offset + 17, len(rows)):
+ scripts = scripts + ", " + rows[f]
+ rows = scriptsSplit.split(scripts)
+ # Needs .split(';') and \n each
+ if len(rows) > 1:
+ UseScript = scriptsTextColon.sub(';',scriptsTextComma.sub('', scriptsTextClean.sub('', rows[0]))).strip().split(';')
+ EquipScript = scriptsTextColon.sub(';',scriptsTextComma.sub('', scriptsTextClean.sub('', rows[1]))).strip().split(';')
+ else:
+ UseScript = ""
+ EquipScript = ""
+ # move to for stmt
+ if len(UseScript) > 1:
+ writeStartScript(w, "Script")
+ for uline in UseScript:
+ if len(uline) > 0:
+ w.write(" {0};\n".format(uline))
+ writeEndScript(w)
+ if len(EquipScript) > 1:
+ writeStartScript(w, "OnEquipScript")
+ for eline in EquipScript:
+ if len(eline) > 0:
+ w.write(" {0};\n".format(eline))
+ writeEndScript(w)
+
+ w.write("},\n")
+ w.write(")\n")
+ return items
diff --git a/hercules/code/server/tmw/main.py b/hercules/code/server/tmw/main.py
new file mode 100644
index 0000000..bfbf5ef
--- /dev/null
+++ b/hercules/code/server/tmw/main.py
@@ -0,0 +1,49 @@
+#! /usr/bin/env python
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+from sets import Set
+
+from code.server.account import convertAccount
+from code.server.accreg import convertAccReg
+from code.server.party import convertParty
+from code.server.storage import convertStorage
+from code.server.db.char import saveCharTableCustom
+from code.server.db.charregnumdb import saveCharRegNumDbTable
+from code.server.db.inventory import saveInventoryTable
+from code.server.db.skill import saveSkillTable
+from code.server.tmw.athena import readAthena
+from code.server.tmw.consts import convertConsts
+from code.server.tmw.itemdb import convertItemDb
+from code.server.tmw.mobdb import convertMobDb
+from code.server.tmw.mobskilldb import convertMobSkillDb
+from code.server.tmw.npcs import createMainScript, convertNpcs
+from code.server.utils import cleanServerData
+
+def serverTmwMain(isNew):
+ try:
+ cleanServerData()
+ except:
+ print "Updating server"
+ createMainScript()
+ items = convertItemDb(isNew)
+ npcIds = Set()
+ convertNpcs(items, npcIds)
+ convertMobDb(items)
+ quests = dict()
+ convertConsts(quests, npcIds)
+ convertMobSkillDb()
+
+def dbTmwMain():
+ convertAccount()
+ users = readAthena()
+# saveCharTable(users)
+ saveCharTableCustom(users)
+ saveCharRegNumDbTable(users)
+ saveSkillTable(users)
+ saveInventoryTable(users)
+ convertStorage()
+ convertAccReg()
+ convertParty(users)
diff --git a/hercules/code/server/tmw/mobdb.py b/hercules/code/server/tmw/mobdb.py
new file mode 100644
index 0000000..04d1fce
--- /dev/null
+++ b/hercules/code/server/tmw/mobdb.py
@@ -0,0 +1,170 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import math
+import os
+import re
+
+from code.configutils import isHaveData, writeCondField2, writeStartBlock, writeEndBlock, writeIntField, writeStrField, writeFieldArr, writeIntField2, writeFieldList, writeSubField
+from code.fileutils import readFile
+
+def getMobDbFile(srcDir):
+ files = os.listdir(srcDir)
+ for srcFile in files:
+ if srcFile.find("mob_db") >= 0:
+ yield srcFile
+
+def replaceStr(line):
+ vals = [
+ ("lanternaJack", "LanternaJack"),
+ ("foice", "Foice"),
+ ("BlueFairy", "BlueFairyMob"),
+ ("RedFairy", "RedFairyMob"),
+ ("GreenFairy", "GreenFairyMob"),
+ ("Scorpion", "ScorpionMob"),
+ ("Tritan", "TritanMob"),
+ ("Ukar", "UkarMob"),
+ ];
+
+ for val in vals:
+ line = line.replace(val[0], val[1]);
+ return line
+
+def convertMobDb(items):
+ srcDir = "oldserverdata/world/map/db/"
+ dstFile = "newserverdata/db/re/mob_db.conf"
+ fieldsSplit = re.compile(",")
+ tpl = readFile("templates/mob_db.tpl")
+ with open(dstFile, "w") as w:
+ w.write(tpl)
+ for srcFile in getMobDbFile(srcDir):
+ with open(srcDir + srcFile, "r") as r:
+ for line in r:
+ if len(line) < 2 or line[:2] == "//" or line[:1] == "#":
+ w.write(line)
+ continue
+ line = replaceStr(line)
+ rows = fieldsSplit.split(line)
+ for f in xrange(0, len(rows)):
+ rows[f] = rows[f].strip()
+ try:
+ val = int(rows[23])
+ if val < 20:
+ rows[23] = "20"
+ except:
+ None
+
+ # Experience and Job experience, following *tmw-eathena*/src/map/mob.c
+ calc_exp = 0
+
+ if rows[6] == "0":
+ if int(rows[4]) <= 1:
+ calc_exp = 1
+
+ mod_def = 100 - int(rows[11])
+
+ if mod_def == 0:
+ mod_def = 1
+
+ effective_hp = ((50 - int(rows[18])) * int(rows[4]) / 50) + (2 * int(rows[18]) * int(rows[4]) / mod_def)
+ attack_factor = (int(rows[9]) + int(rows[10]) + int(rows[13]) / 3 + int(rows[17]) / 2 + int(rows[18])) * (1872 / int(rows[26])) / 4
+ dodge_factor = (int(rows[3]) + int(rows[14]) + int(rows[18]) / 2)**(4 / 3)
+ persuit_factor = (3 + int(rows[8])) * (int(rows[24]) % 2) * 1000 / int(rows[25])
+ aggression_factor = 1
+
+ if False:
+ aggression_factor = 10 / 9
+
+ base_exp_rate = 100 # From *tmw-eathena-data*/conf/battle_athena.conf
+
+ calc_exp = int(math.floor(effective_hp * (math.sqrt(attack_factor) + math.sqrt(dodge_factor) + math.sqrt(persuit_factor) + 55)**3 * aggression_factor / 2000000 * base_exp_rate / 100))
+
+ if calc_exp < 1:
+ calc_exp = 1
+ else:
+ calc_exp = rows[6]
+
+ w.write("{\n")
+ writeIntField(w, "Id", rows[0])
+ writeStrField(w, "SpriteName", rows[1])
+ writeStrField(w, "Name", rows[2])
+ writeIntField(w, "Lv", rows[3])
+ writeIntField(w, "Hp", rows[4])
+ writeIntField(w, "Sp", rows[5])
+ writeIntField(w, "Exp", calc_exp)
+ writeIntField(w, "JExp", rows[7])
+ writeIntField(w, "AttackRange", rows[8])
+ writeFieldArr(w, "Attack", rows[9], rows[10])
+ writeIntField(w, "Def", rows[11])
+ writeIntField(w, "Mdef", rows[12])
+ writeStartBlock(w, "Stats")
+ writeIntField2(w, "Str", rows[13])
+ writeIntField2(w, "Agi", rows[14])
+ writeIntField2(w, "Vit", rows[15])
+ writeIntField2(w, "Int", rows[16])
+ writeIntField2(w, "Dex", rows[17])
+ writeIntField2(w, "Luk", rows[18])
+ writeEndBlock(w)
+ writeIntField(w, "ViewRange", rows[19])
+ writeIntField(w, "ChaseRange", 10)
+ writeIntField(w, "Size", rows[21])
+ writeIntField(w, "Race", rows[22])
+ writeFieldList(w, "Element", int(rows[23]) % 10, int(rows[23]) / 20)
+ mode = int(rows[24], 0)
+ if mode != 0:
+ writeStartBlock(w, "Mode")
+ writeCondField2(w, mode & 0x0001, "CanMove")
+ writeCondField2(w, mode & 0x0002, "Looter")
+ writeCondField2(w, mode & 0x0004, "Aggressive")
+ writeCondField2(w, mode & 0x0008, "Assist")
+ writeCondField2(w, mode & 0x0010, "CastSensorIdle")
+ writeCondField2(w, mode & 0x0020, "Boss")
+ writeCondField2(w, mode & 0x0040, "Plant")
+ writeCondField2(w, mode & 0x0080, "CanAttack")
+ writeCondField2(w, mode & 0x0100, "Detector")
+ writeCondField2(w, mode & 0x0200, "CastSensorChase")
+ writeCondField2(w, mode & 0x0400, "ChangeChase")
+ writeCondField2(w, mode & 0x0800, "Angry")
+ writeCondField2(w, mode & 0x1000, "ChangeTargetMelee")
+ writeCondField2(w, mode & 0x2000, "ChangeTargetChase")
+ writeCondField2(w, mode & 0x4000, "TargetWeak")
+ writeCondField2(w, mode & 0x8000, "LiveWithoutMaster")
+ writeEndBlock(w)
+ writeIntField(w, "MoveSpeed", rows[25])
+ writeIntField(w, "AttackDelay", rows[26])
+ writeIntField(w, "AttackMotion", rows[27])
+ writeIntField(w, "DamageMotion", rows[28])
+ writeIntField(w, "MvpExp", rows[45])
+
+ if isHaveData(rows, 47, 3):
+ writeStartBlock(w, "MvpDrops")
+ for f in range(0, 3):
+ value = rows[47 + f * 2]
+ chance = rows[47 + f * 2]
+ if value == "" or value == "0" or chance == "" or chance == "0":
+ continue
+ value = int(value)
+ if value not in items:
+ w.write("// Error: mvp drop with id {0} not found in item db\n".format(value))
+ print("Error: mvp drop with id {0} not found in item db".format(value))
+ else:
+ writeSubField(w, items[value]["name"], chance)
+ writeEndBlock(w)
+ if isHaveData(rows, 29, 10):
+ writeStartBlock(w, "Drops")
+ for f in range(0, 10):
+ value = rows[29 + f * 2]
+ chance = rows[30 + f * 2]
+ if value == "" or value == "0" or chance == "" or chance == "0":
+ continue
+ value = int(value)
+ if value not in items:
+ w.write("// Error: drop with id {0} not found in item db\n".format(value))
+ print("Error: drop with id {0} not found in item db".format(value))
+ else:
+ writeSubField(w, items[value]["name"], chance)
+ writeEndBlock(w)
+ w.write("},\n")
+ w.write(")\n")
diff --git a/hercules/code/server/tmw/mobskilldb.py b/hercules/code/server/tmw/mobskilldb.py
new file mode 100644
index 0000000..7188824
--- /dev/null
+++ b/hercules/code/server/tmw/mobskilldb.py
@@ -0,0 +1,55 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.fileutils import readFile
+
+def convertMobSkillDb():
+ srcFile = "oldserverdata/world/map/db/mob_skill_db.txt"
+ dstFile = "newserverdata/db/re/mob_skill_db.txt"
+ fieldsSplit = re.compile(",")
+ notintown = re.compile("notintown")
+ notintownSub = re.compile("notintown,0")
+ with open(srcFile, "r") as r:
+ with open(dstFile, "w") as w:
+ tpl = readFile("templates/mob_skill_db.tpl")
+ w.write(tpl)
+ for line in r:
+ if notintown.search(line):
+ if line[0:2] == "//":
+ line = ''
+ line = notintownSub.sub("myhpltmaxrate,20",line)
+ if len(line) < 2 or line[0:2] == "//":
+ w.write(line)
+ continue
+ rows = fieldsSplit.split(line)
+ if len(rows) < 10:
+ w.write(line)
+ continue
+
+ for f in xrange(0, len(rows)):
+ rows[f] = rows[f].strip()
+
+ w.write("{0},{1},{2},{3},{4},{5},{6},"
+ "{7},{8},{9},{10},{11},{12},{13},"
+ "{14},,,,\n".format(
+ rows[0],
+ rows[1],
+ rows[2],
+ rows[3],
+ rows[4],
+ rows[5],
+ rows[6],
+ rows[7],
+ rows[8],
+ rows[9],
+ rows[10],
+ rows[11],
+ rows[12],
+ rows[13],
+ rows[14]
+ ))
+
diff --git a/hercules/code/server/tmw/npcs.py b/hercules/code/server/tmw/npcs.py
new file mode 100644
index 0000000..c51da78
--- /dev/null
+++ b/hercules/code/server/tmw/npcs.py
@@ -0,0 +1,878 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import os
+import re
+
+from code.fileutils import makeDir
+from code.stringutils import stripWindows, stripNewLine
+
+mapsConfFile = "newserverdata/conf/maps.conf"
+if os.path.isfile(mapsConfFile):
+ os.remove(mapsConfFile)
+mapsIndexFile = "newserverdata/db/map_index.txt"
+if os.path.isfile(mapsIndexFile):
+ os.remove(mapsIndexFile)
+npcMainScript = "newserverdata/npc/re/scripts_main.conf"
+mapsIndex = 1
+scriptRe = re.compile("^(((?P<map>[^/](.+)),([ ]*)(?P<x>[\d]+),([ ]*)(?P<y>[\d]+),([ ]*)(?P<dir>[\d]+))|(?P<function>function)|-)" +
+ "[|](?P<tag>script)[|](?P<name>[^|]+)([|]"
+ "(?P<class>[\d-]+)((,((?P<xs>[\d]+),(?P<ys>[\d]+)))|)|)$")
+
+scriptRe2 = re.compile("^(((?P<map>[^/](.+))[.]gat,([ ]*)(?P<x>[\d]+),([ ]*)(?P<y>[\d]+),([ ]*)(?P<dir>[\d]+))|(?P<function>function)|-)" +
+ "[\t](?P<tag>script)[\t](?P<name>[\w#'\\[\\]_ äü.-]+)[\t]"
+ "(((?P<class>[\d-]+)((,((?P<xs>[\d-]+),(?P<ys>[\d-]+)))|)(|,)(|[ \t]))|){(|[ ])$")
+
+shopRe = re.compile("^(?P<map>[^/](.+)),([ ]*)(?P<x>[\d]+),([ ]*)(?P<y>[\d]+),([ ]*)(?P<dir>[\d]+)(|,(?P<gender>[\d]+))" +
+ "[|](?P<tag>shop)[|](?P<name>[^|]+)[|]"
+ "(?P<class>[\d-]+),(?P<items>(.+))$")
+
+shopRe2 = re.compile("^(?P<map>[^/](.+))[.]gat,([ ]*)(?P<x>[\d]+),([ ]*)(?P<y>[\d]+),([ ]*)(?P<dir>[\d]+)" +
+ "[\t](?P<tag>shop)[\t](?P<name>[^\t]+)[\t]"
+ "(?P<class>[\d]+),(?P<items>(.+))$")
+
+mapFlagRe = re.compile("^(?P<map>[^/](.+))" +
+ "[|](?P<tag>mapflag)[|](?P<name>[\w#']+)(|[|](?P<flag>.*))$")
+
+mapFlagRe2 = re.compile("^(?P<map>[^/](.+))[.]gat" +
+ "[ ](?P<tag>mapflag)[ ](?P<name>[\w#']+)(|[ ](?P<flag>.*))$")
+
+warpRe = re.compile("^(?P<map>[^/](.+)),([ ]*)(?P<x>[\d]+),([ ]*)(?P<y>[\d]+)[|]"
+ "(?P<tag>warp)[|](?P<name>[^|]+)[|](?P<xs>[\d-]+),(?P<ys>[\d-]+),(?P<targetmap>[^/](.+)),([ ]*)(?P<targetx>[\d]+),([ ]*)(?P<targety>[\d]+)$")
+warpRe2 = re.compile("^(?P<map>[^/](.+))[.]gat,([ ]*)(?P<x>[\d]+),([ ]*)(?P<y>[\d]+)([\t]+)"
+ "(?P<tag>warp)[\t](?P<name>[^\t]+)([\t]+)(?P<xs>[\d-]+),(?P<ys>[\d-]+),(?P<targetmap>[^/](.+))[.]gat,([ ]*)(?P<targetx>[\d]+),([ ]*)(?P<targety>[\d]+)$")
+warpRe3 = re.compile("^(?P<map>[^/](.+)),([ ]*)(?P<x>[\d]+),([ ]*)(?P<y>[\d]+)[|]"
+ "(?P<tag>warp)[|](?P<xs>[\d-]+),(?P<ys>[\d-]+),(?P<targetmap>[^/](.+)),([ ]*)(?P<targetx>[\d]+),([ ]*)(?P<targety>[\d]+)$")
+
+monsterRe = re.compile("^(?P<map>[^/](.+)),([ ]*)(?P<x>[\d]+),([ ]*)(?P<y>[\d]+),([ ]*)(?P<xs>[\d-]+),(?P<ys>[\d-]+)[|]"
+ "(?P<tag>monster)[|](?P<name>[^|]+)[|]"
+ "(?P<class>[\d]+),(?P<num>[\d]+),(?P<delay1>[\d]+)ms,(?P<delay2>[\d]+)ms(|,(?P<label>[\w+-:#]+))$")
+
+monsterRe2 = re.compile("^(?P<map>[^/](.+))[.]gat,([ ]*)(?P<x>[\d]+),([ ]*)(?P<y>[\d]+),([ ]*)(?P<xs>[\d-]+),(?P<ys>[\d-]+)\t"
+ "(?P<tag>monster)[\t](?P<name>[\w#' ]+)([\t]+)"
+ "(?P<class>[\d]+),(?P<num>[\d]+),(?P<delay1>[\d]+),(?P<delay2>[\d]+)(|,(?P<label>[\w+-:#]+))$")
+
+setRe = re.compile("^(?P<space>[ ]+)set[ ](?P<var>[^,]+),([ ]*)(?P<val>[^;]+);$");
+
+class ScriptTracker:
+ pass
+
+def createMainScript():
+ with open(npcMainScript, "w") as w:
+ w.write("npc: npc/functions/main.txt\n")
+ w.write("import: npc/scripts.conf\n")
+
+def convertNpcs(items, npcIds):
+ processNpcDir("oldserverdata/world/map/npc/", "newserverdata/npc/", items, npcIds)
+
+def processNpcDir(srcDir, dstDir, items, npcIds):
+ makeDir(dstDir)
+ files = os.listdir(srcDir)
+ for file1 in files:
+ if file1[0] == '.':
+ continue
+ srcPath = os.path.abspath(srcDir + os.path.sep + file1)
+ dstPath = os.path.abspath(dstDir + os.path.sep + file1)
+ if not os.path.isfile(srcPath):
+ processNpcDir(srcPath, dstPath, items, npcIds)
+ else:
+ if file1[-5:] == ".conf" or file1[-4:] == ".txt":
+ processNpcFile(srcPath, dstPath, items, npcIds)
+
+def processNpcFile(srcFile, dstFile, items, npcIds):
+# print "file: " + srcFile
+ tracker = ScriptTracker()
+ tracker.insideScript = False
+ tracker.items = items
+ tracker.npcIds = npcIds
+ with open(srcFile, "r") as r:
+ with open(dstFile, "w") as w:
+ tracker.w = w
+ for line in r:
+ line = stripWindows(line)
+ tracker.line = line
+ res = convertTextLine(tracker)
+ if res:
+ w.write(tracker.line)
+
+def convertTextLine(tracker):
+ line = tracker.line
+ if line[:5] == "map: ":
+ processScriptMapLine(line)
+ return False
+
+ if len(line) >= 2 and line[0:2] == "//":
+ return False
+
+ if line == "};\n":
+ tracker.w.write("}\n");
+ return False
+
+ idx = line.find("|script|")
+ if idx <= 0:
+ idx = line.find("\tscript\t")
+ if idx >= 0:
+ processScript(tracker)
+ return False
+ idx = line.find("|shop|")
+ if idx <= 0:
+ idx = line.find("\tshop\t")
+ if idx >= 0:
+ processShop(tracker)
+ return False
+ idx = line.find("|monster|")
+ if idx <= 0:
+ idx = line.find("\tmonster\t")
+ if idx >= 0:
+ processMonster(tracker)
+ return False
+ idx = line.find("|warp|")
+ if idx <= 0:
+ idx = line.find("\twarp\t")
+ if idx >= 0:
+ processWarp(tracker)
+ return False
+ idx = line.find("|mapflag|")
+ if idx <= 0:
+ idx = line.find(".gat mapflag ")
+ if idx >= 0:
+ processMapFlag(tracker)
+ return False
+ processStrReplace(tracker)
+ return False
+
+def processScriptMapLine(line):
+ global mapsIndex
+ line = stripNewLine(line)
+ if line[-4:] == ".gat":
+ line = line[:-4]
+ with open(mapsConfFile, "a") as w:
+ w.write(line + "\n")
+
+ with open(mapsIndexFile, "a") as w:
+ w.write("{0} {1}\n".format(line[5:], mapsIndex))
+ mapsIndex = mapsIndex + 1
+
+def writeScript(w, m, npcIds):
+ try:
+ if m.group("function") != None:
+ isFunction = True
+ else:
+ isFunction = False
+ except:
+ isFunction = False
+
+ if isFunction:
+ w.write("function");
+ elif m.group("x") == None or (m.group("x") == 0 and m.group("y") == 0): # float npc
+ w.write("-")
+ else:
+ w.write("{0},{1},{2},{3}".format(m.group("map"), m.group("x"), m.group("y"), m.group("dir")))
+ if isFunction:
+ funcName = m.group("name")
+ if funcName == "DailyQuestPoints":
+ funcName = "DailyQuestPointsFunc"
+ w.write("\t{0}\t{1}\t".format(m.group("tag"), funcName));
+ else:
+ class_ = m.group("class")
+ if class_ == "0": # hidden npc
+ class_ = 32767
+ elif class_ == None:
+ class_ = -1;
+ else:
+ class_ = int(class_)
+# if class_ > 125 and class_ <= 400:
+# class_ = class_ + 100
+ npcIds.add(int(class_))
+ if class_ == -1:
+ class_ = "MINUS1"
+ w.write("\t{0}\t{1}\tNPC{2}".format(m.group("tag"), m.group("name"), class_));
+
+def processScript(tracker):
+ line = tracker.line[:-1]
+ w = tracker.w
+
+ m = scriptRe.search(line)
+ if m == None:
+ m = scriptRe2.search(line)
+ if m == None:
+ print "error in parsing: " + line
+ w.write("!!!error parsing line")
+ w.write(line)
+ return
+# print "source=" + line
+# print "map={0} x={1} y={2} dir={3} tag={4} name={5} class={6}, xs={7}, ys={8}".format(
+# m.group("map"), m.group("x"), m.group("y"), m.group("dir"),
+# m.group("tag"), m.group("name"), m.group("class"), m.group("xs"), m.group("ys"))
+ # debug
+
+ try:
+ if m.group("function") != None:
+ isFunction = True
+ else:
+ isFunction = False
+ except:
+ isFunction = False
+
+ writeScript(w, m, tracker.npcIds)
+
+ if m.group("xs") != None:
+ w.write(",{0},{1}".format(m.group("xs"), m.group("ys")));
+
+ if isFunction == False:
+ w.write(",{\n");
+ else:
+ w.write("{\n");
+
+def itemsToShop(tracker, itemsStr):
+ itemsSplit = re.compile(",")
+ itemsSplit2 = re.compile(":")
+ itemsDict = tracker.items
+ outStr = ""
+ items = itemsSplit.split(itemsStr)
+ for str2 in items:
+ parts = itemsSplit2.split(str2)
+ if len(parts) != 2:
+ print "Wrong shop item name {0}: {1}".format(str2, itemsStr)
+ continue
+ if parts[1][0] == "*":
+ parts[1] = str((int(parts[1][1:]) * int(itemsDict[parts[0].strip()]['buy'])))
+ if outStr != "":
+ outStr = outStr + ","
+ itemName = parts[0].strip()
+ if itemName in itemsDict:
+ outStr = outStr + itemsDict[itemName]['id'] + ":" + parts[1]
+ else:
+ print "Wrong item name in shop: {0}".format(itemName)
+ return outStr
+
+def processShop(tracker):
+ line = tracker.line
+ w = tracker.w
+
+ m = shopRe.search(line)
+ if m == None:
+ m = shopRe2.search(line)
+ if m == None:
+ print "error in parsing: " + line
+ w.write("!!!error parsing line")
+ w.write(line)
+ return
+
+# print "source=" + line[:-1]
+# print "map={0} x={1} y={2} dir={3} tag={4} name={5} class={6} items={7}".format(
+# m.group("map"), m.group("x"), m.group("y"), m.group("dir"),
+# m.group("tag"), m.group("name"), m.group("class"), m.group("items"))
+
+ writeScript(w, m, tracker.npcIds)
+ w.write("," + itemsToShop(tracker, m.group("items")) + "\n")
+
+def processMapFlag(tracker):
+ line = tracker.line
+ w = tracker.w
+
+ m = mapFlagRe.search(line)
+ if m == None:
+ m = mapFlagRe2.search(line)
+ if m == None:
+ print "error in parsing: " + line
+ w.write("!!!error parsing line")
+ w.write(line)
+ return
+# print "source=" + line[:-1]
+# print "map={0} tag={1} name={2} flag={3}".format(
+# m.group("map"), m.group("tag"), m.group("name"), m.group("flag"))
+
+ if m.group("name") == "town" or m.group("name") == "resave":
+ w.write("//")
+ w.write("{0}\t{1}\t{2}".format(m.group("map"), m.group("tag"), m.group("name")))
+ if m.group("flag") == None:
+ w.write("\n")
+ else:
+ w.write("\t{0}\n".format(m.group("flag")))
+
+def processWarp(tracker):
+ line = tracker.line
+ w = tracker.w
+ m = warpRe.search(line)
+ noName = False
+ if m == None:
+ m = warpRe2.search(line)
+ if m == None:
+ m = warpRe3.search(line)
+ noName = True
+ if m == None:
+ print "error in parsing: " + line
+ w.write("!!!error parsing line")
+ w.write(line)
+ return
+
+ if noName == True:
+ warpName = "{0}_{1}_{2}".format(m.group("map"), m.group("x"), m.group("y"))
+ else:
+ warpName = m.group("name")
+
+# print "source=" + line[:-1]
+# print "map={0} xy={1},{2} tag={3} name={4} xs={5} ys={6} target: map={7} xy={8},{9}".format(
+# m.group("map"), m.group("x"), m.group("y"), m.group("tag"), warpName, m.group("xs"), m.group("ys"), m.group("targetmap"), m.group("targetx"), m.group("targety"))
+
+ xs = int(m.group("xs"))
+ ys = int(m.group("ys"))
+ if xs < 0:
+ xs = 0
+ if ys < 0:
+ ys = 0
+ w.write("{0},{1},{2},{3}\t{4}\t{5}\t{6},{7},{8},{9},{10}\n".format(
+ m.group("map"), m.group("x"), m.group("y"), "0", m.group("tag"), warpName,
+ xs, ys, m.group("targetmap"), m.group("targetx"), m.group("targety")))
+
+
+def processMonster(tracker):
+ line = tracker.line
+ w = tracker.w
+ m = monsterRe.search(line)
+ if m == None:
+ m = monsterRe2.search(line)
+ if m == None:
+ print "error in parsing: " + line
+ w.write("!!!error parsing line")
+ w.write(line)
+ return
+
+# print "source=" + line[:-1]
+# print ("map={0} xy={1},{2} xs={3} ys={4} tag={5} name={6} class={7} " +
+# "num={8} delays={9},{10}, label={11}").format(
+# m.group("map"), m.group("x"), m.group("y"), m.group("xs"), m.group("ys"),
+# m.group("tag"), m.group("name"), m.group("class"),
+# m.group("num"), m.group("delay1"), m.group("delay2"), m.group("label"))
+
+ if m.group("label") != None:
+ label = "," + m.group("label")
+ else:
+ label = ""
+ w.write("{0},{1},{2},{3},{4}\t{5}\t{6}\t{7},{8},{9},{10}{11}\n".format(m.group("map"),
+ m.group("x"), m.group("y"), m.group("xs"), m.group("ys"),
+ m.group("tag"), m.group("name"),
+ m.group("class"), m.group("num"), m.group("delay1"), m.group("delay2"), label))
+
+
+def processStrReplace(tracker):
+ line = tracker.line
+ w = tracker.w
+ if line == "{\n":
+ return
+ vals = [
+ ("setskill ", "addtoskill "),
+ ("zeny", "Zeny"),
+ ("sc_poison", "SC_POISON"),
+ ("sc_slowpoison", "SC_SLOWPOISON"),
+ ("sex", "Sex"),
+ ("SEX", "Sex"),
+
+ (".gat", ""),
+ ("Bugleg", "BugLeg"),
+ ("set BugLeg, 0;", "//set BugLeg, 0;"),
+ ("set CaveSnakeLamp, 0;", "//set CaveSnakeLamp, 0;"),
+ ("set Class, @BaseClass;", "//set Class, @BaseClass;"),
+ ("goto_Exit;", "goto L_Exit;"),
+ ("if @spants_state < 7 goto", "if(@spants_state < 7) goto"),
+ ("isdead()", "ispcdead()"),
+ ("changeSex", "changecharsex()"),
+ ("getpartnerid2()", "getpartnerid()"),
+ ("getpartnerid2(0)", "getpartnerid()"),
+ ("getgmlevel(0)", "getgmlevel()"),
+ ("if @beer_count > 4", "if (@beer_count > 4)"),
+ ("if !(@Q_status)", "if (!(@Q_status))"),
+ ("if isin(\"021-1\", 130, 120, 140, 125) ", "if (isin(\"021-1\", 130, 120, 140, 125)) "),
+ ("if divorce(0) goto L_", "if (divorce()) goto L_"),
+
+ ("getmap()", "getmapname()"),
+ ("L_end", "L_End"),
+ ("gmcommand", "atcommand"),
+ ("MF_NOSAVE", "mf_nosave"),
+ ("S_update_var", "S_Update_Var"),
+ ("L_teach", "L_Teach"),
+ ("L_focus", "L_Focus"),
+ ("L_unfocus", "L_Unfocus"),
+ ("L_main", "L_Main"),
+ ("L_next", "L_Next"),
+ ("L_close", "L_Close"),
+ ("@NpcName$", "@npcname$"),
+ ("@npcName$", "@npcname$"),
+ ("@cost", "@Cost"),
+ ("@TEMP", "@temp"),
+ ("L_Menuitems", "L_MenuItems"),
+ ("L_no_item", "L_No_Item"),
+ ("L_no_water", "L_No_Water"),
+ ("L_NOT_ENOUGH", "L_No_Water"),
+ ("L_bye", "L_Bye"),
+ ("L_NOHELP", "L_NoHelp"),
+ ("L_Eyepatch", "L_EyePatch"),
+ ("@PC_STAMINA", "@pc_stamina"),
+ ("L_magic", "L_Magic"),
+ ("L_cont", "L_Cont"),
+ ("L_accept", "L_Accept"),
+ ("L_no_event", "L_No_Event"),
+ ("L_event_done", "L_Event_Done"),
+ ("L_nobeer", "L_NoBeer"),
+ ("L_iron", "L_Iron"),
+ ("L_sulphur", "L_Sulphur"),
+ ("L_red", "L_Red"),
+ ("L_yellow", "L_Yellow"),
+ ("L_green", "L_Green"),
+ ("L_orange", "L_Orange"),
+ ("L_pink", "L_Pink"),
+ ("L_purple", "L_Purple"),
+ ("L_question", "L_Question"),
+ ("L_quest", "L_Quest"),
+ ("L_dead", "L_Dead"),
+ ("L_menu", "L_Menu"),
+ ("L_give", "L_Give"),
+ ("@Items$", "@items$"),
+ ("@menuItems$", "@menuitems$"),
+ ("L_Teach_initial", "L_Teach_Initial"),
+ ("L_finish", "L_Finish"),
+ ("L_No_ash", "L_No_Ash"),
+ ("L_no_ash", "L_No_Ash"),
+ ("L_No_water", "L_No_Water"),
+ ("L_cave", "L_Cave"),
+ ("L_farewell", "L_Farewell2"),
+ ("@Q_forestbow_", "@Q_Forestbow_"),
+ ("L_game", "L_Game"),
+ ("L_good", "L_Good"),
+ ("L_abort", "L_Abort"),
+ ("@menuID", "@menuid"),
+ ("L_NO_ITEM", "L_No_Item"),
+ ("L_HELP", "L_Help"),
+ ("L_Noitem", "L_NoItem"),
+ ("L_No_fur", "L_No_Fur"),
+ ("@EXP", "@Exp"),
+ ("L_water", "L_Water"),
+ ("L_get", "L_Get"),
+ ("L_happy", "L_Happy"),
+ ("L_cheat", "L_Cheat"),
+ ("@Reward_EXP", "@Reward_Exp"),
+ ("@REWARD_EXP", "@Reward_Exp"),
+ ("L_no_money", "L_No_Money"),
+ ("@MinLevel", "@minLevel"),
+ ("L_return", "L_Return"),
+ ("L_intro", "L_Intro"),
+ ("L_full", "L_Full"),
+ ("@minlevel", "@minLevel"),
+ ("@MinLevel", "@minLevel"),
+ ("L_Cleanup", "L_CleanUp"),
+ ("L_Alreadystarted", "L_AlreadyStarted"),
+ ("@amount", "@Amount"),
+ ("L_again", "L_Again"),
+ ("L_no_potion", "L_No_Potion"),
+ ("L_wizard_hat", "L_Wizard_Hat"),
+ ("L_notenough", "L_NotEnough"),
+ ("L_offer", "L_Offer"),
+ ("L_giveup", "L_GiveUp"),
+ ("L_not_ready", "L_Not_Ready"),
+ ("@MobID", "@mobId"),
+ ("@mobID", "@mobId"),
+ ("L_naked", "L_Naked"),
+ ("L_shortcut", "L_Shortcut"),
+ ("L_shirt", "L_Shirt"),
+ ("L_goodjob", "L_GoodJob"),
+ ("L_kill", "L_Kill"),
+ ("L_nothing", "L_Nothing"),
+ ("L_lowlevel", "L_LowLevel"),
+ ("L_MENU", "L_Menu"),
+ ("L_potion", "L_Potion"),
+ ("L_fertig", "L_Fertig"),
+ ("L_exit", "L_Exit"),
+ ("L_fight", "L_Fight"),
+ ("L_start", "L_Start"),
+ ("L_unvollst", "L_Unvollst"),
+ ("L_no_room_for_rings", "L_No_Room_For_Rings"),
+ ("@mask", "@Mask"),
+ ("@MAP$", "@map$"),
+ ("baselevel", "BaseLevel"),
+ ("L_Lifestones_Trade_Missing", "L_Lifestones_TM"),
+ ("L_Caretaker_first_reward", "L_Caretaker_fr"),
+ ("L_NohMask_TravelingTroupe", "L_NohMask_TravT"),
+ ("L_listen_to_a_story_first", "L_listen_to_sf"),
+ ("L_post_ironpowder_option", "L_post_ip_op"),
+ ("L_Sulphur_teach_spell_no", "L_Sulphur_tspell"),
+ ("L_Lifestones_MakeSelf_yes", "L_Lifestones_MSy"),
+ ("L_Lifestones_MakeSelf_no", "L_Lifestones_MSn"),
+ ("L_Caretaker_later_rewards", "L_Caretaker_lr"),
+ ("L_boneknife_quest_completed", "L_boneknife_qc"),
+ ("L_boneknife_quest_tooweak", "L_boneknife_qtw"),
+ ("L_tanktop_insufficient_cloth", "L_tanktop_ic"),
+ ("L_dark_green_q_toolittle", "L_dark_greenqtit"),
+ ("L_about_schools_minimenu", "L_about_sch_mm"),
+ ("L_insufficient_BaseLevel", "L_insuf_BL"),
+ ("L_Teach_Initial_nonature", "L_Teach_Ininn"),
+ ("L_Teach_CheckAdvanceTo2_fail", "L_Teach_CATo2f"),
+ ("L_Levelup2_must_practice", "L_Levelup2_mpr"),
+ ("L_Airlia_intro_mana_loss", "L_Airlia_iml"),
+ ("L_knife_quest_completecheck", "L_knife_q_cc"),
+ ("L_Magic_purify_explained", "L_Magic_pur_exp"),
+ ("L_tanktop_insufficient_Zeny", "L_tanktop_ins_Z"),
+ ("L_monster_oil_knows_recipe", "L_monsteroil_kn_r"),
+ ("L_Teach_CheckAdvanceToLOH", "L_TeachChATLOH"),
+ ("L_knife_quest_missing_stingers", "L_knife_qm_sti"),
+ ("L_Magic_train_tree_backgd", "L_Mag_tr_tr_ba"),
+ ("SUB_pick_one_of_many_items", "SUB_pick_1_m_it"),
+ ("L_about_other_prerequisites", "L_a_o_prereq"),
+ ("L_monster_oil_why_dangerous", "L_monstero_w_dang"),
+ ("L_Teach_LOH_advance_abort0", "L_Teach_LOH_a_a0"),
+ ("L_Teach_LOH_advance_abort1", "L_Teach_LOH_a_a1"),
+ ("L_knife_quest_missing_mushrooms", "L_knife_q_m_mushr"),
+ ("L_Magic_train_sagatha_fail", "L_Magic_tsag_f"),
+ ("L_Q_manaseed_touched_short", "L_Q_manas_tou_sh"),
+ ("L_monster_oil_ingredients", "L_monsoil_ingr"),
+ ("L_snakeskins_completecheck", "L_snakes_comc"),
+ ("L_Magic_train_sagatha_lvl1", "L_Mag_tr_sag_lvl1"),
+ ("L_make_mana_potion_missing", "L_make_m_p_mis"),
+ ("L_monster_oil_where_gold", "L_monoil_wg"),
+ ("L_golden_scorpion_wrestle_intro", "L_gold_scor_wr_i"),
+ ("L_component_quest_missing", "L_comp_q_mis"),
+ ("L_monster_oil_start_brew", "L_monsto_st_br"),
+ ("L_golden_requires_knife_quest_done", "L_gold_req_kn_q_d"),
+ ("LL_student_4_wrong_potion", "L_stud_4_wrong_p"),
+ ("L_monster_oil_missing_gold", "L_monsto_mis_g"),
+ ("L_golden_requires_knife_quest", "L_gold_req_kn_q"),
+ ("LL_Magic_skill_insufficient", "L_Mag_sk_insuf"),
+ ("L_monster_oil_out_of_leaves", "L_monso_oo_lea"),
+ ("L_too_lowlevel_for_stinger", "L_too_lol_f_sti"),
+ ("L_monster_oil_leaf_color", "L_monso_le_co"),
+ ("L_golden_scorpion_over_ask", "L_gold_scor_ov_a"),
+ ("S_monster_oil_random_move", "L_monso_rand_mo"),
+ ("L_golden_scorpion_ask_again", "L_gold_scor_as_ag"),
+ ("L_monster_oil_random_0_lighten", "L_monso_rand_0_li"),
+ ("L_golden_scorpion_wrestle_again", "L_gold_scorp_wre_ag"),
+ ("L_monster_oil_explode_dodge", "L_monso_expl_dod"),
+ ("L_golden_scorpion_wrestle", "L_gold_scorp_wre"),
+ ("L_monster_oil_no_gold_end", "L_monso_no_go_e"),
+ ("L_mopox_cure_", "L_moc_"),
+ ("L_mopox_failed_", "L_mof_"),
+ # fix at same time usage with same name function and variable
+ ("\"DailyQuestPoints\"", "\"DailyQuestPointsFunc\""),
+ # TMW-BR errors
+ ("@cerveja", "@Cerveja"),
+ ("@custo", "@Custo"),
+ ("@genero", "@Genero"),
+ ("@gosmaVerme", "@GosmaVerme"),
+ ("@I", "@i"),
+ ("@valor", "@Valor"),
+
+ ("@peloBranco", "@PeloBranco"),
+ ("@pelobranco", "@PeloBranco"),
+ ("@raiz", "@Raiz"),
+ ("@senha", "@Senha"),
+ ("@garrafaVazia", "@GarrafaVazia"),
+ ("@GAMBOGE", "@Gamboge"),
+ ("@FM$", "@fm$"),
+ ("L_abrir", "L_Abrir"),
+ ("L_Abrir", "L_Abrir"),
+ ("L_acabou", "L_Acabou"),
+ ("L_ACABOU", "L_Acabou"),
+ ("L_Aceita", "L_Aceita"),
+ ("L_aceita", "L_Aceita"),
+ ("L_AceitaCapaceteDeMineiro", "L_AceitaCapMineiro"),
+ ("L_AceitaShortDeAlgodao", "L_AceitaShortAlgodao"),
+ ("L_AceitoFlechasDeFerro", "L_AceitaFlechaFerro"),
+ ("L_AceitoFlechasNormais", "L_AceitaFlechaNormal"),
+ ("L_Aceitou", "L_Aceitou"),
+ ("L_aceitou", "L_Aceitou"),
+ ("L_adicionar_registro_AGE", "L_addreg_AGE"),
+ ("L_adicionar_registro_GP", "L_addreg_GP"),
+ ("L_adicionar_registro_LVL", "L_addreg_LVL"),
+ ("L_Ajuda", "L_Ajuda"),
+ ("L_ajuda", "L_Ajuda"),
+ ("L_ajudaComMaisPresentes2", "L_AjudaMaisPresentes2"),
+ ("L_ajudaComMaisPresentes", "L_AjudaMaisPresentes"),
+ ("L_Apresentacao", "L_Apresentacao"),
+ ("L_apresentacao", "L_Apresentacao"),
+ ("L_Arco", "L_Arco"),
+ ("L_arco", "L_Arco"),
+ ("L_Azul", "L_Azul"),
+ ("L_azul", "L_Azul"),
+ ("L_boneknife_quest_completed", "L_BoneKnife_Completo"),
+ ("L_boneknife_quest_tooweak", "L_BoneKnife_Fraco"),
+ ("L_buscar_e_adicionar_AGE", "L_seekadd_AGE"),
+ ("L_buscar_e_adicionar_GP", "L_seekadd_GP"),
+ ("L_buscar_e_adicionar_LVL", "L_seekadd_LVL"),
+ ("L_buscar_e_selecionar_AGE", "L_seeksel_AGE"),
+ ("L_buscar_e_selecionar_GP", "L_seeksel_GP"),
+ ("L_buscar_e_selecionar_LVL", "L_seeksel_LVL"),
+ ("L_Check", "L_Check"),
+ ("L_CHECK", "L_Check"),
+ ("L_cheio", "L_Cheio"),
+ ("L_CHEIO", "L_Cheio"),
+ ("L_Close", "L_close"),
+ ("L_close", "L_close"),
+ ("L_coelhoProcurandoOvos", "L_CoelhoProcuraOvos"),
+ ("L_ComGrana", "L_ComGrana"),
+ ("L_comgrana", "L_ComGrana"),
+ ("L_comprimenta_conhecendo", "L_CumprimentaSabe"),
+ ("L_Concluida", "L_Concluida"),
+ ("L_concluida", "L_Concluida"),
+ ("L_confirmacao_invalida", "L_Inv_Conf"),
+ ("L_Congratulacoes", "L_Congratulacao"),
+ ("L_congratulacoes", "L_Congratulacao"),
+ ("L_Continua", "L_Continua"),
+ ("L_continua", "L_Continua"),
+ ("L_continua_destransformar", "L_Continua_destransf"),
+ ("L_Continua_destransformar", "L_Continua_destransf"),
+ ("L_continuacao", "L_Continuacao"),
+ ("L_Continuacao", "L_Continuacao"),
+ ("L_controlarNacionalidade", "L_ControlaNacao"),
+ ("L_ControlarNacionalidade", "L_ControlaNacao"),
+ ("L_conversa_com_garotas", "L_ConvesaGarota"),
+ ("L_conversa_com_garotos", "L_ConversaGaroto"),
+ ("L_cor", "L_Cor"),
+ ("L_Cor", "L_Cor"),
+ ("L_Curar", "L_Curar"),
+ ("L_curar", "L_Curar"),
+ ("L_dark_green_q_explain2", "L_VerdeEscuro_qexp2"),
+ ("L_dark_green_q_explain", "L_VerdeEscuro_qexp"),
+ ("L_dark_green_q_guess_0", "L_VerdeEscuro_qguess"),
+ ("L_dark_green_q_noslime", "L_VerdeEscuro_noslim"),
+ ("L_dark_green_q_toolittle", "L_VerdeEscuro_pouco"),
+ ("L_dark_green_q_toomuch", "L_VerdeEscuro_muito"),
+ ("L_dep_tudo", "L_Dep_Tudo"),
+ ("L_dep_Tudo", "L_Dep_Tudo"),
+ ("L_DesistenteSelecionado", "L_DesistSel"),
+ ("L_Desistir", "L_Desistir"),
+ ("L_desistir", "L_Desistir"),
+ ("L_desistir_de_assinar2", "L_DesistirAssinar2"),
+ ("L_DICA", "L_Dica"),
+ ("L_Dica", "L_Dica"),
+ ("L_dica", "L_Dica"),
+ ("L_Encontrei", "L_Encontrei"),
+ ("L_encontrei", "L_Encontrei"),
+ ("L_End", "L_End"),
+ ("L_end", "L_end"),
+ ("L_engana_player_novamente", "L_Engana2"),
+ ("L_EQUIP", "L_Equip"),
+ ("L_Equip", "L_Equip"),
+ ("L_ErrouPalavrasMagicas", "L_ErrouMagia2"),
+ ("L_explicacao", "L_Explicacao"),
+ ("L_Explicacao", "L_Explicacao"),
+ ("L_ExplicacaoCausaCrise", "L_ExplicacaoCrise"),
+ ("L_Explicar", "L_Explicar"),
+ ("L_explicar", "L_Explicar"),
+ ("L_fala2", "L_Fala2"),
+ ("L_Fala2", "L_Fala2"),
+ ("L_Fala", "L_Fala"),
+ ("L_fala", "L_Fala"),
+ ("L_falasRestauracaoErro", "L_FalaRestauraErro"),
+ ("L_Falta", "L_Falta"),
+ ("L_falta", "L_Falta"),
+ ("L_falta_ovos_novamente", "L_FaltaOvos2"),
+ ("L_Fechar", "L_Fechar"),
+ ("L_fechar", "L_Fechar"),
+ ("L_Fim", "L_Fim"),
+ ("L_fim", "L_Fim"),
+ ("L_FIM", "L_Fim"),
+ ("L_FimListaDesconectados", "L_FimListaOff"),
+ ("L_Fraco", "L_Fraco"),
+ ("L_fraco", "L_Fraco"),
+ ("L_FRACO", "L_Fraco"),
+ ("L_GanhaPartyParticipar", "L_GanhaPartyP"),
+ ("L_golden_requires_knife_quest", "L_Ouroreq_Knife_q"),
+ ("L_golden_requires_knife_quest_done", "L_Ouroreq_Kinfe_ok"),
+ ("L_golden_scorpion_ask_again", "L_EscorpOuro_again"),
+ ("L_golden_scorpion_over_ask", "L_EscorpOuro_overask"),
+ ("L_golden_scorpion_wrestle", "L_EscorpOuro_Wrest"),
+ ("L_golden_scorpion_wrestle_again", "L_EscorpOuro_Wrest2"),
+ ("L_golden_scorpion_wrestle_intro", "L_EscorpOuro_WerestI"),
+ ("L_HistoriaAdagaBoneClaide", "L_HistoriaAdagaBC"),
+ ("L_Info", "L_Info"),
+ ("L_info", "L_Info"),
+ ("L_iniciaQuest", "L_IniciaQuest"),
+ ("L_IniciaQuest", "L_IniciaQuest"),
+ ("L_Inicio", "L_Inicio"),
+ ("L_inicio", "L_Inicio"),
+ ("L_Inicio_Segunda_Parte", "L_Inicio_P2"),
+ ("L_INSUF", "L_Insuf"),
+ ("L_insuf", "L_Insuf"),
+ ("L_Introfloresta", "L_IntroFloresta"),
+ ("L_introFloresta", "L_IntroFloresta"),
+ ("L_introfloresta", "L_IntroFloresta"),
+ ("L_itens", "L_Itens"),
+ ("L_Itens", "L_Itens"),
+ ("L_jaParticipandoPartida", "L_JaPartPart"),
+ ("L_knife_quest_completecheck", "L_Knife_CompCheck"),
+ ("L_knife_quest_missing_mushrooms", "L_Knife_MissMush"),
+ ("L_knife_quest_missing_mushrooms_2", "L_Knife_MissMush2"),
+ ("L_knife_quest_missing_stingers", "L_Knife_MissSting"),
+ ("L_knife_quest_missing_stingers_2", "L_Knife_MissSting2"),
+ ("L_limpar", "L_Limpar"),
+ ("L_Limpar", "L_Limpar"),
+ ("L_lista", "L_Lista2"),
+ ("L_listen_to_a_story_first", "L_ListenStory"),
+ ("L_longe", "L_Longe"),
+ ("L_LONGE", "L_Longe"),
+ ("L_loop_buscar_desconectados", "L_LoopOff"),
+ ("L_loop_buscar_lutadores2", "L_LoopLutador2"),
+ ("L_loop_buscar_lutadores", "L_LoopLutador"),
+ ("L_main_menu_post_setzer", "L_MM_PostSetzer"),
+ ("L_maisDoces //< pede o dobro de itens e dá o dobro de XP.", "L_MaisDoces //< pede o dobro de itens e dá o dobro de XP."),
+ ("L_Menu", "L_Menu"),
+ ("L_menu", "L_Menu"),
+ ("L_menuGM", "L_MenuGM"),
+ ("L_menugm", "L_MenuGM"),
+ ("L_MenuGM", "L_MenuGM"),
+ ("L_Menugm", "L_MenuGM"),
+ ("L_MenuItens", "L_MenuItens"),
+ ("L_menuItens", "L_MenuItens"),
+ ("L_missaoCogumeloCompleta", "L_MissCog_Ok"),
+ ("L_missaoCordaPescador1", "L_MissCorda_1"),
+ ("L_missaoCordaPescador2", "L_MissCorda_2"),
+ ("L_missaoEscudoMadeira1", "L_MissEscudMad_1"),
+ ("L_missaoEscudoMadeira2", "L_MissEscudMad_2"),
+ ("L_missaoMadeiraResistente", "L_MissMadeiraR"),
+ ("L_missaoMadeiraResistenteCompleta", "L_MissMadeiraR_Ok"),
+ ("L_missaoMaisPresentesCompleta", "L_MissMaisPresenteOK"),
+ ("L_missaoPocaoQueimadura", "L_MissPotQueim"),
+ ("L_missaoPresentesCompleta", "L_MissPresenteOk"),
+ ("L_MOBS_queimaduraEscorpiao", "L_MOB_QueimEscorp"),
+ ("L_nada", "L_Nada2"),
+ ("L_nao", "L_Nao2"),
+ ("L_naoPode", "L_NaoPode"),
+ ("L_naoSei", "L_NaoSei"),
+ ("L_naotenho", "L_NaoTenho"),
+ ("L_Naotenho", "L_NaoTenho"),
+ ("L_naoTrouxe", "L_NaoTrouxe"),
+ ("L_naotrouxe", "L_NaoTrouxe"),
+ ("L_Naotrouxe", "L_NaoTrouxe"),
+ ("L_novaPartida", "L_NovaPartida2"),
+ ("L_NovaPartida", "L_NovaPartida"),
+ ("L_novorecordindividual", "L_NovoRecordSeu"),
+ ("L_obrigado", "L_Obrigado2"),
+ ("L_obsidian_spork_intro", "L_ObsidianSporkIntro"),
+ ("L_OK", "L_Ok2"),
+ ("L_ok", "L_Ok3"),
+ ("L_onde", "L_Onde"),
+ ("L_opcaoFinalizarPartida", "L_opFimPart"),
+ ("L_opcaoIniciaPartidaErro", "L_opIniPartErro"),
+ ("L_opcaoParticipaJogoErro", "L_opPartJogoErro"),
+ ("L_Participante_Invalido", "L_Partc_Invalido"),
+ ("L_Participante_Perdedor", "L_Partc_Perde"),
+ ("L_Participante_Vencedor", "L_partc_Vence"),
+ ("L_pobre", "L_Pobre"),
+ ("L_porque", "L_Porque"),
+ ("L_possuiFragmento", "L_PossuiFragmento"),
+ ("L_preciso", "L_Preciso"),
+ ("L_PrecoPartyParticipar", "L_PrecoPartyPart"),
+ ("L_presente", "L_Presente"),
+ ("L_PrometeVoltarComGRana", "L_VoltoComGp"),
+ ("L_pronto", "L_Pronto"),
+ ("L_quem", "L_Quem"),
+ ("L_QueroArcoDeCurtoAlcance", "L_ArcoCurto"),
+ ("L_ReexplicaApostaParaCobrir", "L_ExpApostaCobrir"),
+ ("L_ReexplicaApostaParaIniciar", "L_ExpApostaIniciar"),
+ ("L_ret_tudo", "L_Ret_Tudo"),
+ ("L_ret_Tudo", "L_Ret_Tudo"),
+ ("L_retirar", "L_Retirar"),
+ ("L_retorno", "L_Retorno"),
+ ("L_retorno_Doces_Escondidos", "L_Retorno_DoceHid"),
+ ("L_Retorno_Segunda_Parte", "L_Retorno_Part2"),
+ ("L_return", "L_Return"),
+ ("L_rever", "L_Rever"),
+ ("L_rico", "L_Rico"),
+ ("L_saia", "L_Saia"),
+ ("L_salvar", "L_Salvar"),
+ ("L_Segunda_Parte_MaisPirulitos", "L_P2_MaisPirulito"),
+ ("L_Segunda_Parte_NaoPirulito", "L_P2_NaoPirulito"),
+ ("L_Segunda_Parte_Pirulito", "L_P2_Pirulito"),
+ ("L_Segunda_Parte_Satisfeito", "L_P2_Satisfeito"),
+ ("L_selecionar_registro_AGE", "L_selreg_AGE"),
+ ("L_selecionar_registro_GP", "L_selreg_GP"),
+ ("L_selecionar_registro_LVL", "L_selreg_LVL"),
+ ("L_semEspaco", "L_SemEspaco"),
+ ("L_semGrana", "L_SemGrana"),
+ ("L_semgrana", "L_SemGrana"),
+ ("L_semItem", "L_SemItem"),
+ ("L_semItens", "L_SemItens"),
+ ("L_semLevel", "L_SemLevel"),
+ ("L_semlevel", "L_SemLevel"),
+ ("L_semlugar", "L_SemLugar"),
+ ("L_semLugar", "L_SemLugar"),
+ ("L_semLvl", "L_SemLvl"),
+ ("L_semLVL", "L_SemLvl"),
+ ("L_SemLVL", "L_SemLvl"),
+ ("L_set", "L_Set"),
+ ("L_SET", "L_Set"),
+ ("L_ShieldNoLeatherPatch", "L_ShdNoLeathPatch"),
+ ("L_sim2", "L_Sim2"),
+ ("L_sim3", "L_Sim3"),
+ ("L_sim", "L_Sim"),
+ ("L_Nao2trouxe", "L_Nao2Trouxe"),
+ ("L_snakeskins_completecheck", "L_snakeskin_okcheck"),
+ ("L_SugerePesquisaDePreco", "L_PesquisePreco"),
+ ("L_tchau2", "L_Tchau2"),
+ ("L_tchau", "L_Tchau"),
+ ("L_This_shouldn_t_happen", "L_NaoDeviaAcontecer"),
+ ("L_too_lowlevel_for_stinger", "L_Stinger_LvlBaixo"),
+ ("L_verde", "L_Verde"),
+ ("L_verificaMaisPresentes", "L_MaisPresenteCheck"),
+ ("L_VOLTA", "L_Volta"),
+ ("L_voltaCheckIngredientes", "L_VoltaCheckItens"),
+ ("L_voltaComIngredientes", "L_VoltaComItens"),
+ ("L_warp", "L_Warp2"),
+ ("L_minissaia", "L_Minissaia"),
+ ("L_preMenu", "L_PreMenu"),
+ ("L_Regras", "L_regras"),
+ ("S_MOBS_queimaduraEscorpiao", "S_MOBS_QueimEscorp"),
+ ("S_MOBS_queimaduraTartaruga", "S_MOBS_QueimTartaruga"),
+ ("Refinamento \\+\" + (@menu", "Refinamento \" + (@menu"),
+ ("Bom! \Aqui vou eu...", "Bom! Aqui vou eu..."),
+ ("\\:\";", ":\";"),
+ ("if BaseLevel <= 10, set", "if (BaseLevel <= 10) set"),
+ ("\\: Passando", ": Passando"),
+ ("\\o/", "o/"),
+ ("(getgmlevel ==", "(getgmlevel() =="),
+ ("foice", "Foice"),
+ ("lanternaJack", "LanternaJack"),
+ ("0), set @preco, ", "0) set @preco, "),
+ ("255), set @preco, ", "255) set @preco, "),
+ ("L_pass", "L_Pass"),
+ ("Quest_threepwood1", "QUEST_threepwood1"),
+ ("L_no", "L_No"),
+ ("L_askHelp", "L_AskHelp"),
+ ("L_ask", "L_Ask"),
+ ("L_notEnough", "L_NotEnough"),
+ ("L_done", "L_Done"),
+ ("L_toomany", "L_TooMany"),
+ ("L_not_enough_money", "L_Not_enough_money"),
+ ("L_island", "L_Island"),
+ ("if @colorID == 2 ", "if (@colorID == 2) "),
+ ("L_help", "L_Help"),
+ ("L_yes", "L_Yes"),
+ ("if @opacityID == 2 ", "if (@opacityID == 2) "),
+ ("L_NohMask_Accuse_Respond", "L_NohMask_AResp"),
+ ("if @opacityID > 4 ", "if (@opacityID > 4) "),
+ ("if @tmpHairStyle > 0 ", "if (@tmpHairStyle > 0) "),
+ ("if @colorID > 6 ", "if (@colorID > 6) "),
+ ("if @opacityID < 0 ", "if (@opacityID < 0) "),
+ ("if countitem(\"MaggotSlime\") >= 10 goto", "if (countitem(\"MaggotSlime\") >= 10) goto"),
+ ("if @colorID < 0 set", "if (@colorID < 0) set"),
+ ];
+
+ for val in vals:
+ line = line.replace(val[0], val[1]);
+
+ idx = line.find("getmapmobs(")
+ if idx >= 0:
+ idx2 = line.find("\"", idx + len("getmapmobs(") + 1)
+ idx3 = line.find(")", idx + len("getmapmobs(") + 1)
+ if idx2 + 1 == idx3:
+ line = line[:idx2 + 1] + ",\"all\"" + line[idx2 + 1:]
+
+ line = line.replace("getmapmobs(", "mobcount(")
+
+ m = setRe.search(line);
+ if m != None:
+ line = "{0}{1} = {2};\n".format(m.group("space"), m.group("var"), m.group("val"))
+
+ w.write(line)
+
diff --git a/hercules/code/server/utils.py b/hercules/code/server/utils.py
new file mode 100644
index 0000000..ebbddf3
--- /dev/null
+++ b/hercules/code/server/utils.py
@@ -0,0 +1,12 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+from code.fileutils import removeAllFiles, makeDir
+
+def cleanServerData():
+ removeAllFiles("newserverdata")
+ makeDir("newserverdata/conf")
+ makeDir("newserverdata/db/re/")
+ makeDir("newserverdata/npc/re/")
diff --git a/hercules/code/servertoclient/__init__.py b/hercules/code/servertoclient/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hercules/code/servertoclient/__init__.py
diff --git a/hercules/code/servertoclient/homunculuses.py b/hercules/code/servertoclient/homunculuses.py
new file mode 100644
index 0000000..678a96c
--- /dev/null
+++ b/hercules/code/servertoclient/homunculuses.py
@@ -0,0 +1,31 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.fileutils import makeDir, readFile, saveFile
+
+def convertHomunculuses():
+ destDir = "clientdata/"
+ templatesDir = "templates/"
+ homunculusesDbFile = "serverdata/db/re/homunculus_db.txt"
+ fieldsSplit = re.compile(",")
+ makeDir(destDir)
+ tpl = readFile(templatesDir + "homunculus.tpl")
+ homunculuses = readFile(templatesDir + "homunculuses.xml")
+ data = ""
+ homunculusSprite = "<sprite>monsters/tortuga.xml</sprite>";
+ with open(homunculusesDbFile, "r") as f:
+ for line in f:
+ if line == "" or line[0:2] == "//":
+ continue
+ rows = fieldsSplit.split(line)
+ if len(rows) < 9:
+ continue
+ data = data + tpl.format(
+ id = rows[0],
+ sprite = homunculusSprite,
+ name = rows[2])
+ saveFile(destDir + "homunculuses.xml", homunculuses.format(data))
diff --git a/hercules/code/servertoclient/items.py b/hercules/code/servertoclient/items.py
new file mode 100644
index 0000000..3fdb531
--- /dev/null
+++ b/hercules/code/servertoclient/items.py
@@ -0,0 +1,138 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+from sets import Set
+
+from code.fileutils import makeDir, readFile, saveFile
+from code.stringutils import stripQuotes2, strToXml
+
+def prepStat(val, text):
+ if val != "0" and val != "":
+ return " {0}=\"{1}\"\n".format(text, val)
+ return ""
+
+def convertItems():
+ destDir = "clientdata/"
+ templatesDir = "templates/"
+ itemsDbFile = "serverdata/sql-files/item_db_re.sql"
+ fieldsSplit = re.compile(",")
+ bracketsSplit = re.compile("[(]|[)]")
+ makeDir(destDir)
+ tpl = readFile(templatesDir + "item.tpl")
+ items = readFile(templatesDir + "items.xml")
+ data = ""
+ ids = Set()
+ with open(itemsDbFile, "r") as f:
+ for line in f:
+ if len(line) < 10 or line[0:2] == "//" or line[0:12] != "REPLACE INTO":
+ continue
+ rows = bracketsSplit.split(line)
+ if len(rows) < 2:
+ continue
+ rows = fieldsSplit.split(rows[1])
+ if len(rows) < 31:
+ continue
+ rows = stripQuotes2(rows)
+ itemId = rows[0]
+ name = rows[1]
+# name2 = rows[2]
+ itemType = rows[3]
+# priceBuy = rows[4]
+# priceSell = rows[5]
+ weight = rows[6]
+ atk = rows[7]
+ matk = rows[8]
+ defense = rows[9]
+ attackRange = rows[10]
+# slots = rows[11]
+# equipJobs = rows[12]
+# equipUpper = rows[12]
+# equipGender = rows[14]
+ equipLocations = rows[15]
+# weaponLevel = rows[16]
+# equipLevelMin = rows[17]
+# equipLevelMax = rows[18]
+# refinable = rows[19]
+ view = rows[20]
+# bindOnEquip = rows[21]
+# buyInStore = rows[22]
+ delay = rows[23]
+# tradeFlag = rows[24]
+# tradeGroup = rows[25]
+# nouseFlag = rows[26]
+# nouseGroup = rows[27]
+# stackAmount = rows[28]
+# stackFlag = rows[29]
+# sprite = rows[30]
+
+ name = name.replace("\\'", "'")
+ image = ""
+
+ statStr = prepStat(atk, "attack")
+ statStr = statStr + prepStat(matk, "mattack")
+ statStr = statStr + prepStat(defense, "defense")
+ statStr = statStr + prepStat(weight, "weight")
+ statStr = statStr + prepStat(attackRange, "range")
+ statStr = statStr + prepStat(delay, "speed")
+# print itemId + "," + equipLocations
+# typeStr = "other"
+ typeStr = "equip-legs"
+ spriteStr = "equipment/legs/trousers-male.xml"
+ image = "generic/box-fish.png"
+ if itemType == 0 or itemType == 2 or itemType == 18: # usable
+ image = "usable/bread.png"
+ typeStr = "usable"
+ spriteStr = "";
+ elif equipLocations == "0":
+ image = "usable/bread.png"
+ typeStr = "usable"
+ spriteStr = "";
+ elif equipLocations == "1":
+ image = "equipment/legs/shorts.png|S:#4d4d4d,514d47,686868,706662,919191,99917b,b6b6b6,c0b698,dfdfdf,e4dfca"
+ typeStr = "equip-legs"
+ spriteStr = "equipment/legs/shorts-male.xml|#4d4d4d,514d47,686868,706662,919191,99917b,b6b6b6,c0b698,dfdfdf,e0d5bf";
+ elif equipLocations == "2":
+ image = "equipment/weapons/knife.png"
+ typeStr = "equip-1hand"
+ spriteStr = "equipment/weapons/knife.xml";
+ elif equipLocations == "4":
+ image = "equipment/hands/armbands.png"
+ typeStr = "equip-arms"
+ spriteStr = "equipment/hands/armbands-male.xml";
+ elif equipLocations == "16":
+ image = "equipment/chest/cottonshirt.png|S:#3c3c3c,3e3c38,4d4d4d,514d47,686868,706662,919191,99917b,b6b6b6,c0b698,dfdfdf,e4dfca"
+ typeStr = "equip-torso"
+ spriteStr = "equipment/chest/cottonshirt-male.xml|#43413d,59544f,7a706c,8a8176,a69e88,d1c7a7,e0d5bf";
+ elif equipLocations == "64":
+ image = "equipment/feet/boots.png|S:#3c3c3c,40332d,4d4d4d,5e4a3d,686868,705740,919191,a1825d,b6b6b6,b59767,dfdfdf,dbbf88"
+ typeStr = "equip-feet"
+ spriteStr = "equipment/feet/boots-male.xml|#40332d,5e4a3d,705740,a1825d,b59767,dbbf88";
+ elif equipLocations == "136":
+ image = "equipment/chest/cottonshirt.png|S:#3c3c3c,3e3c38,4d4d4d,514d47,686868,706662,919191,99917b,b6b6b6,c0b698,dfdfdf,e4dfca"
+ typeStr = "equip-torso"
+ spriteStr = "equipment/chest/cottonshirt-male.xml|#43413d,59544f,7a706c,8a8176,a69e88,d1c7a7,e0d5bf";
+ elif equipLocations == "256":
+ image = "equipment/head/bandana.png"
+ typeStr = "equip-head"
+ spriteStr = "equipment/head/bandana-male.xml";
+ elif equipLocations == "512":
+ # no sprites in evol
+ image = "equipment/chest/sailorshirt.png"
+ typeStr = "equip-torso"
+ spriteStr = "equipment/chest/shirt-male.xml|#131913,1b231d,233129,35433e,4e6059,6c8279;#72571e,836737,a5854d,b18f45";
+
+ name = strToXml(name);
+
+ if itemId not in ids:
+ ids.add(itemId)
+ data = data + tpl.format(itemId, name, 0,
+ statStr, image, typeStr, spriteStr)
+ if view != "0" and view not in ids:
+ ids.add(view)
+ data = data + tpl.format(view, name, 0,
+ statStr, image, typeStr, spriteStr)
+
+ saveFile(destDir + "items.xml", items.format(data))
diff --git a/hercules/code/servertoclient/luas.py b/hercules/code/servertoclient/luas.py
new file mode 100644
index 0000000..8b401b7
--- /dev/null
+++ b/hercules/code/servertoclient/luas.py
@@ -0,0 +1,51 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2015 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+comaSplit = re.compile(",")
+equalSplit = re.compile("=")
+
+def extractLuaArray(fileName, arrName):
+ with open(fileName, "r") as f:
+ for line in f:
+ if line.find(arrName) == 0:
+ line = line[line.find("{") + 1:]
+ line = line[:line.find("}")]
+ return line
+ return ""
+
+def convertJobName():
+ jobs = dict()
+ jobNameFile = "rodata/decompiled/jobname.lua"
+ line = extractLuaArray(jobNameFile, "JobNameTable")
+ arr = comaSplit.split(line)
+ for itemStr in arr:
+ parts = equalSplit.split(itemStr.strip())
+ if parts[0].find("[jobtbl.") == 0:
+ key = parts[0].strip()
+ key = key[8:-1].strip()
+ val = parts[1].strip()
+ val = val[1:-1].strip()
+ jobs[key] = val
+ return jobs
+
+def convertIdentity(jobs):
+ idents = dict()
+ npcIdentityFile = "rodata/decompiled/npcidentity.lua"
+ line = extractLuaArray(npcIdentityFile, "jobtbl = ")
+ arr = comaSplit.split(line)
+ for itemStr in arr:
+ parts = equalSplit.split(itemStr.strip())
+ key = parts[0].strip()
+ val = parts[1].strip()
+ if key in jobs:
+ idents[val] = jobs[key].lower()
+ return idents
+
+def convertLuas():
+ jobs = convertJobName()
+ idtofile = convertIdentity(jobs)
+ return idtofile
diff --git a/hercules/code/servertoclient/maps.py b/hercules/code/servertoclient/maps.py
new file mode 100644
index 0000000..0f1ca5b
--- /dev/null
+++ b/hercules/code/servertoclient/maps.py
@@ -0,0 +1,48 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import zlib
+
+from code.fileutils import readInt16, readInt32, makeDir, copyFile, readFile, readMapName, saveFile, readData
+from code.tileutils import getTileData, getTile
+
+def covertToTmx(f, mapsCount):
+ destDir = "clientdata/"
+ mapsDir = destDir + "maps/"
+ tilesetsDir = destDir + "graphics/tilesets/"
+ templatesDir = "templates/"
+ makeDir(mapsDir)
+ makeDir(tilesetsDir)
+ copyFile(templatesDir, tilesetsDir, "collision.png")
+ copyFile(templatesDir, tilesetsDir, "tileset.png")
+ tmx = readFile("templates/template.tmx")
+ for i in xrange(0, mapsCount):
+ name = readMapName(f)
+ print "converting map [{0:4}/{1:4}]: {2}".format(i, mapsCount + 1, name)
+ sx = readInt16(f)
+ sy = readInt16(f)
+ sz = readInt32(f)
+ mapData = readData(f, sz)
+ dc = zlib.decompressobj()
+ mapData = dc.decompress(mapData)
+ ground = ""
+ collision = ""
+ fringe = ""
+ for y in xrange(0, sy):
+ for x in xrange(0, sx):
+ tileData = getTileData(mapData, x, y, sx)
+ tile = getTile(tileData)
+ if x + 1 == sx and y + 1 == sy:
+ ground = ground + tile[0]
+ collision = collision + tile[1]
+ fringe = fringe + "0";
+ else:
+ ground = ground + tile[0] + ","
+ collision = collision + tile[1] + ","
+ fringe = fringe + "0,";
+ ground = ground + "\n"
+ collision = collision + "\n"
+ fringe = fringe + "\n"
+ saveFile(mapsDir + name + ".tmx", tmx.format(sx, sy, ground, collision, fringe))
diff --git a/hercules/code/servertoclient/mercenaries.py b/hercules/code/servertoclient/mercenaries.py
new file mode 100644
index 0000000..481f66c
--- /dev/null
+++ b/hercules/code/servertoclient/mercenaries.py
@@ -0,0 +1,31 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.fileutils import makeDir, readFile, saveFile
+
+def convertMercenaries():
+ destDir = "clientdata/"
+ templatesDir = "templates/"
+ mercenariesDbFile = "serverdata/db/mercenary_db.txt"
+ fieldsSplit = re.compile(",")
+ makeDir(destDir)
+ tpl = readFile(templatesDir + "mercenary.tpl")
+ mercenaries = readFile(templatesDir + "mercenaries.xml")
+ data = ""
+ mercenarySprite = "<sprite>monsters/croc.xml</sprite>";
+ with open(mercenariesDbFile, "r") as f:
+ for line in f:
+ if line == "" or line[0:2] == "//":
+ continue
+ rows = fieldsSplit.split(line)
+ if len(rows) < 9:
+ continue
+ data = data + tpl.format(
+ id = rows[0],
+ sprite = mercenarySprite,
+ name = rows[2])
+ saveFile(destDir + "mercenaries.xml", mercenaries.format(data))
diff --git a/hercules/code/servertoclient/monsters.py b/hercules/code/servertoclient/monsters.py
new file mode 100644
index 0000000..969795d
--- /dev/null
+++ b/hercules/code/servertoclient/monsters.py
@@ -0,0 +1,43 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.fileutils import makeDir, readFile, saveFile
+from code.stringutils import strToXml, stripQuotes
+
+def convertMonsters(isNonFree = False, idtofile = None):
+ destDir = "clientdata/"
+ templatesDir = "templates/"
+ monstersDbFile = "serverdata/sql-files/mob_db_re.sql"
+ fieldsSplit = re.compile(",")
+ bracketsSplit = re.compile("[(]|[)]")
+ makeDir(destDir)
+ tpl = readFile(templatesDir + "monster.tpl")
+ monsters = readFile(templatesDir + "monsters.xml")
+ data = ""
+
+ with open(monstersDbFile, "r") as f:
+ for line in f:
+ if len(line) < 10 or line[0:2] == "//" or line[0:12] != "REPLACE INTO":
+ continue
+ rows = bracketsSplit.split(line)
+ if len(rows) < 2:
+ continue
+ rows = fieldsSplit.split(rows[1])
+ if len(rows) < 5:
+ continue
+ monsterId = rows[0]
+ if isNonFree == True and monsterId in idtofile:
+ #convertSprite("rodata/data/sprite/ёуЅєЕН/", idtofile[monsterId])
+ monsterSprite = "<sprite>sprites/{0}.xml</sprite>".format(idtofile[monsterId])
+ else:
+ monsterSprite = """<sprite>monsters/blub.xml</sprite>
+ <sprite>accessories/blub-tentacle.xml|#3e4164,3a3968,544a82,64437a,7d6db4,a26392,8f99c4,d294ab,b3cdcd,e7b8b8,d9ecd1,f0e8c5</sprite>""";
+
+ name = strToXml(stripQuotes(rows[2]))
+ data = data + tpl.format(monsterId, name, monsterSprite)
+
+ saveFile(destDir + "monsters.xml", monsters.format(data))
diff --git a/hercules/code/servertoclient/npcs.py b/hercules/code/servertoclient/npcs.py
new file mode 100644
index 0000000..5e49ffd
--- /dev/null
+++ b/hercules/code/servertoclient/npcs.py
@@ -0,0 +1,29 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2015 Evol Online
+# Author: Andrei Karas (4144)
+
+from code.fileutils import makeDir, readFile, saveFile
+
+
+def getNpcIds(idtofile):
+ for key1 in idtofile:
+ key = int(key1)
+ if 45 <= key <= 125 or 400 < key < 1000 or 10001 <= key < 10100:
+ yield key1
+
+
+def convertNpcsNonFree(idtofile):
+ destDir = "clientdata/"
+ templatesDir = "templates/"
+ makeDir(destDir)
+ tpl = readFile(templatesDir + "npc.tpl")
+ npcs = readFile(templatesDir + "npcs.xml")
+ data = ""
+
+ for key in getNpcIds(idtofile):
+ npcSprite = "<sprite>sprites/{0}.xml</sprite>".format(idtofile[key])
+ data = data + tpl.format(
+ id = key,
+ sprite = npcSprite)
+ saveFile(destDir + "npcs.xml", npcs.format(data))
diff --git a/hercules/code/servertoclient/pets.py b/hercules/code/servertoclient/pets.py
new file mode 100644
index 0000000..bc6deef
--- /dev/null
+++ b/hercules/code/servertoclient/pets.py
@@ -0,0 +1,31 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.fileutils import makeDir, readFile, saveFile
+
+def convertPets():
+ destDir = "clientdata/"
+ templatesDir = "templates/"
+ petsDbFile = "serverdata/db/re/pet_db.txt"
+ fieldsSplit = re.compile(",")
+ makeDir(destDir)
+ tpl = readFile(templatesDir + "pet.tpl")
+ pets = readFile(templatesDir + "pets.xml")
+ data = ""
+ petSprite = "<sprite>monsters/tortuga.xml</sprite>";
+ with open(petsDbFile, "r") as f:
+ for line in f:
+ if line == "" or line[0:2] == "//":
+ continue
+ rows = fieldsSplit.split(line)
+ if len(rows) < 9:
+ continue
+ data = data + tpl.format(
+ id = rows[0],
+ sprite = petSprite,
+ name = rows[2])
+ saveFile(destDir + "pets.xml", pets.format(data))
diff --git a/hercules/code/servertoclient/quests.py b/hercules/code/servertoclient/quests.py
new file mode 100644
index 0000000..df47fd5
--- /dev/null
+++ b/hercules/code/servertoclient/quests.py
@@ -0,0 +1,39 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.fileutils import makeDir, readFile, saveFile
+from code.stringutils import strToXml, stripQuotes
+
+def convertQuests():
+ print "quests disabled for now"
+ return
+ destDir = "clientdata/"
+ templatesDir = "templates/"
+ questsDbFile = "serverdata/db/quest_db.txt"
+ fieldsSplit = re.compile(",")
+ makeDir(destDir)
+ tpl = readFile(templatesDir + "quest.tpl")
+ quests = readFile(templatesDir + "quests.xml")
+ data = ""
+ with open(questsDbFile, "r") as f:
+ for line in f:
+ if line == "" or line[0:2] == "//":
+ continue
+ rows = fieldsSplit.split(line)
+ if len(rows) < 9:
+ continue
+ questId = rows[0]
+ text = rows[8]
+ if text[-1] == "\n":
+ text = text[:-1]
+ text = strToXml(stripQuotes(text))
+ name = text
+ if len(name) > 20:
+ name = name[:19]
+
+ data = data + tpl.format(questId, name, text + ": " + str(questId))
+ saveFile(destDir + "quests.xml", quests.format(data))
diff --git a/hercules/code/servertoclient/skills.py b/hercules/code/servertoclient/skills.py
new file mode 100644
index 0000000..6affb72
--- /dev/null
+++ b/hercules/code/servertoclient/skills.py
@@ -0,0 +1,33 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import re
+
+from code.fileutils import makeDir, readFile, saveFile
+
+def convertSkillsToXml():
+ destDir = "clientdata/"
+ templatesDir = "templates/"
+ skillsDbFile = "serverdata/db/re/skill_db.txt"
+ fieldsSplit = re.compile(",")
+ makeDir(destDir)
+ tpl = readFile(templatesDir + "skill.tpl")
+ skills = readFile(templatesDir + "skills.xml")
+ data = ""
+ with open(skillsDbFile, "r") as f:
+ for line in f:
+ if line == "" or line[0:2] == "//":
+ continue
+ rows = fieldsSplit.split(line)
+ if len(rows) < 9:
+ continue
+ skillId = rows[0]
+ name = rows[15].strip()
+ description = rows[16].strip()
+ idx = description.find ("//")
+ if idx > 1:
+ description = description[:idx]
+ data = data + tpl.format(skillId, name, description)
+ saveFile(destDir + "skills.xml", skills.format(data))
diff --git a/hercules/code/servertoclient/sprites.py b/hercules/code/servertoclient/sprites.py
new file mode 100644
index 0000000..5dad9d1
--- /dev/null
+++ b/hercules/code/servertoclient/sprites.py
@@ -0,0 +1,467 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2015 Evol Online
+# Author: Andrei Karas (4144)
+
+import array
+import os
+from PIL import Image
+
+from code.fileutils import readInt8, readInt16, readInt32, makeDir, readFile, saveFile, skipData, readData, findFileIn
+
+class ActClass:
+ pass
+
+class ActAnimationClass:
+ pass
+
+class ActSpriteClass:
+ pass
+
+class ActFrameClass:
+ pass
+
+class ActPivotClass:
+ pass
+
+class ActEventClass:
+ pass
+
+class SprClass:
+ pass
+
+class SprImageClass:
+ pass
+
+def readIndexedRLEImage(f, spr):
+ spr.nimages = spr.indexedSpritesCount
+ spr.images = dict()
+ for imageN in range(0, spr.indexedSpritesCount):
+ image = SprImageClass()
+ spr.images[imageN] = image
+ image.width = readInt16(f)
+ image.height = readInt16(f)
+ image.compressed = True
+ data = array.array('B', (0 for _ in xrange(image.width * image.height)))
+ image.data = data
+
+ compressSize = readInt16(f)
+ #uncompressedSize = image.width * image.height
+ #print "uncompressed size=" + str(uncompressedSize)
+ #print "compressed size=" + str(compressSize)
+
+ idx = 0
+ readCnt = 0
+ while readCnt < compressSize:
+ c = readInt8(f)
+ readCnt = readCnt + 1
+ data[idx] = c;
+ #print "{0:4}: {1}".format(idx, c)
+ idx = idx + 1
+ if c == 0:
+ cnt = readInt8(f)
+ readCnt = readCnt + 1
+ if cnt == 0:
+ data[idx] = cnt
+ #print "{0:4}: {1}".format(idx, cnt)
+ idx = idx + 1
+ else:
+ for j in range(1, cnt):
+ data[idx] = c
+ #print "{0:4} ({1} to {2}): {3}".format(idx, j, cnt - 1, c)
+ idx = idx + 1
+ #print "read bytes: " + str(readCnt)
+
+def readIndexedImage(f, spr):
+ pass
+
+def readRgbaImage(f, spr):
+ for imageN in range(0, spr.rgbaSpritesCount):
+ image = SprImageClass()
+ spr.images[imageN + spr.indexedSpritesCount] = image
+ image.width = readInt16(f)
+ image.height = readInt16(f)
+ data = array.array('I', (0 for _ in xrange(image.width * image.height)))
+ image.compressed = False
+ image.data = data
+ for idx in range(0, image.width * image.height):
+ data[idx] = readInt32(f)
+
+
+def readPalette(f, spr):
+ palette = array.array('I', (0 for _ in xrange(256)))
+ spr.palette = palette
+ for col in range(0, 256):
+ palette[col] = readInt32(f)
+
+def getSignedNumber(number, bitLength):
+ mask = (2 ** bitLength) - 1
+ if number & (1 << (bitLength - 1)):
+ return number | ~mask
+ else:
+ return number & mask
+
+def decodeSprite(spr):
+ palette = spr.palette
+ spr.maxwidth = 0
+ spr.maxheight = 0
+ alpha = False
+ for imageN in range(0, spr.indexedSpritesCount):
+ image = spr.images[imageN]
+ indexed = image.data
+ data = array.array('I', (0 for _ in xrange(image.width * image.height)))
+ for idx in range(0, image.width * image.height):
+ col = indexed[idx]
+ if col == 0:
+ data[idx] = 0
+ else:
+ if palette[col] > 0x00ffffff:
+ data[idx] = palette[col]
+ alpha = True
+ else:
+ data[idx] = palette[col] + 0xff000000
+ image.data = data
+ if image.width > spr.maxwidth:
+ spr.maxwidth = image.width
+ if image.height > spr.maxheight:
+ spr.maxheight = image.height
+
+ # for debug
+# png = Image.new('RGBA', (image.width, image.height))
+# png.putdata(image.data)
+# png.save("test{0}.png".format(imageN))
+
+ if alpha:
+ print "detected alpha"
+
+ for imageN in range(0, spr.rgbaSpritesCount):
+ image = spr.images[imageN + spr.indexedSpritesCount]
+ if image.width > spr.maxwidth:
+ spr.maxwidth = image.width
+ if image.height > spr.maxheight:
+ spr.maxheight = image.height
+
+
+
+def saveSpriteImage(act, spr, spriteDir, spriteName):
+# for imageN in range(0, spr.rgbaSpritesCount):
+# image = spr.images[imageN + spr.indexedSpritesCount]
+# print "{0} x {1}".format(image.width, image.height)
+# png = Image.new('RGBA', (image.width, image.height))
+# png.putdata(image.data)
+# png.save("test{0}.png".format(imageN + spr.indexedSpritesCount))
+
+# numTiles = spr.indexedSpritesCount + spr.rgbaSpritesCount
+
+ counted = 0
+ for animN in range(0, act.nanimations):
+ anim = act.animations[animN]
+ for spriteN in range(0, anim.nsprites):
+ sprite = anim.sprites[spriteN]
+# key = []
+# for frameN in range(0, sprite.nframes):
+# frame = sprite.frames[frameN]
+# idf = frame.frameIndex
+# if frame.mirror > 0:
+# idf = - idf
+# key.append(idf)
+ counted = counted + 1
+
+ numTiles = counted
+ #print "max: {0}x{1}".format(spr.maxwidth, spr.maxheight)
+
+ # in row
+ rowTiles = int(2048 / spr.maxwidth)
+ colTiles = int(numTiles / rowTiles)
+ if colTiles * rowTiles < numTiles:
+ colTiles = colTiles + 1
+ tilesetWidth = 2048
+ tilesetHeight = colTiles * spr.maxheight
+# print "num {0} row {1}, col {2}".format(numTiles, rowTiles, colTiles)
+# print "size {0}x{1}".format(2048, colTiles * spr.maxheight)
+
+# tileset = array.array('I', (0 for _ in xrange(tilesetWidth * tilesetHeight)))
+ tileset = Image.new('RGBA', (tilesetWidth, tilesetHeight))
+# png.putdata(tileset)
+ #draw = ImageDraw.Draw(png)
+
+ x = 0
+ y = 0
+
+ frameToIdx = dict()
+
+ tile = 0
+ for animN in range(0, act.nanimations):
+ anim = act.animations[animN]
+ for spriteN in range(0, anim.nsprites):
+ sprite = anim.sprites[spriteN]
+ frameToIdx[str(animN) + "_" + str(spriteN)] = tile
+ for frameN in range(0, sprite.nframes):
+ frame = sprite.frames[frameN]
+ frm = frame.frameIndex
+ if frame.mirror > 0:
+ frm = - frm
+
+ if frm in frameToIdx:
+ continue
+
+ if frame.frameIndex not in spr.images:
+ print "wrong frame index: {0}".format(frame.frameIndex)
+ continue
+ image = spr.images[frame.frameIndex]
+ png = Image.new('RGBA', (image.width, image.height))
+ png.putdata(image.data)
+ if frame.mirror > 0:
+ png = png.transpose(Image.FLIP_LEFT_RIGHT)
+ offsetX = (spr.maxwidth - image.width) / 2
+ offsetY = spr.maxheight - image.height
+# offsetX = (spr.maxwidth - image.width) / 2 + getSignedNumber(frame.offsetX, 32)
+# offsetY = spr.maxheight - image.height + getSignedNumber(frame.offsetY, 32)
+ tileset.paste(png, (x + offsetX, y + offsetY))
+ tile = tile + 1
+ x = x + spr.maxwidth
+ if x + spr.maxwidth > 2048:
+ x = 0
+ y = y + spr.maxheight
+
+# for imageN in range(0, spr.rgbaSpritesCount + spr.indexedSpritesCount):
+# image = spr.images[imageN]
+# png = Image.new('RGBA', (image.width, image.height))
+# png.putdata(image.data)
+# tileset.paste(png, (x, y))
+# x = x + spr.maxwidth
+# if x + spr.maxwidth > 2048:
+# x = 0
+# y = y + spr.maxheight
+
+ spr.frameToIdx = frameToIdx
+ spr.tilesetWidth = tilesetWidth
+ spr.tilesetHeight = tilesetHeight
+ makeDir(spriteDir)
+ tileset.save(spriteDir + spriteName + ".png")
+
+def extractSpriteAnimData(act, spr, actIndex, direction):
+# delay = anim.delay
+ delay = 100
+ data = " <animation direction=\"" + direction + "\">\n"
+ if actIndex not in act.animations:
+ data = data + " <frame index=\"{0}\" delay=\"{1}\" offsetX=\"{2}\" offsetY=\"{3}\"/>\n".format(
+ 0, delay, 0, 0)
+ data = data + " </animation>\n"
+ return data
+
+ anim = act.animations[actIndex]
+ for spriteN in range(0, anim.nsprites):
+ #sprite = anim.sprites[spriteN]
+ #for frameN in range(0, sprite.nframes):
+ #frame = sprite.frames[frameN]
+ #frm = frame.frameIndex
+ #if frame.mirror > 0:
+ # frm = -frm
+ #if frm not in spr.frameToIdx:
+ # continue
+
+ idx = spr.frameToIdx[str(actIndex) + "_" + str(spriteN)]
+ offsetX = 0
+ offsetY = 0
+# offsetX = frame.offsetX
+# offsetY = frame.offsetY
+# if offsetX > 4294900000:
+# offsetX = - (4294967296 - offsetX)
+# if offsetY > 4294000000:
+# offsetY = -(4294967296 - offsetY)
+
+ data = data + " <frame index=\"{0}\" delay=\"{1}\" offsetX=\"{2}\" offsetY=\"{3}\"/>\n".format(
+ idx, delay, offsetX, offsetY)
+
+ data = data + " </animation>\n"
+ return data
+
+def extractSpriteDataAll(act, spr, actIndex, name):
+ data = extractSpriteAnimData(act, spr, actIndex, "down")
+ data = data + extractSpriteAnimData(act, spr, actIndex + 1, "downleft")
+ data = data + extractSpriteAnimData(act, spr, actIndex + 2, "left")
+ data = data + extractSpriteAnimData(act, spr, actIndex + 3, "upleft")
+ data = data + extractSpriteAnimData(act, spr, actIndex + 4, "up")
+ data = data + extractSpriteAnimData(act, spr, actIndex + 5, "upright")
+ data = data + extractSpriteAnimData(act, spr, actIndex + 6, "right")
+ data = data + extractSpriteAnimData(act, spr, actIndex + 6, "downright")
+ return data
+
+def saveSpriteXml(act, spr, spriteDir, spriteName):
+ templatesDir = "templates/"
+ dstFile = spriteDir + spriteName + ".xml"
+ tpl = readFile(templatesDir + "sprite.xml")
+ # 0, 8, 16, 24, 32, 40, 48
+ # 0 - walk or attack or ?
+ # 8 - walk
+ # 16 - attack
+ # 24 - dead
+ # 32 - dead2 ?
+ # no more
+ standData = extractSpriteDataAll(act, spr, 0, "stand")
+ walkData = extractSpriteDataAll(act, spr, 8, "walk")
+ attackData = extractSpriteDataAll(act, spr, 16, "attack")
+ deadData = extractSpriteDataAll(act, spr, 32, "dead")
+
+ data = tpl.format(
+ src = "graphics/sprites/sprites/" + spriteName + ".png",
+ width = spr.maxwidth,
+ height = spr.maxheight,
+ stand = standData,
+ walk = walkData,
+ attack = attackData,
+ dead = deadData
+ )
+ saveFile(dstFile, data)
+
+
+def readAct(actFile):
+ act = ActClass()
+ with open(actFile, "r") as f:
+ act.header = readInt16(f)
+ if act.header != 17217:
+ #print "Wrong header in file {0}".format(actFile)
+ return None
+ act.minorVersion = readInt8(f)
+ act.majorVersion = readInt8(f)
+ act.nanimations = readInt16(f)
+ print "{0}, {1}.{2}, {3}".format(actFile, act.majorVersion, act.minorVersion, act.nanimations)
+ #print " animations: " + str(act.nanimations)
+ act.animations = dict()
+ skipData(f, 10)
+ for animN in range(0, act.nanimations):
+ anim = ActAnimationClass()
+ anim.delay = 30
+ act.animations[animN] = anim
+ anim.nsprites = readInt32(f)
+ #print " sprites: " + str(anim.nsprites)
+ anim.sprites = dict()
+ for spriteN in range(0, anim.nsprites):
+ sprite = ActSpriteClass()
+ anim.sprites[spriteN] = sprite
+ sprite.left1 = readInt32(f)
+ sprite.top1 = readInt32(f)
+ sprite.right1 = readInt32(f)
+ sprite.buttom1 = readInt32(f)
+ sprite.left2 = readInt32(f)
+ sprite.top2 = readInt32(f)
+ sprite.right2 = readInt32(f)
+ sprite.buttom2 = readInt32(f)
+ sprite.nframes = readInt32(f)
+ #print "sprite {0}, frames: {1}".format(spriteN, sprite.nframes)
+ sprite.frames = dict()
+ for frameN in range(0, sprite.nframes):
+ frame = ActFrameClass()
+ sprite.frames[frameN] = frame
+ frame.offsetX = readInt32(f)
+ frame.offsetY = readInt32(f)
+ frame.frameIndex = readInt32(f)
+ #print " index: " + str(frame.frameIndex)
+ frame.mirror = readInt32(f)
+ if act.majorVersion >= 2:
+ frame.color = readInt32(f)
+ frame.scaleX = readInt32(f)
+ if act.majorVersion > 2 or (act.majorVersion == 2 and act.minorVersion >= 4):
+ frame.scaleY = readInt32(f)
+ else:
+ frame.scaleY = frame.scaleX
+ frame.angle = readInt32(f)
+ frame.spriteType = readInt32(f)
+ if act.majorVersion > 2 or (act.majorVersion == 2 and act.minorVersion >= 5):
+ frame.width = readInt32(f)
+ frame.height = readInt32(f)
+ #print "{0} x {1}".format(frame.width, frame.height)
+ if act.majorVersion >= 2:
+ sprite.eventIndex = readInt32(f)
+ if act.majorVersion > 2 or (act.majorVersion == 2 and act.minorVersion >= 3):
+ sprite.npivots = readInt32(f)
+ sprite.pivots = dict()
+ #if sprite.npivots > 0:
+ #print " pivotes: " + str(sprite.npivots)
+ for pivotN in range(0, sprite.npivots):
+ pivot = ActPivotClass()
+ sprite.pivots[pivotN] = pivot
+ pivot.unknown = readInt32(f)
+ pivot.centerX = readInt32(f)
+ pivot.centerY = readInt32(f)
+ pivot.nAttribute = readInt32(f)
+ if act.majorVersion > 2 or (act.majorVersion == 2 and act.minorVersion >= 1):
+ act.nevents = readInt32(f)
+ act.events = dict()
+ for eventN in range(0, act.nevents):
+ event = ActEventClass()
+ act.events[eventN] = event;
+ event.name = readData(f, 40)
+ if act.majorVersion > 2 or (act.majorVersion == 2 and act.minorVersion >= 2):
+ for animN in range(0, act.nanimations):
+ anim = act.animations[animN]
+ anim.delay = readInt32(f)
+ return act
+
+def readSpr(sprFile):
+ spr = SprClass()
+ with open(sprFile, "r") as f:
+ spr.header1 = readInt8(f)
+ spr.header2 = readInt8(f)
+ if spr.header1 != 0x53 or spr.header2 != 0x50:
+ return None
+ spr.minorVersion = readInt8(f)
+ spr.majorVersion = readInt8(f)
+ spr.indexedSpritesCount = readInt16(f)
+ if spr.majorVersion > 1 or (spr.majorVersion == 1 and spr.minorVersion >= 1):
+ spr.rgbaSpritesCount = readInt16(f)
+ else:
+ spr.rgbaSpritesCount = 0
+ print "{0}, {1}.{2}, {3}, {4}".format(sprFile, spr.majorVersion, spr.minorVersion, spr.indexedSpritesCount, spr.rgbaSpritesCount)
+ spr.frames = spr.indexedSpritesCount + spr.rgbaSpritesCount
+
+ if spr.majorVersion > 2 or (spr.majorVersion == 2 and spr.minorVersion >= 1):
+ readIndexedRLEImage(f, spr)
+ else:
+ readIndexedImage(f, spr)
+ readRgbaImage(f, spr)
+
+ if spr.majorVersion > 1 or (spr.majorVersion == 1 and spr.minorVersion >= 1):
+ readPalette(f, spr)
+
+ return spr
+
+def convertSprite(spritePath, spriteName):
+ actFile = "{0}.act".format(spritePath + spriteName)
+ sprFile = "{0}.spr".format(spritePath + spriteName)
+ if os.path.exists(actFile) == False or os.path.exists(sprFile) == False:
+ return None
+ act = readAct(actFile)
+ spr = readSpr(sprFile)
+ decodeSprite(spr)
+ saveSpriteImage(act, spr, "clientdata/graphics/sprites/sprites/", spriteName)
+ saveSpriteXml(act, spr, "clientdata/graphics/sprites/sprites/", spriteName)
+# if actFile.find("wolf") > 0:
+# exit(0)
+# exit(0)
+
+def findSpritePath(spriteName):
+ testName = spriteName + ".act"
+ testName2 = spriteName.upper() + ".act"
+ path = findFileIn((testName, testName2),
+ ("rodata/data/sprite/ёуЅєЕН/",
+ "rodata/data/sprite/npc/",
+ "rodata/data/sprite/homun/"))
+ return path
+
+def convertSpritesNonFree(idtofile):
+ processed = []
+ for spriteid in idtofile:
+ spriteName = idtofile[spriteid]
+ if spriteName in processed:
+ print "skipping " + spriteName
+ continue
+
+ path = findSpritePath(spriteName)
+ if path is None:
+ print "not found " + spriteName
+ continue
+ print spriteName
+ convertSprite(path, spriteName)
+ processed.append(spriteName)
diff --git a/hercules/code/serverutils.py b/hercules/code/serverutils.py
new file mode 100644
index 0000000..39a283e
--- /dev/null
+++ b/hercules/code/serverutils.py
@@ -0,0 +1,12 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import os
+
+def detectServerType():
+ if os.path.exists("oldserverdata/world/map/news.d"):
+ return "tmw"
+ else:
+ return "evol"
diff --git a/hercules/code/stringutils.py b/hercules/code/stringutils.py
new file mode 100644
index 0000000..73db611
--- /dev/null
+++ b/hercules/code/stringutils.py
@@ -0,0 +1,62 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import codecs
+
+def stripQuotes(data):
+ if len(data) == 0:
+ return data
+ if data[-1] == "\"":
+ data = data[:-1]
+ if data[0] == "\"":
+ data = data[1:]
+ if data[-1] == "'":
+ data = data[:-1]
+ if data[0] == "'":
+ data = data[1:]
+ return data
+
+def stripQuotes2(data):
+ for idx in xrange(0, len(data)):
+ data[idx] = stripQuotes(data[idx])
+ return data
+
+def strToXml(data):
+ data = data.replace("&", "&amp;");
+ data = data.replace("<", "&lt;");
+ data = data.replace(">", "&gt;");
+ return data
+
+def stripNewLine(data):
+ if len(data) == 0:
+ return data
+ if data[-1] == "\r":
+ data = data[:-1]
+ if len(data) > 0 and data[-1] == "\n":
+ data = data[:-1]
+ return data
+
+def stripWindows(data):
+ if len(data) == 0:
+ return data
+ if data[-1] == "\r":
+ data = data[:-1]
+ if len(data) > 1 and data[-2] == "\r":
+ data = data[:-2] + data[-1]
+ data = data.replace(codecs.BOM_UTF8, "", 1)
+ return data
+
+def escapeSqlStr(data):
+ data = data.replace("\\", "\\\\");
+ data = data.replace("'", "\\'");
+ data = data.replace("`", "\\`");
+ data = data.replace("{", "\\{");
+ data = data.replace("}", "\\}");
+ return data
+
+def removeGat(data):
+ if len(data) >= 4 and data[-4:] == ".gat":
+ data = data[:-4]
+ return data
diff --git a/hercules/code/tileutils.py b/hercules/code/tileutils.py
new file mode 100644
index 0000000..42e7a84
--- /dev/null
+++ b/hercules/code/tileutils.py
@@ -0,0 +1,48 @@
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import array
+
+def getTileData(mapData, x, y, sx):
+ data = mapData[y * sx + x]
+ arr = array.array("B")
+ arr.fromstring(data)
+ data = arr[0]
+ return data
+
+def getTile(data):
+ normal = 0
+ collision = 0
+ if data == 0: # 000 normal walkable
+ normal = 1
+ collision = 5
+ elif data == 1: # 001 non walkable
+ normal = 2
+ collision = 6
+ elif data == 2: # 010 same with 0
+ normal = 1
+ collision = 5
+ elif data == 3: # 011 same with 0, but water
+ normal = 3
+ collision = 5
+ elif data == 4: # 100 same with 0
+ normal = 1
+ collision = 5
+ elif data == 5: # 101 same with 1, but shootable (for now not supported!!!)
+ normal = 4
+ collision = 6
+ elif data == 6: # 110 same with 0
+ normal = 1
+ collision = 5
+ return (str(normal), str(collision))
+
+def getGroundTile(flag):
+ return str(flag + 1)
+
+def getCollisionTile(flag):
+ if flag == 0:
+ return "0"
+ return "4"
+
diff --git a/hercules/convert_mapcache_to_tmx.py b/hercules/convert_mapcache_to_tmx.py
new file mode 100755
index 0000000..e6e6bc9
--- /dev/null
+++ b/hercules/convert_mapcache_to_tmx.py
@@ -0,0 +1,24 @@
+#! /usr/bin/env python
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import os
+
+from code.fileutils import readInt32, readInt16
+from code.servertoclient.maps import covertToTmx
+
+def runFunction(path):
+ with open(path, "rb") as f:
+ size = readInt32(f)
+ if os.path.getsize(path) != size:
+ print "Map cache corrupted, wrong file size."
+ exit(1)
+ mapsCount = readInt16(f)
+ print "Maps count: " + str(mapsCount)
+ readInt16(f) # padding
+ covertToTmx(f, mapsCount)
+
+
+runFunction("serverdata/db/re/map_cache.dat")
diff --git a/hercules/convert_tmx_to_mapcache.py b/hercules/convert_tmx_to_mapcache.py
new file mode 100755
index 0000000..62d0d18
--- /dev/null
+++ b/hercules/convert_tmx_to_mapcache.py
@@ -0,0 +1,9 @@
+#! /usr/bin/env python
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+from code.clienttoserver.maps import recreateMapCache
+
+recreateMapCache();
diff --git a/hercules/extract_mapcache.py b/hercules/extract_mapcache.py
new file mode 100755
index 0000000..39a63c3
--- /dev/null
+++ b/hercules/extract_mapcache.py
@@ -0,0 +1,24 @@
+#! /usr/bin/env python
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import os
+
+from code.fileutils import readInt16, readInt32
+from code.server.maps import extractMaps
+
+
+def runFunction(path):
+ with open(path, "rb") as f:
+ size = readInt32(f)
+ if os.path.getsize(path) != size:
+ print "Map cache corrupted, wrong file size."
+ exit(1)
+ mapsCount = readInt16(f)
+ print "Maps count: " + str(mapsCount)
+ readInt16(f) # padding
+ extractMaps(f, mapsCount)
+
+runFunction("serverdata/db/re/map_cache.dat")
diff --git a/hercules/import_newdb.sh b/hercules/import_newdb.sh
new file mode 100755
index 0000000..ba81d4f
--- /dev/null
+++ b/hercules/import_newdb.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+export CMD="$*"
+
+if [[ -z "${CMD}" ]]; then
+ echo "Usage example: ./import_newsb.sh -u evol -pevol evol"
+ exit
+fi
+
+cd newdb
+
+function import {
+echo Importing $1
+mysql $CMD <$1
+}
+
+import acc_reg_num_db.sql
+import char.sql
+import char_reg_num_db.sql
+import inventory.sql
+import login.sql
+import party.sql
+import skill.sql
+import storage.sql
diff --git a/hercules/list_mapcache.py b/hercules/list_mapcache.py
new file mode 100755
index 0000000..2a83f53
--- /dev/null
+++ b/hercules/list_mapcache.py
@@ -0,0 +1,24 @@
+#! /usr/bin/env python
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2014 Evol Online
+# Author: Andrei Karas (4144)
+
+import os
+
+from code.fileutils import readInt16, readInt32
+from code.server.maps import listMapCache
+
+def runFunction(path):
+ with open(path, "rb") as f:
+ size = readInt32(f)
+ if os.path.getsize(path) != size:
+ print "Map cache corrupted, wrong file size."
+ exit(1)
+ mapsCount = readInt16(f)
+ print "Maps count: " + str(mapsCount)
+ readInt16(f) # padding
+ listMapCache(f, mapsCount)
+
+
+runFunction("serverdata/db/re/map_cache.dat")
diff --git a/hercules/templates/acc_reg_num_db.sql b/hercules/templates/acc_reg_num_db.sql
new file mode 100644
index 0000000..da8b061
--- /dev/null
+++ b/hercules/templates/acc_reg_num_db.sql
@@ -0,0 +1,11 @@
+DROP TABLE IF EXISTS `acc_reg_num_db`;
+
+CREATE TABLE IF NOT EXISTS `acc_reg_num_db` (
+ `account_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `key` VARCHAR(32) BINARY NOT NULL DEFAULT '',
+ `index` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `value` INT(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`account_id`,`key`,`index`),
+ KEY `account_id` (`account_id`)
+) ENGINE=InnoDB;
+
diff --git a/hercules/templates/char.sql b/hercules/templates/char.sql
new file mode 100644
index 0000000..0f75eb5
--- /dev/null
+++ b/hercules/templates/char.sql
@@ -0,0 +1,72 @@
+DROP TABLE IF EXISTS `char`;
+
+CREATE TABLE IF NOT EXISTS `char` (
+ `char_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `account_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `char_num` TINYINT(1) NOT NULL DEFAULT '0',
+ `name` VARCHAR(30) NOT NULL DEFAULT '',
+ `class` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',
+ `base_level` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '1',
+ `job_level` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '1',
+ `base_exp` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
+ `job_exp` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
+ `zeny` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `str` SMALLINT(4) UNSIGNED NOT NULL DEFAULT '0',
+ `agi` SMALLINT(4) UNSIGNED NOT NULL DEFAULT '0',
+ `vit` SMALLINT(4) UNSIGNED NOT NULL DEFAULT '0',
+ `int` SMALLINT(4) UNSIGNED NOT NULL DEFAULT '0',
+ `dex` SMALLINT(4) UNSIGNED NOT NULL DEFAULT '0',
+ `luk` SMALLINT(4) UNSIGNED NOT NULL DEFAULT '0',
+ `max_hp` INT(9) UNSIGNED NOT NULL DEFAULT '0',
+ `hp` INT(9) UNSIGNED NOT NULL DEFAULT '0',
+ `max_sp` INT(9) UNSIGNED NOT NULL DEFAULT '0',
+ `sp` INT(9) UNSIGNED NOT NULL DEFAULT '0',
+ `status_point` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `skill_point` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `option` INT(11) NOT NULL DEFAULT '0',
+ `karma` TINYINT(3) NOT NULL DEFAULT '0',
+ `manner` SMALLINT(6) NOT NULL DEFAULT '0',
+ `party_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `guild_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `pet_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `homun_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `elemental_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `hair` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0',
+ `hair_color` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
+ `clothes_color` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
+ `body` SMALLINT(5) unsigned NOT NULL default '0',
+ `weapon` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',
+ `shield` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',
+ `head_top` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',
+ `head_mid` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',
+ `head_bottom` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',
+ `robe` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',
+ `last_map` VARCHAR(11) NOT NULL DEFAULT '',
+ `last_x` SMALLINT(4) UNSIGNED NOT NULL DEFAULT '53',
+ `last_y` SMALLINT(4) UNSIGNED NOT NULL DEFAULT '111',
+ `save_map` VARCHAR(11) NOT NULL DEFAULT '',
+ `save_x` SMALLINT(4) UNSIGNED NOT NULL DEFAULT '53',
+ `save_y` SMALLINT(4) UNSIGNED NOT NULL DEFAULT '111',
+ `partner_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `online` TINYINT(2) NOT NULL DEFAULT '0',
+ `father` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `mother` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `child` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `fame` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `rename` SMALLINT(3) UNSIGNED NOT NULL DEFAULT '0',
+ `delete_date` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `slotchange` SMALLINT(3) UNSIGNED NOT NULL DEFAULT '0',
+ `char_opt` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `font` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
+ `unban_time` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `uniqueitem_counter` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
+ `sex` ENUM('M','F','U') NOT NULL DEFAULT 'U',
+ `hotkey_rowshift` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
+ PRIMARY KEY (`char_id`),
+ UNIQUE KEY `name_key` (`name`),
+ KEY `account_id` (`account_id`),
+ KEY `party_id` (`party_id`),
+ KEY `guild_id` (`guild_id`),
+ KEY `online` (`online`)
+) ENGINE=InnoDB AUTO_INCREMENT=150000;
+
diff --git a/hercules/templates/char_reg_num_db.sql b/hercules/templates/char_reg_num_db.sql
new file mode 100644
index 0000000..ea9429f
--- /dev/null
+++ b/hercules/templates/char_reg_num_db.sql
@@ -0,0 +1,11 @@
+DROP TABLE IF EXISTS `char_reg_num_db`;
+
+CREATE TABLE IF NOT EXISTS `char_reg_num_db` (
+ `char_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `key` VARCHAR(32) BINARY NOT NULL DEFAULT '',
+ `index` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `value` INT(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`char_id`,`key`,`index`),
+ KEY `char_id` (`char_id`)
+) ENGINE=InnoDB;
+
diff --git a/hercules/templates/collision.png b/hercules/templates/collision.png
new file mode 100644
index 0000000..f360c63
--- /dev/null
+++ b/hercules/templates/collision.png
Binary files differ
diff --git a/hercules/templates/constants.tpl b/hercules/templates/constants.tpl
new file mode 100644
index 0000000..b42bb18
--- /dev/null
+++ b/hercules/templates/constants.tpl
@@ -0,0 +1,45 @@
+//================= Hercules Database =====================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//================= License ===============================================
+//= This file is part of Hercules.
+//= http://herc.ws - http://github.com/HerculesWS/Hercules
+//=
+//= Copyright (C) 2016 Hercules Dev Team
+//=
+//= Hercules is free software: you can redistribute it and/or modify
+//= it under the terms of the GNU General Public License as published by
+//= the Free Software Foundation, either version 3 of the License, or
+//= (at your option) any later version.
+//=
+//= This program is distributed in the hope that it will be useful,
+//= but WITHOUT ANY WARRANTY; without even the implied warranty of
+//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//= GNU General Public License for more details.
+//=
+//= You should have received a copy of the GNU General Public License
+//= along with this program. If not, see <http://www.gnu.org/licenses/>.
+//=========================================================================
+//= Script Constants Database
+//=========================================================================
+
+constants_db: {
+/************* Entry structure (short) ************************************
+ Identifier: value // (int)
+ ************* Entry structure (full) *************************************
+ Identifier: {
+ Value: value // (int)
+ Parameter: true // (boolean) Defaults to false.
+ Deprecated: true // (boolean) Defaults to false.
+ }
+**************************************************************************/
+// NOTE:
+// Parameters are special in that they retrieve certain runtime values
+// depending on the specified ID in field Value. Depending on the
+// implementation values assigned by scripts to parameters will affect
+// runtime values, such as Zeny, as well (see pc_readparam/pc_setparam).
+
diff --git a/hercules/templates/group.tpl b/hercules/templates/group.tpl
new file mode 100644
index 0000000..c93b778
--- /dev/null
+++ b/hercules/templates/group.tpl
@@ -0,0 +1,11 @@
+ <group
+ id="{id}"
+ name={name}
+ longName={name}{inherit}
+ highlightName="true"
+ >
+ <commands>{commands}
+ </commands>
+ <permissions>{permissions}
+ </permissions>
+ </group>
diff --git a/hercules/templates/homunculus.tpl b/hercules/templates/homunculus.tpl
new file mode 100644
index 0000000..67a76bc
--- /dev/null
+++ b/hercules/templates/homunculus.tpl
@@ -0,0 +1,3 @@
+ <homunculus id="{id}" name="{name}">
+ {sprite}
+ </homunculus>
diff --git a/hercules/templates/homunculuses.xml b/hercules/templates/homunculuses.xml
new file mode 100644
index 0000000..3737d8b
--- /dev/null
+++ b/hercules/templates/homunculuses.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Authors: 4144
+Copyright (C) 2014 Evol Online -->
+<homunculuses>
+{0}
+</homunculuses>
diff --git a/hercules/templates/inventory.sql b/hercules/templates/inventory.sql
new file mode 100644
index 0000000..9fabd54
--- /dev/null
+++ b/hercules/templates/inventory.sql
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS `inventory`;
+
+CREATE TABLE IF NOT EXISTS `inventory` (
+ `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `char_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `nameid` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `amount` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `equip` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `identify` SMALLINT(6) NOT NULL DEFAULT '0',
+ `refine` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
+ `attribute` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0',
+ `card0` SMALLINT(11) NOT NULL DEFAULT '0',
+ `card1` SMALLINT(11) NOT NULL DEFAULT '0',
+ `card2` SMALLINT(11) NOT NULL DEFAULT '0',
+ `card3` SMALLINT(11) NOT NULL DEFAULT '0',
+ `expire_time` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `favorite` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
+ `bound` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
+ `unique_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`),
+ KEY `char_id` (`char_id`)
+) ENGINE=InnoDB;
+
diff --git a/hercules/templates/item.tpl b/hercules/templates/item.tpl
new file mode 100644
index 0000000..a7f28a9
--- /dev/null
+++ b/hercules/templates/item.tpl
@@ -0,0 +1,7 @@
+ <item id="{0}"
+ name="{1}"
+ description="{1}"
+{3} image="{4}"
+ type="{5}">
+ <sprite>{6}</sprite>
+ </item>
diff --git a/hercules/templates/item_db.tpl b/hercules/templates/item_db.tpl
new file mode 100644
index 0000000..198343d
--- /dev/null
+++ b/hercules/templates/item_db.tpl
@@ -0,0 +1,63 @@
+item_db: (
+// Items Database
+//
+/******************************************************************************
+ ************* Entry structure ************************************************
+ ******************************************************************************
+{
+ // =================== Mandatory fields ===============================
+ Id: ID (int)
+ AegisName: "Aegis_Name" (string)
+ Name: "Item Name" (string)
+ // =================== Optional fields ================================
+ Type: Item Type (int, defaults to 3 = etc item)
+ Buy: Buy Price (int, defaults to Sell * 2)
+ Sell: Sell Price (int, defaults to Buy / 2)
+ Weight: Item Weight (int, defaults to 0)
+ Atk: Attack (int, defaults to 0)
+ Matk: Magical Attack (int, defaults to 0, ignored in pre-re)
+ Def: Defense (int, defaults to 0)
+ Range: Attack Range (int, defaults to 0)
+ Slots: Slots (int, defaults to 0)
+ Job: Job mask (int, defaults to all jobs = 0xFFFFFFFF)
+ Upper: Upper mask (int, defaults to any = 0x3f)
+ Gender: Gender (int, defaults to both = 2)
+ Loc: Equip location (int, required value for equipment)
+ WeaponLv: Weapon Level (int, defaults to 0)
+ EquipLv: Equip required level (int, defaults to 0)
+ EquipLv: [min, max] (alternative syntax with min / max level)
+ Refine: Refineable (boolean, defaults to true)
+ View: View ID (int, defaults to 0)
+ BindOnEquip: true/false (boolean, defaults to false)
+ BuyingStore: true/false (boolean, defaults to false)
+ Delay: Delay to use item (int, defaults to 0)
+ FloorLifeTime: Delay to remove item from ground (int, default flooritem_lifetime)
+ KeepAfterUse: true/false (boolean, defaults to false)
+ Trade: { (defaults to no restrictions)
+ override: GroupID (int, defaults to 100)
+ nodrop: true/false (boolean, defaults to false)
+ notrade: true/false (boolean, defaults to false)
+ partneroverride: true/false (boolean, defaults to false)
+ noselltonpc: true/false (boolean, defaults to false)
+ nocart: true/false (boolean, defaults to false)
+ nostorage: true/false (boolean, defaults to false)
+ nogstorage: true/false (boolean, defaults to false)
+ nomail: true/false (boolean, defaults to false)
+ noauction: true/false (boolean, defaults to false)
+ nodelonuse: true/false (boolean, defaults to false)
+ }
+ Nouse: { (defaults to no restrictions)
+ override: GroupID (int, defaults to 100)
+ sitting: true/false (boolean, defaults to false)
+ }
+ Stack: [amount, type] (int, defaults to 0)
+ Sprite: SpriteID (int, defaults to 0)
+ Script: <"
+ Script
+ (it can be multi-line)
+ ">
+ OnEquipScript: <" OnEquip Script (can also be multi-line) ">
+ OnUnequipScript: <" OnUnequip Script (can also be multi-line) ">
+},
+******************************************************************************/
+
diff --git a/hercules/templates/items.xml b/hercules/templates/items.xml
new file mode 100644
index 0000000..07d1cb7
--- /dev/null
+++ b/hercules/templates/items.xml
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="utf-8"?>
+<?xml-stylesheet type="text/xsl" href="items.xsl"?>
+<!-- Authors: 4144, Piman, Reid
+Copyright (C) 2010-2013 Evol Online -->
+
+<items>
+ <its:rules xmlns:its="http://www.w3.org/2005/11/its" version="1.0">
+ <its:translateRule selector="//item/@name" translate="yes"/>
+ <its:translateRule selector="//item/@description" translate="yes"/>
+ <its:translateRule selector="//item/@useButton" translate="yes"/>
+ <its:translateRule selector="//item/@useButton2" translate="yes"/>
+ <its:translateRule selector="//item/@effect" translate="yes"/>
+ <its:translateRule selector="//*" translate="no"/>
+ </its:rules>
+
+ <!-- Hairstyles -->
+ <item id="-1" type="hairsprite" name="Bald">
+ <sprite>hairstyles/hairstyle01.xml</sprite>
+ </item>
+ <item id="-2" type="hairsprite" name="Bowl Cut" colors="hairS">
+ <sprite>hairstyles/hairstyle02.xml</sprite>
+ </item>
+ <item id="-3" type="hairsprite" name="Combed Back" colors="hairS">
+ <sprite>hairstyles/hairstyle03.xml</sprite>
+ </item>
+ <item id="-4" type="hairsprite" name="Emo" colors="hairS">
+ <sprite>hairstyles/hairstyle04.xml</sprite>
+ </item>
+ <item id="-5" type="hairsprite" name="Mohawk" colors="hairS">
+ <sprite>hairstyles/hairstyle05.xml</sprite>
+ </item>
+ <item id="-6" type="hairsprite" name="Pompadour" colors="hairS">
+ <sprite>hairstyles/hairstyle06.xml</sprite>
+ </item>
+ <item id="-7" type="hairsprite" name="Center Parting" colors="hairS">
+ <sprite>hairstyles/hairstyle07.xml</sprite>
+ </item>
+ <item id="-8" type="hairsprite" name="Long and Slick" colors="hair">
+ <sprite>hairstyles/hairstyle08.xml</sprite>
+ </item>
+ <item id="-9" type="hairsprite" name="Short and Curly" colors="hair">
+ <sprite>hairstyles/hairstyle09.xml</sprite>
+ </item>
+ <item id="-10" type="hairsprite" name="Pigtails" colors="hair">
+ <sprite>hairstyles/hairstyle10.xml</sprite>
+ </item>
+ <item id="-11" type="hairsprite" name="Long and Curly" colors="hair">
+ <sprite>hairstyles/hairstyle11.xml</sprite>
+ </item>
+ <item id="-12" type="hairsprite" name="Parted" colors="hair">
+ <sprite>hairstyles/hairstyle12.xml</sprite>
+ </item>
+ <item id="-13" type="hairsprite" name="Perky Ponytail" colors="hairS">
+ <sprite>hairstyles/hairstyle13.xml</sprite>
+ </item>
+ <item id="-14" type="hairsprite" name="Wave" colors="hairS">
+ <sprite>hairstyles/hairstyle14.xml</sprite>
+ </item>
+ <item id="-15" type="hairsprite" name="Mane" colors="hairS">
+ <sprite>hairstyles/hairstyle15.xml</sprite>
+ </item>
+ <item id="-16" type="hairsprite" name="Bun" colors="hairS">
+ <sprite>hairstyles/hairstyle16.xml</sprite>
+ </item>
+ <item id="-17" type="hairsprite" name="Wavy" colors="hairS">
+ <sprite>hairstyles/hairstyle17.xml</sprite>
+ </item>
+ <item id="-18" type="hairsprite" name="Bunches" colors="hairS">
+ <sprite>hairstyles/hairstyle18.xml</sprite>
+ </item>
+ <item id="-19" type="hairsprite" name="Long Ponytail" colors="hairS">
+ <sprite>hairstyles/hairstyle19.xml</sprite>
+ </item>
+ <item id="-20" type="hairsprite" name="Infinitely Long" colors="hair">
+ <sprite>hairstyles/hairstyle20.xml</sprite>
+ </item>
+ <item id="-21" type="hairsprite" name="Choppy" colors="hair">
+ <sprite>hairstyles/hairstyle21.xml</sprite>
+ </item>
+ <item id="-22" type="hairsprite" name="Wild" colors="hair">
+ <sprite>hairstyles/hairstyle22.xml</sprite>
+ </item>
+ <item id="-23" type="hairsprite" name="Punk" colors="hairS">
+ <sprite>hairstyles/hairstyle23.xml</sprite>
+ </item>
+ <item id="-24" type="hairsprite" name="Imperial" colors="hairS">
+ <sprite>hairstyles/hairstyle24.xml</sprite>
+ </item>
+ <item id="-25" type="hairsprite" name="Side Strand" colors="hairS">
+ <sprite>hairstyles/hairstyle25.xml</sprite>
+ </item>
+ <item id="-26" type="hairsprite" name="Messy" colors="hairS">
+ <sprite>hairstyles/hairstyle26.xml</sprite>
+ </item>
+ <item id="-27" type="hairsprite" name="Flat Ponytail" colors="hairS">
+ <sprite>hairstyles/hairstyle27.xml</sprite>
+ </item>
+ <item id="-28" type="hairsprite" name="Tapered Nape" colors="hairS">
+ <sprite>hairstyles/hairstyle28.xml</sprite>
+ </item>
+
+ <!-- Races -->
+ <item id="-100" type="racesprite" name="Human">
+ <sprite gender="unisex">races/human-male.xml</sprite>
+ <sprite gender="female">races/human-female.xml</sprite>
+ <sound event="hit">weapons/barehands/hit1.ogg</sound>
+ <sound event="miss">weapons/barehands/miss1.ogg</sound>
+ </item>
+ <item id="-101" type="racesprite" name="Ukar">
+ <sprite gender="unisex">races/ukar-male.xml</sprite>
+ <sprite gender="female">races/ukar-female.xml</sprite>
+ <sound event="hit">weapons/barehands/hit1.ogg</sound>
+ <sound event="miss">weapons/barehands/miss1.ogg</sound>
+ </item>
+ <item id="-102" type="racesprite" name="Demon">
+ <sprite gender="unisex">races/demon-male.xml</sprite>
+ <sprite gender="female">races/demon-female.xml</sprite>
+ <sound event="hit">weapons/barehands/hit1.ogg</sound>
+ <sound event="miss">weapons/barehands/miss1.ogg</sound>
+ </item>
+ <item id="-103" type="racesprite" name="Elven">
+ <sprite gender="unisex">races/elven-male.xml</sprite>
+ <sprite gender="female">races/elven-female.xml</sprite>
+ <sound event="hit">weapons/barehands/hit1.ogg</sound>
+ <sound event="miss">weapons/barehands/miss1.ogg</sound>
+ </item>
+ <item id="-104" type="racesprite" name="Orc">
+ <sprite gender="unisex">races/orc-male.xml</sprite>
+ <sprite gender="female">races/orc-female.xml</sprite>
+ <sound event="hit">weapons/barehands/hit1.ogg</sound>
+ <sound event="miss">weapons/barehands/miss1.ogg</sound>
+ </item>
+ <item id="-105" type="racesprite" name="Raijin">
+ <sprite gender="unisex">races/raijin-male.xml</sprite>
+ <sprite gender="female">races/raijin-female.xml</sprite>
+ <sound event="hit">weapons/barehands/hit1.ogg</sound>
+ <sound event="miss">weapons/barehands/miss1.ogg</sound>
+ </item>
+ <item id="-106" type="racesprite" name="Tritan">
+ <sprite gender="unisex">races/tritan-male.xml</sprite>
+ <sprite gender="female">races/tritan-female.xml</sprite>
+ <sound event="hit">weapons/barehands/hit1.ogg</sound>
+ <sound event="miss">weapons/barehands/miss1.ogg</sound>
+ </item>
+
+ <!-- Items -->
+{0}
+</items>
diff --git a/hercules/templates/login.sql b/hercules/templates/login.sql
new file mode 100644
index 0000000..e64de4a
--- /dev/null
+++ b/hercules/templates/login.sql
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS `login`;
+
+CREATE TABLE IF NOT EXISTS `login` (
+ `account_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `userid` VARCHAR(23) NOT NULL DEFAULT '',
+ `user_pass` VARCHAR(32) NOT NULL DEFAULT '',
+ `sex` ENUM('M','F','S') NOT NULL DEFAULT 'M',
+ `email` VARCHAR(39) NOT NULL DEFAULT '',
+ `group_id` TINYINT(3) NOT NULL DEFAULT '0',
+ `state` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `unban_time` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `expiration_time` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `logincount` MEDIUMINT(9) UNSIGNED NOT NULL DEFAULT '0',
+ `lastlogin` DATETIME NULL,
+ `last_ip` VARCHAR(100) NOT NULL DEFAULT '',
+ `birthdate` DATE NULL,
+ `character_slots` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
+ `pincode` VARCHAR(4) NOT NULL DEFAULT '',
+ `pincode_change` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ PRIMARY KEY (`account_id`),
+ KEY `name` (`userid`)
+) ENGINE=InnoDB AUTO_INCREMENT=2000000;
+
+INSERT INTO `login` (`account_id`, `userid`, `user_pass`, `sex`, `email`) VALUES ('1', 's1', 'p1', 'S','athena@athena.com');
+
diff --git a/hercules/templates/mercenaries.xml b/hercules/templates/mercenaries.xml
new file mode 100644
index 0000000..cfc801c
--- /dev/null
+++ b/hercules/templates/mercenaries.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Authors: 4144
+Copyright (C) 2014 Evol Online -->
+<mercenaries>
+{0}
+</mercenaries>
diff --git a/hercules/templates/mercenary.tpl b/hercules/templates/mercenary.tpl
new file mode 100644
index 0000000..c371659
--- /dev/null
+++ b/hercules/templates/mercenary.tpl
@@ -0,0 +1,3 @@
+ <mercenary id="{id}" name="{name}">
+ {sprite}
+ </mercenary>
diff --git a/hercules/templates/mob_db.tpl b/hercules/templates/mob_db.tpl
new file mode 100644
index 0000000..ecf4a27
--- /dev/null
+++ b/hercules/templates/mob_db.tpl
@@ -0,0 +1,101 @@
+//================= Hercules Database =====================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//================= License ===============================================
+//= This file is part of Hercules.
+//= http://herc.ws - http://github.com/HerculesWS/Hercules
+//=
+//= Copyright (C) 2015 Hercules Dev Team
+//=
+//= Hercules is free software: you can redistribute it and/or modify
+//= it under the terms of the GNU General Public License as published by
+//= the Free Software Foundation, either version 3 of the License, or
+//= (at your option) any later version.
+//=
+//= This program is distributed in the hope that it will be useful,
+//= but WITHOUT ANY WARRANTY; without even the implied warranty of
+//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//= GNU General Public License for more details.
+//=
+//= You should have received a copy of the GNU General Public License
+//= along with this program. If not, see <http://www.gnu.org/licenses/>.
+//=========================================================================
+//= Mobs Database
+//=========================================================================
+
+mob_db: (
+// Mobs Database
+//
+/******************************************************************************
+ ************* Entry structure ************************************************
+ ******************************************************************************
+{
+ // =================== Mandatory fields ===============================
+ Id: ID (int)
+ SpriteName: "SPRITE_NAME" (string)
+ Name: "Mob name" (string)
+ // =================== Optional fields ================================
+ JName: "Mob name" (string)
+ Lv: level (int, defaults to 1)
+ Hp: health (int, defaults to 1)
+ Sp: mana (int, defaults to 0)
+ Exp: basic experience (int, defaults to 0)
+ JExp: job experience (int, defaults to 0)
+ AttackRange: attack range (int, defaults to 1)
+ Attack: [attack1, attack2] (int, defaults to 0)
+ Def: defence (int, defaults to 0)
+ Mdef: magic defence (int, defaults to 0)
+ Stats: {
+ Str: strength (int, defaults to 0)
+ Agi: agility (int, defaults to 0)
+ Vit: vitality (int, defaults to 0)
+ Int: intelligence (int, defaults to 0)
+ Dex: dexterity (int, defaults to 0)
+ Luk: luck (int, defaults to 0)
+ }
+ ViewRange: view range (int, defaults to 1)
+ ChaseRange: chase range (int, defaults to 1)
+ Size: size (int, defaults to 1)
+ Race: race (int, defaults to 0)
+ Element: (type, level)
+ Mode: {
+ CanMove: true/false (bool, defaults to false)
+ Looter: true/false (bool, defaults to false)
+ Aggressive: true/false (bool, defaults to false)
+ Assist: true/false (bool, defaults to false)
+ CastSensorIdle:true/false (bool, defaults to false)
+ Boss: true/false (bool, defaults to false)
+ Plant: true/false (bool, defaults to false)
+ CanAttack: true/false (bool, defaults to false)
+ Detector: true/false (bool, defaults to false)
+ CastSensorChase: true/false (bool, defaults to false)
+ ChangeChase: true/false (bool, defaults to false)
+ Angry: true/false (bool, defaults to false)
+ ChangeTargetMelee: true/false (bool, defaults to false)
+ ChangeTargetChase: true/false (bool, defaults to false)
+ TargetWeak: true/false (bool, defaults to false)
+ NoKnockback: true/false (bool, defaults to false)
+ SurviveWithoutMaster: true/false (bool, defaults to false)
+ }
+ MoveSpeed: move speed (int, defaults to 0)
+ WalkMask: walk mask (int, defaults to 0)
+ AttackDelay: attack delay (int, defaults to 4000)
+ AttackMotion: attack motion (int, defaults to 2000)
+ DamageMotion: damage motion (int, defaults to 0)
+ MvpExp: mvp experience (int, defaults to 0)
+ MvpDrops: {
+ AegisName: chance (string: int)
+ ...
+ }
+ Drops: {
+ AegisName: chance (string: int)
+ ...
+ }
+
+},
+******************************************************************************/
+
diff --git a/hercules/templates/mob_skill_db.tpl b/hercules/templates/mob_skill_db.tpl
new file mode 100644
index 0000000..d8cc0a4
--- /dev/null
+++ b/hercules/templates/mob_skill_db.tpl
@@ -0,0 +1,56 @@
+// Mob Skill Database
+// Based on Aegis Episode 14.2
+//
+// Structure of Database:
+// MobID,Dummy value (info only),State,SkillID,SkillLv,Rate,CastTime,Delay,Cancelable,Target,Condition type,Condition value,val1,val2,val3,val4,val5,Emotion,Chat
+//
+// RATE: the chance of the skill being casted when the condition is fulfilled (10000 = 100%).
+// DELAY: the time (in milliseconds) before attempting to recast the same skill.
+//
+// STATE:
+// any (except dead) / idle (in standby) / walk (in movement) / dead (on killed) /
+// loot /attack / angry (like attack, except player has not attacked mob yet) /
+// chase (following target, after being attacked) / follow (following target,
+// without being attacked) / anytarget (attack+angry+chase+follow)
+//
+// TARGET:
+// target (current target) / self / friend / master / randomtarget (any enemy within skill's range)
+//
+// The following are for ground-skills, a random target tile is selected from the specified area:
+// around1 (3x3 area around self) / around2 (5x5 area around self) /
+// around3 (7x7 area around self) / around4 (9x9 area around self) /
+// around5 (3x3 area around target) / around6 (5x5 area around target) /
+// around7 (7x7 area around target) / around8 (9x9 area around target) /
+// around = around4
+//
+// CONDITION:
+// always Unconditional (no condition value).
+// onspawn When mob spawns/respawns (no condition value).
+// myhpltmaxrate When mob's HP drops to the specified %.
+// myhpinrate When mob's HP is in a certain % range (condition value = lower bound, val1 = upper bound).
+// mystatuson If mob has the specified abnormality in status.
+// mystatusoff If mob has ended the specified abnormality in status.
+// friendhpltmaxrate When mob's friend's HP drops to the specified %.
+// friendhpinrate When mob's friend's HP is in a certain % range (condition value = lower bound, val1 = upper bound).
+// friendstatuson If friend has the specified abnormality in status.
+// friendstatusoff If friend has ended the specified abnormality in status.
+// attackpcgt When attack PCs become greater than specified number.
+// attackpcge When attack PCs become greater than or equal to the specified number.
+// slavelt When number of slaves is less than the original specified number.
+// slavele When number of slaves is less than or equal to the original specified number.
+// closedattacked When close range melee attacked (no condition value).
+// longrangeattacked When long range attacked, ex. bows, guns, ranged skills (no condition value).
+// skillused When the specified skill is used on the mob.
+// afterskill After mob casts the specified skill.
+// casttargeted When a target is in cast range (no condition value).
+// rudeattacked When mob is rude attacked (no condition value).
+//
+// Status abnormalities specified through the statuson/statusoff system:
+// anybad (any type of state change) / stone / freeze / stun / sleep /
+// poison / curse / silence / confusion / blind / hiding / sight (unhidden)
+//
+// Note: if a negative MobID is provided, the skill will be treated as 'global':
+// -1: added for all boss types.
+// -2: added for all normal types.
+// -4: added for all mobs.
+
diff --git a/hercules/templates/monster.tpl b/hercules/templates/monster.tpl
new file mode 100644
index 0000000..3b6bf4d
--- /dev/null
+++ b/hercules/templates/monster.tpl
@@ -0,0 +1,3 @@
+ <monster id="{0}" name="{1}">
+ {2}
+ </monster>
diff --git a/hercules/templates/monsters.xml b/hercules/templates/monsters.xml
new file mode 100644
index 0000000..9f8ffa0
--- /dev/null
+++ b/hercules/templates/monsters.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010-2014 Evol Online -->
+
+<monsters>
+ <its:rules xmlns:its="http://www.w3.org/2005/11/its" version="1.0">
+ <its:translateRule selector="//monster/@name" translate="yes"/>
+ <its:translateRule selector="//*" translate="no"/>
+ </its:rules>
+
+{0}
+</monsters>
diff --git a/hercules/templates/npc.tpl b/hercules/templates/npc.tpl
new file mode 100644
index 0000000..a201d64
--- /dev/null
+++ b/hercules/templates/npc.tpl
@@ -0,0 +1,3 @@
+ <npc id="{id}">
+ {sprite}
+ </npc>
diff --git a/hercules/templates/npcs.xml b/hercules/templates/npcs.xml
new file mode 100644
index 0000000..72a0a4f
--- /dev/null
+++ b/hercules/templates/npcs.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010-2014 Evol Online -->
+
+<npcs>
+ <its:rules xmlns:its="http://www.w3.org/2005/11/its" version="1.0">
+ <its:translateRule selector="//monster/@name" translate="yes"/>
+ <its:translateRule selector="//*" translate="no"/>
+ </its:rules>
+
+{0}
+</npcs>
diff --git a/hercules/templates/party.sql b/hercules/templates/party.sql
new file mode 100644
index 0000000..0beb439
--- /dev/null
+++ b/hercules/templates/party.sql
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS `party`;
+
+CREATE TABLE IF NOT EXISTS `party` (
+ `party_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `name` VARCHAR(24) NOT NULL DEFAULT '',
+ `exp` TINYINT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `item` TINYINT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `leader_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `leader_char` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ PRIMARY KEY (`party_id`)
+) ENGINE=InnoDB;
+
diff --git a/hercules/templates/pet.tpl b/hercules/templates/pet.tpl
new file mode 100644
index 0000000..08d489c
--- /dev/null
+++ b/hercules/templates/pet.tpl
@@ -0,0 +1,3 @@
+ <pet id="{id}" name="{name}">
+ {sprite}
+ </pet>
diff --git a/hercules/templates/pets.xml b/hercules/templates/pets.xml
new file mode 100644
index 0000000..6f838cf
--- /dev/null
+++ b/hercules/templates/pets.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Authors: 4144
+Copyright (C) 2014 Evol Online -->
+<pets>
+{0}
+</pets>
diff --git a/hercules/templates/quest.tpl b/hercules/templates/quest.tpl
new file mode 100644
index 0000000..25615f9
--- /dev/null
+++ b/hercules/templates/quest.tpl
@@ -0,0 +1,5 @@
+ <var id="{0}">
+ <quest name="{1}" group="hercules" incomplete="1" complete="2">
+ <name>{2}</name>
+ </quest>
+ </var>
diff --git a/hercules/templates/quest_db.tpl b/hercules/templates/quest_db.tpl
new file mode 100644
index 0000000..e67b40f
--- /dev/null
+++ b/hercules/templates/quest_db.tpl
@@ -0,0 +1,5 @@
+// Quest Database
+//
+// Structure of Database:
+// Quest ID,Time Limit,Target1,Val1,Target2,Val2,Target3,Val3,Quest Title
+
diff --git a/hercules/templates/quests.xml b/hercules/templates/quests.xml
new file mode 100644
index 0000000..c2828f9
--- /dev/null
+++ b/hercules/templates/quests.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Authors: 4144
+Copyright (C) 2014 Evol Online -->
+<quests>
+{0}
+</quests>
diff --git a/hercules/templates/skill.sql b/hercules/templates/skill.sql
new file mode 100644
index 0000000..2ee3002
--- /dev/null
+++ b/hercules/templates/skill.sql
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS `skill`;
+
+CREATE TABLE IF NOT EXISTS `skill` (
+ `char_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `id` SMALLINT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `lv` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0',
+ `flag` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
+ PRIMARY KEY (`char_id`,`id`)
+) ENGINE=InnoDB;
+
diff --git a/hercules/templates/skill.tpl b/hercules/templates/skill.tpl
new file mode 100644
index 0000000..53dc952
--- /dev/null
+++ b/hercules/templates/skill.tpl
@@ -0,0 +1 @@
+ <skill id="{0}" name="{1}" description="{2}"/>
diff --git a/hercules/templates/skills.xml b/hercules/templates/skills.xml
new file mode 100644
index 0000000..c747ed7
--- /dev/null
+++ b/hercules/templates/skills.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Authors: 4144
+Copyright (C) 2014 Evol Online -->
+<skills>
+ <its:rules xmlns:its="http://www.w3.org/2005/11/its" version="1.0">
+ <its:translateRule selector="//set/@name" translate="yes"/>
+ <its:translateRule selector="//set/skill/@name" translate="yes"/>
+ <its:translateRule selector="//set/skill/@description" translate="yes"/>
+ <its:translateRule selector="//*" translate="no"/>
+ </its:rules>
+
+ <set name="Basic">
+{0}
+ </set>
+</skills>
diff --git a/hercules/templates/sprite.xml b/hercules/templates/sprite.xml
new file mode 100644
index 0000000..7443ba7
--- /dev/null
+++ b/hercules/templates/sprite.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Author: 4144
+Copyright (C) 2015 Evol Online -->
+<sprite>
+ <imageset name="sprite"
+ src="{src}"
+ width="{width}"
+ height="{height}"/>
+
+ <action name="stand" imageset="sprite">
+{stand}</action>
+
+ <action name="walk" imageset="sprite">
+{walk}</action>
+
+ <action name="attack" imageset="sprite">
+{attack}</action>
+
+ <action name="dead" imageset="sprite">
+{dead}</action>
+</sprite>
diff --git a/hercules/templates/storage.sql b/hercules/templates/storage.sql
new file mode 100644
index 0000000..027d291
--- /dev/null
+++ b/hercules/templates/storage.sql
@@ -0,0 +1,22 @@
+DROP TABLE IF EXISTS `storage`;
+
+CREATE TABLE IF NOT EXISTS `storage` (
+ `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `account_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `nameid` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `amount` SMALLINT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `equip` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `identify` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',
+ `refine` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
+ `attribute` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0',
+ `card0` SMALLINT(11) NOT NULL DEFAULT '0',
+ `card1` SMALLINT(11) NOT NULL DEFAULT '0',
+ `card2` SMALLINT(11) NOT NULL DEFAULT '0',
+ `card3` SMALLINT(11) NOT NULL DEFAULT '0',
+ `expire_time` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `bound` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
+ `unique_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`),
+ KEY `account_id` (`account_id`)
+) ENGINE=InnoDB;
+
diff --git a/hercules/templates/template.tmx b/hercules/templates/template.tmx
new file mode 100644
index 0000000..ec9b214
--- /dev/null
+++ b/hercules/templates/template.tmx
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE map SYSTEM "http://mapeditor.org/dtd/1.0/map.dtd">
+<map version="1.0" orientation="orthogonal" width="{0}" height="{1}" tilewidth="32" tileheight="32">
+ <tileset firstgid="1" name="tiles" tilewidth="32" tileheight="32">
+ <image source="../graphics/tilesets/tileset.png" width="64" height="32"/>
+ </tileset>
+ <tileset firstgid="5" name="Collision" tilewidth="32" tileheight="32">
+ <image source="../graphics/tilesets/collision.png" width="64" height="32"/>
+ </tileset>
+ <layer name="ground" width="{0}" height="{1}">
+ <data encoding="csv">
+{2}
+</data>
+ </layer>
+ <layer name="Fringe" width="{0}" height="{1}">
+ <data encoding="csv">
+{4}
+</data>
+ </layer>
+ <layer name="Collision" width="{0}" height="{1}">
+ <data encoding="csv">
+{3}
+</data>
+ </layer>
+</map>
diff --git a/hercules/templates/tileset.png b/hercules/templates/tileset.png
new file mode 100644
index 0000000..f36ccbb
--- /dev/null
+++ b/hercules/templates/tileset.png
Binary files differ
diff --git a/hercules/tmx_converter.py b/hercules/tmx_converter.py
new file mode 100755
index 0000000..96a37d9
--- /dev/null
+++ b/hercules/tmx_converter.py
@@ -0,0 +1,659 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+
+## TMW2 Script
+## Modified by Jesusalva for Moubootaur Legends
+###################################################
+
+## tmx_converter.py - Extract walkmap, warp, and spawn information from maps.
+##
+## Copyright © 2012 Ben Longbons <b.r.longbons@gmail.com>
+## Copyright © 2016-2017 The Mana World Developers
+##
+## This file is part of The Mana World
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+from __future__ import print_function
+
+import sys
+import os
+import posixpath
+import xml.sax
+import traceback
+
+dump_all = False # wall of text
+check_mobs = False # mob_db.txt
+heigherror=True
+fatalError=False
+
+# lower case versions of everything except 'spawn' and 'warp'
+other_object_types = set([
+ 'particle_effect',
+ 'npc', # not interpreted by client
+ 'script', # for ManaServ
+ 'fixme', # flag for things that didn't have a type before
+ 'music',
+])
+
+# Somebody has put ManaServ fields in our data!
+other_spawn_fields = (
+ 'spawn_rate',
+)
+other_warp_fields = (
+)
+
+TILESIZE = 32
+SEPARATOR = ''
+MESSAGE = 'This file is generated automatically. All manually added changes will be removed when running the Converter.'
+CLIENT_MAPS = 'maps'
+SERVER_WLK = 'data'
+SERVER_NPCS = 'npc'
+MOB_DB_CONF = 'db/re/mob_db.conf'
+MAP_CONF = 'conf/map/maps.conf'
+MAP_DB_CONF = 'db/map_index.txt'
+NPC_MOBS = '_mobs.txt'
+NPC_CONFIG = '_config.txt'
+NPC_WARPS = '_warps.txt'
+NPC_IMPORTS = '_import.txt'
+NPC_MASTER_IMPORTS = NPC_IMPORTS
+
+def ifte(ifs, thens, elses):
+ if ifs:
+ return thens
+ else:
+ return elses
+
+class State(object):
+ pass
+State.INITIAL = State()
+State.LAYER = State()
+State.DATA = State()
+State.FINAL = State()
+
+class Object(object):
+ __slots__ = (
+ 'name',
+ 'x', 'y',
+ 'w', 'h',
+ )
+class Mob(Object):
+ __slots__ = (
+ 'monster_id',
+ 'max_beings',
+ 'spawn',
+ 'death',
+ 'script',
+ ) + other_spawn_fields
+ def __init__(self):
+ self.max_beings = 1
+ self.spawn = 0
+ self.death = 0
+ self.script = ''
+
+class Save(Object):
+ __slots__ = (
+ 'inn',
+ )
+
+class Warp(Object):
+ __slots__ = (
+ 'dest_map',
+ 'dest_x',
+ 'dest_y',
+ 'npc_id',
+ 'trigger_x',
+ 'trigger_y',
+ 'notes',
+ ) + other_warp_fields
+ def __init__(self):
+ self.npc_id = 'WARP'
+
+# TMW2 CUSTOM OBJECTS
+####################################
+class Slide(Object):
+ __slots__ = (
+ 'dest_x',
+ 'dest_y',
+ 'npc_id',
+ 'trigger_x',
+ 'trigger_y',
+ 'notes',
+ ) + other_warp_fields
+ def __init__(self):
+ self.npc_id = 'SLIDE'
+
+class DynCollision(Object):
+ __slots__ = (
+ 'colid',
+ 'enabled',
+ )
+ def __init__(self):
+ self.colid = 1
+ self.enabled = True
+
+class DungeonSwitch(Object):
+ __slots__ = (
+ 'enabled',
+ 'distance',
+ 'callfunc',
+ 'doevent',
+ 'args',
+ )
+ def __init__(self):
+ self.enabled = False
+ self.distance = 2
+ self.callfunc = ''
+ self.doevent = ''
+ self.args = ''
+
+class FunctionTrigger(Object):
+ __slots__ = (
+ 'callfunc',
+ 'doevent',
+ 'args',
+ )
+ def __init__(self):
+ self.callfunc = ''
+ self.doevent = ''
+ self.args = ''
+
+class Trap(Object):
+ __slots__ = (
+ 'disarmtime',
+ 'stuntime',
+ 'damage',
+ 'target',
+ )
+ def __init__(self):
+ self.disarmtime = 15
+ self.stuntime = 3
+ self.damage = 80
+ self.target = 3
+
+class TreasureChest(Object):
+ __slots__ = (
+ 'distance',
+ )
+ def __init__(self):
+ self.distance = 2
+
+####################################
+class ContentHandler(xml.sax.ContentHandler):
+ __slots__ = (
+ 'locator', # keeps track of location in document
+ 'state', # state of height info
+ 'tilesets', # first gid of each tileset
+ 'buffer', # characters within a section
+ 'encoding', # encoding of layer data
+ 'compression', # compression of layer data
+ 'width', # width of the height layer
+ 'height', # height of the height layer
+ 'firstgid', # first gid of height layer
+ 'heightmap',# height map
+ 'base', # base name of current map
+ 'npc_dir', # world/map/npc/<base>
+ 'mobs', # open file to _mobs.txt
+ 'warps', # open file to _warps.txt
+ 'imports', # open file to _import.txt
+ 'name', # name property of the current map
+ 'object', # stores properties of the latest <object> tag
+ 'mob_ids', # set of all mob types that spawn here
+ )
+ def __init__(self, npc_dir, mobs, confs, warps, imports):
+ xml.sax.ContentHandler.__init__(self)
+ self.locator = None
+ self.state = State.INITIAL
+ self.tilesets = set([0]) # consider the null tile as its own tileset
+ self.buffer = bytearray()
+ self.encoding = None
+ self.compression = None
+ self.width = None
+ self.height = None
+ self.firstgid = 0
+ self.heightmap = ''
+ self.base = posixpath.basename(npc_dir)
+ self.npc_dir = npc_dir
+ self.mobs = mobs
+ self.confs = confs
+ self.warps = warps
+ self.imports = imports
+ self.object = None
+ self.mob_ids = set()
+ self.mob_cnt = False
+ self.save_cnt = False
+ self.warp_cnt = False
+
+ def setDocumentLocator(self, loc):
+ self.locator = loc
+
+ # this method randomly cuts in the middle of a line; thus funky logic
+ def characters(self, s):
+ if not s.strip():
+ return
+ if self.state is State.DATA:
+ self.buffer += s.encode('ascii')
+
+ def startDocument(self):
+ pass
+
+ def startElement(self, name, attr):
+ global heigherror
+ if dump_all:
+ attrs = ' '.join('%s="%s"' % (k,v) for k,v in attr.items())
+ if attrs:
+ print('<%s %s>' % (name, attrs))
+ else:
+ print('<%s>' % name)
+
+ if self.state is State.INITIAL:
+ if name == u'property' and attr[u'name'].lower() == u'name':
+ self.name = attr[u'value']
+ self.mobs.write('// %s\n' % MESSAGE)
+ self.mobs.write('// Map %s: %s mobs\n' % (self.base, self.name))
+ self.confs.write('// %s\n' % MESSAGE)
+ self.confs.write('// Map %s: %s conf\n' % (self.base, self.name))
+ self.warps.write('// %s\n' % MESSAGE)
+ self.warps.write('// Map %s: %s warps\n' % (self.base, self.name))
+
+ if name == u'tileset':
+ self.tilesets.add(int(attr[u'firstgid']))
+ if 'name' in attr.__dict__['_attrs'].keys():
+ if attr[u'name'] == u'Height Numbers':
+ self.firstgid = int(attr[u'firstgid'])
+
+ if name == u'layer' and attr[u'name'].lower().startswith(u'height'):
+ self.width = int(attr[u'width'])
+ self.height = int(attr[u'height'])
+ self.state = State.LAYER
+ heigherror=False
+ # Map width must be enough to fill the largest widescreen on market
+ if (self.width < 1920/TILESIZE):
+ print('Bad map width: %d (min. %d)' % (self.width, 1920/TILESIZE))
+ elif self.state is State.LAYER:
+ if name == u'data':
+ if attr.get(u'encoding','') not in (u'', u'csv'):
+ print('Bad encoding:', attr.get(u'encoding',''))
+ return
+ self.encoding = attr.get(u'encoding','')
+ if attr.get(u'compression','') not in (u'', u'none'):
+ print('Bad compression:', attr.get(u'compression',''))
+ return
+ self.compression = attr.get(u'compression','')
+ self.state = State.DATA
+ elif self.state is State.FINAL:
+ if name == u'object':
+ obj_type = attr[u'type'].lower()
+ x = int(int(attr[u'x']) / TILESIZE);
+ y = int(int(attr[u'y']) / TILESIZE);
+ w = int(int(attr.get(u'width', 0)) / TILESIZE);
+ h = int(int(attr.get(u'height', 0)) / TILESIZE);
+ # I'm not sure exactly what the w/h shrinking is for,
+ # I just copied it out of the old converter.
+ # I know that the x += w/2 is to get centers, though.
+ if obj_type == 'spawn':
+ self.object = Mob()
+ w = int((w - 1) / 2)
+ h = int((h - 1) / 2)
+ if w < 0:
+ w = 0
+ else:
+ x += w
+ if h < 0:
+ h = 0
+ else:
+ y += h
+ elif obj_type == 'save':
+ self.object = Save()
+ x += w/2
+ y += h/2
+ w -= 2
+ h -= 2
+ elif obj_type == 'warp':
+ self.object = Warp()
+ x += w/2
+ y += h/2
+ w -= 1
+ h -= 1
+ ### TMW2 INSTANCES
+ ##########################################################
+ elif obj_type == 'slide':
+ self.object = Slide()
+ x += w/2
+ y += h/2
+ w -= 1
+ h -= 1
+ elif obj_type == 'dyncollision':
+ self.object = DynCollision()
+ w -= 1
+ h -= 1
+ elif obj_type == 'switch':
+ self.object = DungeonSwitch()
+ x += w/2
+ y += h/2
+ w -= 1
+ h -= 1
+ elif obj_type == 'treasure':
+ self.object = TreasureChest()
+ x += w/2
+ y += h/2
+ w -= 1
+ h -= 1
+ elif obj_type == 'function':
+ self.object = FunctionTrigger()
+ x += w/2
+ y += h/2
+ w -= 1
+ h -= 1
+ elif obj_type == 'trap':
+ self.object = Trap()
+ x += w/2
+ y += h/2
+ w -= 1
+ h -= 1
+ else:
+ if obj_type not in other_object_types:
+ print('Unknown object type:', obj_type, file=sys.stderr)
+ self.object = None
+ return
+ obj = self.object
+ obj.x = x
+ obj.y = y
+ obj.w = w
+ obj.h = h
+ obj.name = attr[u'name']
+ elif name == u'property':
+ obj = self.object
+ if obj is None:
+ return
+ key = attr[u'name'].lower()
+ value = attr[u'value']
+ # Not true due to defaulting
+ #assert not hasattr(obj, key)
+ try:
+ value = int(value)
+ except ValueError:
+ pass
+ setattr(obj, key, value)
+
+
+ def add_warp_line(self, line):
+ self.warps.write(line)
+
+ def endElement(self, name):
+ if dump_all:
+ print('</%s>' % name)
+
+ if name == u'object':
+ obj = self.object
+ if isinstance(obj, Mob):
+ mob_id = obj.monster_id
+ if mob_id < 1000:
+ mob_id += 1002
+ if check_mobs:
+ try:
+ name = mob_names[mob_id]
+ except KeyError:
+ print('Warning: unknown mob ID: %d (%s)' % (mob_id, obj.name))
+ else:
+ if name != obj.name:
+ print('Warning: wrong mob name: %s (!= %s)' % (obj.name, name))
+ obj.name = name
+ self.mob_ids.add(mob_id)
+ if obj.script:
+ obj.script = ",%s" % (obj.script)
+ self.mobs.write(
+ SEPARATOR.join([
+ '%s,%d,%d,%d,%d\t' % (self.base, obj.x, obj.y, obj.w, obj.h),
+ 'monster\t',
+ obj.name,
+ '\t%d,%d,%d,%d%s\n' % (mob_id, obj.max_beings, obj.spawn, obj.death, obj.script),
+ ])
+ )
+ self.mob_cnt = True
+ elif isinstance(obj, Save):
+ """
+ obj_name = "%s_%s_%s" % (self.base, obj.x, obj.y)
+ self.confs.write(
+ SEPARATOR.join([
+ '',
+ '%s,%d,%d,0\tscript\t#save_%s\tNPC_SAVE_POINT,{\n' % (self.base, obj.x, obj.y, obj_name),
+ ' savepointparticle .map$, .x, .y, %s;\n close;\n\nOnInit:\n .distance = 2;\n .sex = G_OTHER;\n end;\n}\n' % (obj.inn),
+ ])
+ )
+ self.save_cnt = True
+ """
+ print("[WARNING] Object type \"Save\" is deprecated!")
+ elif isinstance(obj, Warp):
+ if (obj.npc_id == u'WARP'):
+ obj_name = "#%s_%s_%s" % (self.base, obj.x, obj.y)
+ if (obj.dest_map.lower() in ["slide", "self"]):
+ self.warps.write(
+ SEPARATOR.join([
+ '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
+ 'script\t',
+ '%s\tNPC_HIDDEN,%d,%d,{\n\tend;\nOnTouch:\n\tslide %d,%d; end;\n}\n' % (obj_name, obj.w, obj.h, obj.dest_x, obj.dest_y),
+ ])
+ )
+ else:
+ self.warps.write(
+ SEPARATOR.join([
+ '%s,%d,%d,0\t' % (self.base, obj.x, obj.y),
+ 'warp\t',
+ '%s\t%s,%s,%s,%d,%d\n' % (obj_name, obj.w, obj.h, obj.dest_map, obj.dest_x, obj.dest_y),
+ ])
+ )
+ self.warp_cnt = True
+ ### TMW2 INSTANCES
+ ##############################################################
+ elif isinstance(obj, Slide):
+ if (obj.npc_id == u'SLIDE'):
+ obj_name = "#%s_%s_%s" % (self.base, obj.x, obj.y)
+ self.warps.write(
+ SEPARATOR.join([
+ '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
+ 'script\t',
+ '%s\tNPC_HIDDEN,%d,%d,{\n\tend;\nOnTouch:\n\tslide %d,%d; end;\n}\n' % (obj_name, obj.w, obj.h, obj.dest_x, obj.dest_y),
+ ])
+ )
+ self.warp_cnt = True
+ elif (not obj.npc_id == u'SCRIPT'):
+ obj_name = "#%s_%s_%s" % (self.base, obj.x, obj.y)
+ self.warps.write(
+ SEPARATOR.join([
+ '%s,%d,%d,0\tscript\t%s_h\tNPC_HIDDEN,0,0,{\n' % (self.base, obj.x, obj.y, obj_name),
+ 'OnTouch:\n warp "%s", %d, %d;\nclose;\n\nOnUnTouch:\n doevent "%s::OnUnTouch";\n}\n' % (obj.dest_map, obj.dest_x, obj.dest_y, obj_name),
+ '%s,%d,%d,0\tscript\t%s\t%s,%d,%d,{\n close;\nOnTouch:\n doorTouch;\n\nOnUnTouch:\n doorUnTouch;\n\nOnTimer340:\n doorTimer;\n\nOnInit:\n doorInit;\n}\n\n' % (self.base, obj.x, obj.y, obj_name, obj.npc_id, obj.trigger_x, obj.trigger_y),
+ ])
+ )
+ self.warp_cnt = True
+ elif isinstance(obj, DynCollision):
+ obj_name = "%s_%s_%s" % (self.base, obj.x, obj.y)
+ triggere = ifte(obj.enabled, "\nOnInit:", "")
+ triggerd = ifte(obj.enabled, "", "\nOnInit:")
+ self.confs.write(
+ SEPARATOR.join([
+ '\n',
+ '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
+ 'script\t',
+ '#%s\tNPC_HIDDEN,{\n\tend;\nOnDisable:%s\n\tdelcells "%s"; end;\n' % (obj_name, triggerd, obj_name),
+ 'OnEnable:%s\n\tsetcells "%s", %d, %d, %d, %d, %d, "%s";\n}\n' % (triggere, self.base, obj.x, obj.y, obj.x+obj.w, obj.y+obj.h, obj.colid, obj_name),
+ ])
+ )
+ self.save_cnt = True
+ elif isinstance(obj, DungeonSwitch):
+ obj_name = "%s_%s_%s" % (self.base, obj.x, obj.y)
+ status = ifte(obj.enabled, "OFFLINE", "ONLINE")
+ nstats = ifte(obj.enabled, "ONLINE", "OFFLINE")
+ func_name = ifte(obj.callfunc != "", "\tcallfunc \"%s\"%s;\n" % (obj.callfunc,
+ ifte(obj.args != "", ", %s" % obj.args, "")), "")
+ scrp_name = ifte(obj.doevent != "", "\tdoevent \"%s\";\n" % (obj.doevent), "")
+ self.confs.write(
+ SEPARATOR.join([
+ '\n',
+ '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
+ 'script\t',
+ '#%s\tNPC_SWITCH_%s,{\n' % (obj_name, status),
+ '\tif (getnpcclass() == NPC_SWITCH_%s)\n\t\tend;\n' % (nstats),
+ '%s%s' % (func_name, scrp_name),
+ '\tsetnpcdisplay "#%s", NPC_SWITCH_%s;\n\tend;\nOnInit:\n\t.distance=%d;\n}\n' % (obj_name, nstats, obj.distance),
+ ])
+ )
+ self.save_cnt = True
+ elif isinstance(obj, FunctionTrigger):
+ obj_name = "%s_%s_%s" % (self.base, obj.x, obj.y)
+ func_name = ifte(obj.callfunc != "", "\tcallfunc \"%s\"%s;\n" % (obj.callfunc,
+ ifte(obj.args != "", ", %s" % obj.args, "")), "")
+ scrp_name = ifte(obj.doevent != "", "\tdoevent \"%s\";\n" % (obj.doevent), "")
+ self.confs.write(
+ SEPARATOR.join([
+ '\n',
+ '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
+ 'script\t',
+ '#%s\tNPC_HIDDEN,%d,%d,{\n\tend;\n' % (obj_name, obj.w, obj.h),
+ 'OnTouch:\n%s%s\tend;\n}\n' % (func_name, scrp_name),
+ ])
+ )
+ self.save_cnt = True
+ elif isinstance(obj, Trap):
+ obj_name = "%s_%s_%s" % (self.base, obj.x, obj.y)
+ npcid = ifte(obj.disarmtime, "NPC_TRAP", "NPC_TRAP_ONLINE")
+ timer = ifte(obj.disarmtime, "OnTimer%d:\n\tstopnpctimer; setnpctimer 0; setnpcdisplay \"#%s\", NPC_TRAP; end;\n" % (obj.disarmtime*1000, obj_name), "")
+ self.confs.write(
+ SEPARATOR.join([
+ '\n',
+ '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
+ 'script\t',
+ '#%s\t%s,%d,%d,{\n\tmesn strcharinfo(0);\n\tmesq l("Something seems off with that!");\n\tclose;\n' % (obj_name, npcid, obj.w, obj.h),
+ '%s%s' % (ifte(obj.target & 1, "OnTouch:\n", ""), ifte(obj.target & 2, "OnTouchNPC:\n", "")),
+ '\tIronTrap(%d, %d, %d);\n\tend;\n%s}\n' % (obj.damage, obj.disarmtime, obj.stuntime, timer),
+ ])
+ )
+ self.save_cnt = True
+ elif isinstance(obj, TreasureChest):
+ obj_name = "%s_%s_%s" % (self.base, obj.x, obj.y)
+ self.confs.write(
+ SEPARATOR.join([
+ '\n',
+ '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
+ 'script\t',
+ '#%s\tNPC_CHEST,{\n\tTreasureBox();' % (obj_name),
+ '\n\tspecialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing',
+ '\n\tclose;\nOnInit:\n\t.distance=%d;' % (obj.distance),
+ '\n\tend;\n}\n',
+ ])
+ )
+ self.save_cnt = True
+
+ ##############################################################
+
+ if name == u'data':
+ if self.state is State.DATA:
+ if self.encoding == u'csv':
+ for x in self.buffer.split(','):
+ if int(x) > 0:
+ self.heightmap += str((int(x) - int(self.firstgid)) + 1)
+ else:
+ self.heightmap += str(x)
+ self.state = State.FINAL
+
+ def endDocument(self):
+ if not self.mob_cnt:
+ os.remove(posixpath.join(main.this_map_npc_dir, NPC_MOBS))
+ if not self.save_cnt:
+ os.remove(posixpath.join(main.this_map_npc_dir, NPC_CONFIG))
+ if not self.warp_cnt:
+ os.remove(posixpath.join(main.this_map_npc_dir, NPC_WARPS))
+
+ imp_cnt = (len(os.walk(self.npc_dir).next()[2]))
+
+ if imp_cnt > 0:
+ self.imports.write('// Map %s: %s\n' % (self.base, self.name))
+ self.imports.write('// %s\n' % MESSAGE)
+
+ npcs = os.listdir(self.npc_dir)
+ npcs.sort()
+ for x in npcs:
+ if x == NPC_IMPORTS:
+ continue
+ if x.startswith('.'):
+ continue
+ if x.endswith('.txt') or x.endswith('.c'):
+ self.imports.write('"%s",\n' % posixpath.join(SERVER_NPCS, self.base, x))
+ else:
+ os.remove(posixpath.join(main.this_map_npc_dir, NPC_IMPORTS))
+
+def main(argv):
+ global heigherror, fatalError
+ _, client_data, server_data = argv
+ tmx_dir = posixpath.join(client_data, CLIENT_MAPS)
+ npc_dir = posixpath.join(server_data, SERVER_NPCS)
+ if check_mobs:
+ global mob_names
+ mob_names = {}
+ with open(posixpath.join(server_data, MOB_DB_CONF)) as mob_db:
+ for line in mob_db:
+ if not line.strip():
+ continue
+ if line.startswith('//'):
+ continue
+
+ npc_master = []
+ map_basenames = []
+
+ map_conf = open(posixpath.join(server_data,MAP_CONF), 'w')
+ map_db = open(posixpath.join(server_data,MAP_DB_CONF), 'w')
+ map_conf.write("map_removed: (\n)\nmap_list: (\n")
+ map_count = 1
+ for arg in sorted(os.listdir(tmx_dir)):
+ base, ext = posixpath.splitext(arg)
+
+ if ext == '.tmx':
+ map_basenames.append(base)
+ tmx = posixpath.join(tmx_dir, arg)
+ main.this_map_npc_dir = posixpath.join(npc_dir, base)
+ os.path.isdir(main.this_map_npc_dir) or os.mkdir(main.this_map_npc_dir)
+ print('Converting %s' % (tmx))
+ try:
+ with open(posixpath.join(main.this_map_npc_dir, NPC_MOBS), 'w') as mobs:
+ with open(posixpath.join(main.this_map_npc_dir, NPC_CONFIG), 'w') as confs:
+ with open(posixpath.join(main.this_map_npc_dir, NPC_WARPS), 'w') as warps:
+ with open(posixpath.join(main.this_map_npc_dir, NPC_IMPORTS), 'w') as imports:
+ xml.sax.parse(tmx, ContentHandler(main.this_map_npc_dir, mobs, confs, warps, imports))
+ except:
+ traceback.print_exc()
+ print("ERROR: MAP \"%s\" WAS NOT CONVERTED. ERROR FOUND!" % tmx)
+ fatalError=True
+ if os.path.isfile(posixpath.join(main.this_map_npc_dir, NPC_IMPORTS)):
+ npc_master.append('@include "%s"\n' % posixpath.join(SERVER_NPCS, base, NPC_IMPORTS))
+ if heigherror:
+ print("ERROR: Height layer possibly missing")
+ fatalError=True
+ heigherror=True
+
+ map_db.write('%s %d\n' % (arg.split('.')[0], map_count))
+ map_conf.write(' "%s",\n' % (arg.split('.')[0]))
+ map_count += 1
+ map_conf.write(")\n")
+ with open(posixpath.join(npc_dir, NPC_MASTER_IMPORTS), 'w') as out:
+ out.write('// %s\n\n' % MESSAGE)
+ npc_master.sort()
+ for line in npc_master:
+ out.write(line)
+ if fatalError:
+ exit(1)
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/herculeswrapper/char.sh b/herculeswrapper/char.sh
new file mode 100755
index 0000000..2b794d9
--- /dev/null
+++ b/herculeswrapper/char.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+export PIPE=./log/charpipe.tmp
+
+source ${dir}/include.sh
+
+server_logic ./char-server 2s
diff --git a/herculeswrapper/herc-map-wrapper-config.example b/herculeswrapper/herc-map-wrapper-config.example
new file mode 100644
index 0000000..67a70a0
--- /dev/null
+++ b/herculeswrapper/herc-map-wrapper-config.example
@@ -0,0 +1,77 @@
+## config for herc-map-wrapper
+
+echo "Copy this file to herc-map-wrapper-config" >&2
+echo "Then You need to either set the oauth secret" >&2
+echo "or use a hard-coded list instead of list_issues" >&2
+echo "Both github and gitlab methods are included.">&2
+echo "Make sure to comment out the method that isn't used" >&2
+echo "Update the <repo ids> with the ID number for gitlab" >&2
+echo "or the repo name if github i.e. themanaworld/server-data" >&2
+echo "Then, delete these lines" >&2
+exit 1
+
+server_data=../../server-data
+client_data=../client-data
+evol_music=../music
+tmw_tools=../tools
+
+#gitlab
+list_issues()
+{
+python -c '
+
+# replace this with one of your oauth keys from github (no permissions needed)
+oauth = "01234567890123456789"
+
+import requests
+issues = requests.get("https://git.themanaworld.org/api/v3/projects/%d/merge_requests" % '$1', params={"state": "opened"}, headers={"PRIVATE-TOKEN": "%s" % oauth, "Accept": "application/vnd.gitlab.v3+json"})
+issues.raise_for_status()
+for issue in issues.json():
+ if "test" in issue["labels"]:
+ print(issue["iid"])
+' | sort -n
+}
+
+#github
+list_issues()
+{
+python -c '
+
+# replace this with one of your oauth keys from github (no permissions needed)
+oauth = "0123456789abcdef0123456789abcdef01234567"
+
+import requests
+issues = requests.get("https://api.github.com/search/issues", params={"q": "repo:'$1' type:pr state:open label:test"}, headers={"Authorization": "token %s" % oauth, "Accept": "application/vnd.github.v3+json"})
+issues.raise_for_status()
+for issue in issues.json["items"]:
+ print(issue["number"])
+' | sort -n
+}
+
+
+server_main_branch=origin/master
+
+server_extra_branches=(
+ $(list_issues <repo id> | sed 's|^|origin/merge-requests/|;')
+)
+
+client_main_branch=origin/master
+
+client_extra_branches=(
+ $(list_issues <repo id> | sed 's|^|origin/merge-requests/|;')
+)
+
+music_main_branch=origin/master
+
+music_extra_branches=(
+ $(list_issues <repo id> | sed 's|^|origin/merge-requests/|;')
+)
+unset list_issues
+
+motd=../server-data/npc/commands/motd-debug-text.txt
+
+expected_life=60
+trouble_sleep=60
+normal_sleep=5
+
+ulimit -c unlimited
diff --git a/herculeswrapper/include.sh b/herculeswrapper/include.sh
new file mode 100644
index 0000000..97325f8
--- /dev/null
+++ b/herculeswrapper/include.sh
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+
+function create_pipe {
+ trap "rm -f $PIPE" EXIT
+ if [[ ! -p $PIPE ]]; then
+ echo "Making pipe $PIPE"
+ rm -f $PIPE
+ mkfifo $PIPE
+ fi
+}
+
+# $1 - text
+function send_all_pipes {
+ echo $1 >$LOGIN_PIPE
+ echo $1 >$CHAR_PIPE
+}
+
+# $1 - text
+function send_char_pipe {
+ echo $1 >$CHAR_PIPE
+}
+
+# $1 - server binary name
+# $2 - sleep time
+function server_logic {
+ create_pipe
+ $1
+
+ while true
+ do
+ if read line <$PIPE; then
+ echo pipe: $line
+ if [[ "$line" == 'exit' ]]; then
+ exit
+ fi
+ if [[ "$line" == 'restart' ]]; then
+ sleep $2
+ $1
+ fi
+ fi
+ done
+}
+
+function pull_all {
+ ls
+ # Update Server Data
+ cd ../
+ ./pull.sh force
+ # Update Client Data
+ cd testserver
+ ./pseudo_update.sh
+ cd ..
+ # Return to server data
+ cd server-data
+}
+
diff --git a/herculeswrapper/login.sh b/herculeswrapper/login.sh
new file mode 100755
index 0000000..32085dd
--- /dev/null
+++ b/herculeswrapper/login.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+export PIPE=./log/loginpipe.tmp
+
+source ${dir}/include.sh
+
+server_logic ./login-server 0s
diff --git a/herculeswrapper/map.sh b/herculeswrapper/map.sh
new file mode 100755
index 0000000..67c5147
--- /dev/null
+++ b/herculeswrapper/map.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+
+export PIPE=./log/mappipe.tmp
+export LOGIN_PIPE=./log/loginpipe.tmp
+export CHAR_PIPE=./log/charpipe.tmp
+
+source ${dir}/include.sh
+
+create_pipe
+
+while [ 1 ] ; do
+ ./map-server
+ export ret=$?
+ case "${ret}" in
+ 0)
+ echo "Returned 0. Probably ctrl+c"
+ break
+ ;;
+ 1)
+ echo "Returned 1. Probably error in server"
+ break
+ ;;
+ 100)
+ echo "Terminating server"
+ send_all_pipes "exit"
+ break
+ ;;
+ 101)
+ echo "Restarting all servers..."
+ send_all_pipes "restart"
+ sleep 7s
+ ;;
+ 102)
+ echo "Restarting char and map servers..."
+ send_char_pipe "restart"
+ sleep 5s
+ ;;
+ 103)
+ echo "Restarting map server..."
+ ;;
+ 104)
+ echo "git pull..."
+ pull_all
+ echo "Restarting all servers..."
+ send_all_pipes "restart"
+ sleep 7s
+ ;;
+ esac
+done
+
+killall logmaster.py
+
diff --git a/imagescheck/icccheck.sh b/imagescheck/icccheck.sh
new file mode 100755
index 0000000..28a7b37
--- /dev/null
+++ b/imagescheck/icccheck.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+export DIR="../../clientdata"
+
+find -H $DIR -type f -name "*.png" -exec ./icccheckfile.sh {} \;
diff --git a/imagescheck/icccheckfile.sh b/imagescheck/icccheckfile.sh
new file mode 100755
index 0000000..5b45466
--- /dev/null
+++ b/imagescheck/icccheckfile.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+identify -verbose $1 | egrep -i "profile|iCCP" >/dev/null
+
+if [ "$?" == 0 ]; then
+ export name="$1"
+ export name=${name##../../clientdata/}
+ echo "ICC or iCCP profile found for image $name"
+fi
diff --git a/licensecheck/checkfile.sh b/licensecheck/checkfile.sh
new file mode 100755
index 0000000..38835d5
--- /dev/null
+++ b/licensecheck/checkfile.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+export name="$3"
+export name=${name##../../clientdata/}
+
+grep "$name" $1 >/dev/null
+if [ "$?" != 0 ]; then
+ grep "$name " $2 >/dev/null
+ if [ "$?" != 0 ]; then
+ echo "Missing license for $name"
+ fi
+fi
diff --git a/licensecheck/clientdata.sh b/licensecheck/clientdata.sh
new file mode 100755
index 0000000..cd63426
--- /dev/null
+++ b/licensecheck/clientdata.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+export DIR="../../clientdata"
+
+find -H $DIR -type f -name "*.png" -exec ./checkfile.sh $DIR/LICENSE $DIR/ART_LICENSE {} \;
+find -H $DIR/sfx -type f -name "*.ogg" -exec ./checkfile.sh $DIR/LICENSE $DIR/ART_LICENSE {} \;
+find -H $DIR -type f -name "*.tmx" -exec ./checkfile.sh $DIR/LICENSE $DIR/ART_LICENSE {} \;
+find -H $DIR -type f -name "*.jpg" -exec ./checkfile.sh $DIR/LICENSE $DIR/ART_LICENSE {} \;
+
diff --git a/licensecheck/serverdata.py b/licensecheck/serverdata.py
new file mode 100755
index 0000000..8a731a4
--- /dev/null
+++ b/licensecheck/serverdata.py
@@ -0,0 +1,71 @@
+#! /usr/bin/env python2.7
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2018 TMW-2
+# Author: Jesusalva
+
+# Bad command:
+# ls --recursive --hyperlink=always --format=single-column ../../serverdata/npc/|grep txt
+
+# Initialize stuff
+import subprocess
+import sys
+import os
+erp=[]
+
+# Clear previous NPC list
+try:
+ subprocess.call("rm npcs.txt", shell=True)
+except:
+ pass
+
+# Determine correct path
+PATH="../../serverdata/npc/"
+if len(sys.argv) == 2:
+ PATH=sys.argv[1]
+
+# Generate NPC list
+subprocess.call("find "+PATH+" txt > npcs.txt", shell=True)
+npcs=open("npcs.txt", "r")
+
+# Begin
+print("Checking license info for NPCs")
+print("Source is at: "+PATH)
+
+for mpa in npcs:
+ mp=mpa.replace('\n','')
+ # Skip mapflags
+ if "mapflag" in mp:
+ continue
+ # Skip bad files
+ if not '.txt' in mp:
+ continue
+ # Skip certain folders
+ if "/dev/" in mp or "/00000SAVE/" in mp or "/test/" in mp:
+ continue
+
+ a=open(mp, 'r')
+ #print("Verify %s" % mp)
+ ok=False
+ for line in a:
+ if 'tmw2 script' in line.lower() or 'tmw-2 script' in line.lower() or 'tmw 2 script' in line.lower() or 'tmw2/lof script' in line.lower() or 'This file is generated automatically' in line or 'author' in line.lower() or 'tmw2 function' in line.lower() or 'tmw-2 function' in line.lower() or 'tmw 2 function' in line.lower():
+ ok=True
+ break
+
+ a.close()
+ if not ok:
+ erp.append(mp)
+
+npcs.close()
+if len(erp) > 0:
+ print("-----------------------------------------------------------------------")
+
+for i in sorted(erp):
+ print(i)
+
+print("-----------------------------------------------------------------------")
+print("Serverdata license check result")
+print("Errors: %d" % (len(erp)))
+if len(erp):
+ os.exit(1)
+
diff --git a/localserver/.gitignore b/localserver/.gitignore
new file mode 100644
index 0000000..ff3de76
--- /dev/null
+++ b/localserver/.gitignore
@@ -0,0 +1 @@
+versions
diff --git a/localserver/README b/localserver/README
new file mode 100644
index 0000000..8d1e2a3
--- /dev/null
+++ b/localserver/README
@@ -0,0 +1,14 @@
+Here located for LOCAL server usage ONLY.
+Can be unsecure to use this files in public or production server.
+
+build.sh - build server from sources with most compilers.
+buildasan.sh - build server from sources with asan flags enabled.
+clean.sh - remove all built and temp files.
+givegm.sh - give gm rights to first account created on server.
+initdb.sh - initialize mariadb database.
+installconfigs.sh - install configurations into server data directory.
+updatedb.sh - update if need sql database
+updateconfigs.sh - update if need server data configuration
+
+See also http://wiki.evolonline.org/guidelines/installmariadb
+ http://wiki.evolonline.org/guidelines/localserver
diff --git a/localserver/applicator.py b/localserver/applicator.py
new file mode 100644
index 0000000..beb99a6
--- /dev/null
+++ b/localserver/applicator.py
@@ -0,0 +1,45 @@
+import subprocess
+
+# Open reapply.patch3
+f=open("reapply.patch3", "r")
+subprocess.call("cd ../../server-code", shell=True)
+
+for line in f:
+ if line[0] == "#" or line[0] == "\r" or line[0] == "\n":
+ continue
+ print "Downloading patch "+line.replace("\n", "")
+ subprocess.call("cd ../../server-code ; wget https://gitlab.com/evol/hercules/commit/"+line.replace("\n", "")+".diff", shell=True)
+ print "Applying patch..."
+ #subprocess.call("cd ../../server-code ; ls", shell=True)
+ subprocess.call("cd ../../server-code ; git apply --ignore-whitespace --reject "+line.replace("\n", "")+".diff", shell=True)
+ print "Patch applied"
+ subprocess.call("cd ../../server-code ; rm "+line.replace("\n", "")+".diff", shell=True)
+ print "Patch deleted (success)"
+
+
+f.close()
+print "Evol porting finished"
+
+
+###############################################
+# Open reapply.patch4 (HerculesWS Upstream)
+f=open("reapply.patch4", "r")
+subprocess.call("cd ../../server-code", shell=True)
+
+for line in f:
+ if line[0] == "#" or line[0] == "\r" or line[0] == "\n":
+ continue
+ print "Downloading patch "+line.replace("\n", "")
+ subprocess.call("cd ../../server-code ; wget https://github.com/HerculesWS/Hercules/commit/"+line.replace("\n", "")+".diff", shell=True)
+ print "Applying patch..."
+ #subprocess.call("cd ../../server-code ; ls", shell=True)
+ subprocess.call("cd ../../server-code ; git apply --ignore-whitespace --exclude=doc/ --exclude=tools/ --exclude=db/ --exclude=npc/ --exclude=conf/ --reject "+line.replace("\n", "")+".diff", shell=True)
+ print "Patch applied"
+ subprocess.call("cd ../../server-code ; rm "+line.replace("\n", "")+".diff", shell=True)
+ print "Patch deleted (success)"
+
+
+f.close()
+print "HercWS porting finished"
+subprocess.call("ls --recursive ../../server-code|grep rej", shell=True)
+
diff --git a/localserver/build.sh b/localserver/build.sh
new file mode 100755
index 0000000..e861d0e
--- /dev/null
+++ b/localserver/build.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+export DIR=`pwd`
+
+./checktime.sh
+./updater.sh
+source ./clean.sh
+./build.sh old > /dev/null
diff --git a/localserver/buildasan.sh b/localserver/buildasan.sh
new file mode 100755
index 0000000..6ed4c05
--- /dev/null
+++ b/localserver/buildasan.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+export DIR=`pwd`
+./updater.sh
+source ./clean.sh
+./build.sh
diff --git a/localserver/checktime.sh b/localserver/checktime.sh
new file mode 100755
index 0000000..9ca0c95
--- /dev/null
+++ b/localserver/checktime.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+year=$(date "+%Y")
+
+if [[ $year -lt 2017 ]]; then
+ date
+ echo "Error: Date time is wrong. Please fix time. See man date"
+ exit 1
+fi \ No newline at end of file
diff --git a/localserver/clean.sh b/localserver/clean.sh
new file mode 100755
index 0000000..134f5cb
--- /dev/null
+++ b/localserver/clean.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+rm -rf ../../server-code/src/evol/build/
+mkdir ../../server-code/src/evol/build/
+cd ../../server-code/
+make clean
diff --git a/localserver/conf/battle.conf b/localserver/conf/battle.conf
new file mode 100644
index 0000000..4080eed
--- /dev/null
+++ b/localserver/conf/battle.conf
@@ -0,0 +1,32 @@
+//================= Hercules Configuration ================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//================= License ===============================================
+//= This file is part of Hercules.
+//= http://herc.ws - http://github.com/HerculesWS/Hercules
+//=
+//= Copyright (C) 2014-2016 Hercules Dev Team
+//=
+//= Hercules is free software: you can redistribute it and/or modify
+//= it under the terms of the GNU General Public License as published by
+//= the Free Software Foundation, either version 3 of the License, or
+//= (at your option) any later version.
+//=
+//= This program is distributed in the hope that it will be useful,
+//= but WITHOUT ANY WARRANTY; without even the implied warranty of
+//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//= GNU General Public License for more details.
+//=
+//= You should have received a copy of the GNU General Public License
+//= along with this program. If not, see <http://www.gnu.org/licenses/>.
+//=========================================================================
+//= Battle local configuration file.
+//=========================================================================
+
+battle_configuration: {
+ // See conf/map/battle.conf for details
+}
diff --git a/localserver/conf/char-server.conf b/localserver/conf/char-server.conf
new file mode 100644
index 0000000..03b1900
--- /dev/null
+++ b/localserver/conf/char-server.conf
@@ -0,0 +1,31 @@
+//================= Hercules Configuration ================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//================= License ===============================================
+//= This file is part of Hercules.
+//= http://herc.ws - http://github.com/HerculesWS/Hercules
+//=
+//= Copyright (C) 2014-2016 Hercules Dev Team
+//=
+//= Hercules is free software: you can redistribute it and/or modify
+//= it under the terms of the GNU General Public License as published by
+//= the Free Software Foundation, either version 3 of the License, or
+//= (at your option) any later version.
+//=
+//= This program is distributed in the hope that it will be useful,
+//= but WITHOUT ANY WARRANTY; without even the implied warranty of
+//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//= GNU General Public License for more details.
+//=
+//= You should have received a copy of the GNU General Public License
+//= along with this program. If not, see <http://www.gnu.org/licenses/>.
+//=========================================================================
+//= Character Server local configuration file.
+//=========================================================================
+
+min_char_class: 0
+max_char_class: 2
diff --git a/localserver/conf/constants.conf b/localserver/conf/constants.conf
new file mode 100644
index 0000000..4562d0c
--- /dev/null
+++ b/localserver/conf/constants.conf
@@ -0,0 +1,30 @@
+//================= Hercules Configuration ================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//================= License ===============================================
+//= This file is part of Hercules.
+//= http://herc.ws - http://github.com/HerculesWS/Hercules
+//=
+//= Copyright (C) 2014-2016 Hercules Dev Team
+//=
+//= Hercules is free software: you can redistribute it and/or modify
+//= it under the terms of the GNU General Public License as published by
+//= the Free Software Foundation, either version 3 of the License, or
+//= (at your option) any later version.
+//=
+//= This program is distributed in the hope that it will be useful,
+//= but WITHOUT ANY WARRANTY; without even the implied warranty of
+//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//= GNU General Public License for more details.
+//=
+//= You should have received a copy of the GNU General Public License
+//= along with this program. If not, see <http://www.gnu.org/licenses/>.
+//=========================================================================
+//= Battle local configuration file.
+//=========================================================================
+
+debug: 0
diff --git a/localserver/conf/inter-server.conf b/localserver/conf/inter-server.conf
new file mode 100644
index 0000000..243dda4
--- /dev/null
+++ b/localserver/conf/inter-server.conf
@@ -0,0 +1,32 @@
+//================= Hercules Configuration ================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//================= License ===============================================
+//= This file is part of Hercules.
+//= http://herc.ws - http://github.com/HerculesWS/Hercules
+//=
+//= Copyright (C) 2014-2016 Hercules Dev Team
+//=
+//= Hercules is free software: you can redistribute it and/or modify
+//= it under the terms of the GNU General Public License as published by
+//= the Free Software Foundation, either version 3 of the License, or
+//= (at your option) any later version.
+//=
+//= This program is distributed in the hope that it will be useful,
+//= but WITHOUT ANY WARRANTY; without even the implied warranty of
+//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//= GNU General Public License for more details.
+//=
+//= You should have received a copy of the GNU General Public License
+//= along with this program. If not, see <http://www.gnu.org/licenses/>.
+//=========================================================================
+//= Hercules Inter Server local configuration file.
+//=========================================================================
+
+inter_configuration: {
+ // See conf/common/inter-server.conf
+}
diff --git a/localserver/conf/login-server.conf b/localserver/conf/login-server.conf
new file mode 100644
index 0000000..feadef9
--- /dev/null
+++ b/localserver/conf/login-server.conf
@@ -0,0 +1,32 @@
+//================= Hercules Configuration ================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//================= License ===============================================
+//= This file is part of Hercules.
+//= http://herc.ws - http://github.com/HerculesWS/Hercules
+//=
+//= Copyright (C) 2014-2016 Hercules Dev Team
+//=
+//= Hercules is free software: you can redistribute it and/or modify
+//= it under the terms of the GNU General Public License as published by
+//= the Free Software Foundation, either version 3 of the License, or
+//= (at your option) any later version.
+//=
+//= This program is distributed in the hope that it will be useful,
+//= but WITHOUT ANY WARRANTY; without even the implied warranty of
+//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//= GNU General Public License for more details.
+//=
+//= You should have received a copy of the GNU General Public License
+//= along with this program. If not, see <http://www.gnu.org/licenses/>.
+//=========================================================================
+//= Login Server local configuration file.
+//=========================================================================
+
+login_configuration: {
+ // See conf/login/login-server.conf for details
+}
diff --git a/localserver/conf/logs.conf b/localserver/conf/logs.conf
new file mode 100644
index 0000000..99e666d
--- /dev/null
+++ b/localserver/conf/logs.conf
@@ -0,0 +1,32 @@
+//================= Hercules Configuration ================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//================= License ===============================================
+//= This file is part of Hercules.
+//= http://herc.ws - http://github.com/HerculesWS/Hercules
+//=
+//= Copyright (C) 2014-2016 Hercules Dev Team
+//=
+//= Hercules is free software: you can redistribute it and/or modify
+//= it under the terms of the GNU General Public License as published by
+//= the Free Software Foundation, either version 3 of the License, or
+//= (at your option) any later version.
+//=
+//= This program is distributed in the hope that it will be useful,
+//= but WITHOUT ANY WARRANTY; without even the implied warranty of
+//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//= GNU General Public License for more details.
+//=
+//= You should have received a copy of the GNU General Public License
+//= along with this program. If not, see <http://www.gnu.org/licenses/>.
+//=========================================================================
+//= Hercules Log local configuration file.
+//=========================================================================
+
+map_log: {
+ // See conf/map/logs.conf for details
+}
diff --git a/localserver/conf/map-server.conf b/localserver/conf/map-server.conf
new file mode 100644
index 0000000..2c9b165
--- /dev/null
+++ b/localserver/conf/map-server.conf
@@ -0,0 +1,32 @@
+//================= Hercules Configuration ================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//================= License ===============================================
+//= This file is part of Hercules.
+//= http://herc.ws - http://github.com/HerculesWS/Hercules
+//=
+//= Copyright (C) 2014-2016 Hercules Dev Team
+//=
+//= Hercules is free software: you can redistribute it and/or modify
+//= it under the terms of the GNU General Public License as published by
+//= the Free Software Foundation, either version 3 of the License, or
+//= (at your option) any later version.
+//=
+//= This program is distributed in the hope that it will be useful,
+//= but WITHOUT ANY WARRANTY; without even the implied warranty of
+//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//= GNU General Public License for more details.
+//=
+//= You should have received a copy of the GNU General Public License
+//= along with this program. If not, see <http://www.gnu.org/licenses/>.
+//=========================================================================
+//= Map Server local configuration file.
+//=========================================================================
+
+map_configuration: {
+ // See conf/map/map-server.conf for details
+}
diff --git a/localserver/conf/msg_conf.txt b/localserver/conf/msg_conf.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/localserver/conf/msg_conf.txt
diff --git a/localserver/conf/ports.conf b/localserver/conf/ports.conf
new file mode 100644
index 0000000..7d5481e
--- /dev/null
+++ b/localserver/conf/ports.conf
@@ -0,0 +1,8 @@
+// Login server port
+login_port: 6901
+
+// Character server port
+char_port: 6122
+
+// Map server port
+map_port: 5122
diff --git a/localserver/conf/script.conf b/localserver/conf/script.conf
new file mode 100644
index 0000000..b354b2b
--- /dev/null
+++ b/localserver/conf/script.conf
@@ -0,0 +1,32 @@
+//================= Hercules Configuration ================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//================= License ===============================================
+//= This file is part of Hercules.
+//= http://herc.ws - http://github.com/HerculesWS/Hercules
+//=
+//= Copyright (C) 2014-2016 Hercules Dev Team
+//=
+//= Hercules is free software: you can redistribute it and/or modify
+//= it under the terms of the GNU General Public License as published by
+//= the Free Software Foundation, either version 3 of the License, or
+//= (at your option) any later version.
+//=
+//= This program is distributed in the hope that it will be useful,
+//= but WITHOUT ANY WARRANTY; without even the implied warranty of
+//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//= GNU General Public License for more details.
+//=
+//= You should have received a copy of the GNU General Public License
+//= along with this program. If not, see <http://www.gnu.org/licenses/>.
+//=========================================================================
+//= Hercules Script local configuration file.
+//=========================================================================
+
+script_configuration: {
+ // See conf/map/script.conf for details
+}
diff --git a/localserver/conf/socket.conf b/localserver/conf/socket.conf
new file mode 100644
index 0000000..04d0a40
--- /dev/null
+++ b/localserver/conf/socket.conf
@@ -0,0 +1,32 @@
+//================= Hercules Configuration ================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//================= License ===============================================
+//= This file is part of Hercules.
+//= http://herc.ws - http://github.com/HerculesWS/Hercules
+//=
+//= Copyright (C) 2014-2016 Hercules Dev Team
+//=
+//= Hercules is free software: you can redistribute it and/or modify
+//= it under the terms of the GNU General Public License as published by
+//= the Free Software Foundation, either version 3 of the License, or
+//= (at your option) any later version.
+//=
+//= This program is distributed in the hope that it will be useful,
+//= but WITHOUT ANY WARRANTY; without even the implied warranty of
+//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//= GNU General Public License for more details.
+//=
+//= You should have received a copy of the GNU General Public License
+//= along with this program. If not, see <http://www.gnu.org/licenses/>.
+//=========================================================================
+//= Hercules Sockets local configuration file
+//=========================================================================
+
+socket_configuration: {
+ // See conf/common/socket.conf for details.
+}
diff --git a/localserver/conf/sql_connection.conf b/localserver/conf/sql_connection.conf
new file mode 100644
index 0000000..1f9f514
--- /dev/null
+++ b/localserver/conf/sql_connection.conf
@@ -0,0 +1,52 @@
+//================= Hercules Configuration ================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//================= License ===============================================
+//= This file is part of Hercules.
+//= http://herc.ws - http://github.com/HerculesWS/Hercules
+//=
+//= Copyright (C) 2014-2016 Hercules Dev Team
+//=
+//= Hercules is free software: you can redistribute it and/or modify
+//= it under the terms of the GNU General Public License as published by
+//= the Free Software Foundation, either version 3 of the License, or
+//= (at your option) any later version.
+//=
+//= This program is distributed in the hope that it will be useful,
+//= but WITHOUT ANY WARRANTY; without even the implied warranty of
+//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//= GNU General Public License for more details.
+//=
+//= You should have received a copy of the GNU General Public License
+//= along with this program. If not, see <http://www.gnu.org/licenses/>.
+//=========================================================================
+//= SQL connection configuration file.
+//=========================================================================
+// This file affects how ALL server sql connections work, unless explictly
+// defined so in the server configuration file (See
+// doc/global_configuration.txt for more information).
+//=========================================================================
+
+sql_connection: {
+ // [INTER] You can specify the codepage to use in your mySQL tables here.
+ // (Note that this feature requires MySQL 4.1+)
+ //default_codepage: ""
+
+ // [LOGIN] Is `userid` in account_db case sensitive?
+ //case_sensitive: false
+
+ // For IPs, ideally under linux, you want to use localhost instead of 127.0.0.1.
+ // Under windows, you want to use 127.0.0.1. If you see a message like
+ // "Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)"
+ // and you have localhost, switch it to 127.0.0.1
+ db_hostname: "127.0.0.1"
+ db_port: 3306
+ db_username: "evol"
+ db_password: "evol"
+ db_database: "evol"
+ //codepage:""
+}
diff --git a/localserver/givegm.sh b/localserver/givegm.sh
new file mode 100755
index 0000000..ac9d301
--- /dev/null
+++ b/localserver/givegm.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+ID="$1"
+if [[ -z "${ID}" ]]; then
+ mysql -u evol -pevol evol -e "update login set group_id=99 where account_id=2000000;"
+else
+ mysql -u evol -pevol evol -e "update login set group_id=99 where account_id=${ID} or userid='${ID}';"
+fi
diff --git a/localserver/initdb.sh b/localserver/initdb.sh
new file mode 100755
index 0000000..5a5db2d
--- /dev/null
+++ b/localserver/initdb.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+export DIR="$(pwd)"
+export VER=$(cat versions/sqlver 2>/dev/null)
+
+cd ../../serverdata/sql-files
+
+echo Creating db and user...
+echo Enter mysql root password:
+mysql --force -u root -p <./init.sql
+result=$?
+if [ "$result" != 0 ]; then
+ echo Password failed. Trying with sudo...
+ sudo mysql --force -u root <./init.sql
+fi
+
+export CMD="mysql -u evol -pevol evol"
+
+echo Creating tables...
+$CMD <main.sql
+$CMD <logs.sql
+
+if [[ -z "${VER}" ]]; then
+ mkdir ${DIR}/versions
+ echo "19" >${DIR}/versions/sqlver
+fi
diff --git a/localserver/installconfigs.sh b/localserver/installconfigs.sh
new file mode 100755
index 0000000..2a42c3c
--- /dev/null
+++ b/localserver/installconfigs.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+export SD="../../serverdata"
+export CONF="$SD/conf/import"
+export NPC="$SD/npc"
+
+mkdir $CONF
+cp conf/* $CONF
+cp -f ${SD}/conf/channels.conf.base ${SD}/conf/channels.conf
+cp -f npc/006-10_* ${NPC}/006-10/
+cp -f npc/017-1_* ${NPC}/017-1/
+cp -f npc/015-8_* ${NPC}/015-8/
+cp -f npc/025-2-4_* ${NPC}/025-2-4/
+cp -f npc/018-7-1_* ${NPC}/018-7-1/
+cp -f npc/023-4_* ${NPC}/023-4/
+cp -f npc/botcheck_* ${NPC}/botcheck/
+cp -f npc/easteregg_* ${NPC}/config/
+cp -f npc/magic_* ${NPC}/magic/
+mkdir versions
+echo "4" >versions/confver
diff --git a/localserver/npc/006-10_blackbox.txt b/localserver/npc/006-10_blackbox.txt
new file mode 100644
index 0000000..924a45e
--- /dev/null
+++ b/localserver/npc/006-10_blackbox.txt
@@ -0,0 +1,12 @@
+// TMW2 Script
+// Author:
+// Jesusalva
+// Description:
+// Runestaff Blackbox
+
+// Syntax:
+// 00610_Init_BlackBox( - )
+function script 00610_Init_BlackBox {
+ return;
+}
+
diff --git a/localserver/npc/015-8_blackbox.txt b/localserver/npc/015-8_blackbox.txt
new file mode 100644
index 0000000..7ae5c57
--- /dev/null
+++ b/localserver/npc/015-8_blackbox.txt
@@ -0,0 +1,27 @@
+// TMW2 Script
+// Author:
+// Jesusalva
+// Description:
+// This is the “015-8 mapfile” black box.
+// It contains the answers to all the riddles.
+// This file is kept outside the main tree for translation purposes.
+// You thought you could cheat the answers, eh?
+
+// Syntax:
+// 0158_Riddle_BlackBox( )
+// Returns true on success, false on failure.
+function script 0158_Riddle_BlackBox {
+ .@r$="secret";
+ .@l$=l("secret");
+
+ mesc l("When I was alive, I brought people to their destiny.");
+ mesc l("One day, I was provoked. Others came to my aid,");
+ mesc l("But all I could do was a bite.");
+ mesc l("To my murders, I gave them a full meal.");
+ mesc l("And in a hut, a lone woman mourned my death.");
+ mes "";
+ input .@ans$;
+
+ return riddlevalidation(.@ans$, .@r$[.@e], .@l$[.@e]);
+}
+
diff --git a/localserver/npc/017-1_stranger_blackbox.txt b/localserver/npc/017-1_stranger_blackbox.txt
new file mode 100644
index 0000000..bdd444d
--- /dev/null
+++ b/localserver/npc/017-1_stranger_blackbox.txt
@@ -0,0 +1,40 @@
+// TMW2 Script
+// Author:
+// Jesusalva
+// Description:
+// This is the “Stranger” black box.
+// It contains the answers to all the riddles.
+// This file is kept outside the main tree for translation purposes.
+// You thought you could cheat the answers, eh?
+
+// Syntax:
+// 0171_Stranger_BlackBox( difficulty )
+// Where difficulty can be EASY, MEDIUM or HARD.
+// Returns 1 on success, 0 on failure.
+function script 0171_Stranger_BlackBox {
+ switch (getarg(0)) {
+ case EASY:
+ setarray .@q$, l("Are you cheating?"), l("Should we blame Saulc for bugs?");
+ setarray .@r$, "no", "yes";
+ setarray .@l$, l("no"), l("yes");
+ break;
+ case MEDIUM:
+ setarray .@q$, l("Are you cheating?"), l("Should we blame Saulc for bugs?");
+ setarray .@r$, "no", "yes";
+ setarray .@l$, l("no"), l("yes");
+ break;
+ case HARD:
+ setarray .@q$, l("Are you cheating?"), l("Should we blame Saulc for bugs?");
+ setarray .@r$, "no", "yes";
+ setarray .@l$, l("no"), l("yes");
+ break;
+ default: return 0;
+ }
+
+ .@e=rand(getarraysize(.@q$)-1);
+ mesq .@q$[.@e];
+ input .@ans$;
+
+ return riddlevalidation(.@ans$, .@r$[.@e], .@l$[.@e]);
+}
+
diff --git a/localserver/npc/018-7-1_demure_blackbox.txt b/localserver/npc/018-7-1_demure_blackbox.txt
new file mode 100644
index 0000000..a5e7560
--- /dev/null
+++ b/localserver/npc/018-7-1_demure_blackbox.txt
@@ -0,0 +1,12 @@
+// TMW2 Script
+// Author:
+// Jesusalva
+// Description:
+// Demure Axe Blackbox
+
+// Syntax:
+// 01871_Demure_BlackBox( - )
+function script 01871_Demure_BlackBox {
+ return;
+}
+
diff --git a/localserver/npc/023-4_blackbox.txt b/localserver/npc/023-4_blackbox.txt
new file mode 100644
index 0000000..a5687e5
--- /dev/null
+++ b/localserver/npc/023-4_blackbox.txt
@@ -0,0 +1,36 @@
+// TMW2 Script
+// Author:
+// Jesusalva
+// Description:
+// 023-4 Blackbox
+
+// Syntax:
+// 0234_Password_BlackBox( PASSWORD )
+// 0234_Init_BlackBox( - )
+function script 0234_Password_BlackBox {
+ .@message$=getarg(0, "");
+ // Only react if the message is what we want to hear
+ if (.@message$ == "PASSWORD") {
+ if (getq(FrostiaQuest_Homunculus) < 1)
+ end;
+
+ if (isat("023-3", 26, 219)) {
+ warp "023-4", 42, 69;
+ sleep2(15);
+ dispbottom l("The gates to Aethyr have opened themselves to you.");
+ }
+ }
+ return;
+}
+
+function script 0234_Init_BlackBox {
+ return;
+}
+
+- script #0234BlackBox NPC_HIDDEN,{
+ end;
+OnIter:
+ end;
+}
+
+
diff --git a/localserver/npc/025-2-4_aegis_blackbox.txt b/localserver/npc/025-2-4_aegis_blackbox.txt
new file mode 100644
index 0000000..524d704
--- /dev/null
+++ b/localserver/npc/025-2-4_aegis_blackbox.txt
@@ -0,0 +1,33 @@
+// TMW2 Script
+// Author:
+// Jesusalva
+// Description:
+// Aegis Shield & Mana Tree Blackbox
+
+// Syntax:
+// 02524_Tree_BlackBox( target name, target GID )
+// 02524_Revenge_BlackBox( - )
+// 02524_Avenge_BlackBox( deadflag )
+function script 02524_Tree_BlackBox {
+ .@prayer$=getarg(0);
+ .@gid=getarg(1);
+ .@org=getcharid(3);
+ message .@prayer$, strcharinfo(0)+" is praying for you!";
+ if (!attachrid(.@gid)) return;
+ percentheal 100, 100;
+ @purifio=max(gettimetick(2), @purifio)+60;
+ attachrid(.@org);
+ getmapxy(.@m$, .@x, .@y, 0);
+ .@mob=monster(.@m$, .@x+any(-2,-3,2,3), .@y+any(-1,0,1), "Pinkie of Doom", PinkieSuseran, 1);
+ unitattack(.@mob, .@org);
+ return;
+}
+
+function script 02524_Revenge_BlackBox {
+ return;
+}
+
+function script 02524_Avenge_BlackBox {
+ return;
+}
+
diff --git a/localserver/npc/botcheck_blackbox.txt b/localserver/npc/botcheck_blackbox.txt
new file mode 100644
index 0000000..b11050e
--- /dev/null
+++ b/localserver/npc/botcheck_blackbox.txt
@@ -0,0 +1,35 @@
+// TMW2 Script
+// Author:
+// Jesusalva
+// Description:
+// Bot check area configuration, measures, utilitaries,
+// and various configuration files to prevent bots.
+// You must fill this file manually, or don't file it at all.
+
+botcheck,37,41,0 script Bot Master NPC_ELMO,{
+ if (!is_staff())
+ end;
+ mesc l("This file must be requested due not being public."), 1;
+ mesc l("Too many evil bots out there %%e They can't learn this!"), 1;
+ close;
+
+OnCall:
+ if (getmap() ~= "botcheck") {
+ if (@bck_map$ != "")
+ warp @bck_map$, @bck_x, @bck_y;
+ else
+ warp "Save", 0, 0;
+ dispbottom l("You passed the botcheck. Thanks for playing the game %%G");
+ end;
+ }
+ getmapxy(@bck_map$, @bck_x, @bck_y, 0);
+ warp "botcheck", 38, 40;
+ dispbottom l("BOTCHECK: You are on botcheck area. DO NOT attack or collect items until given the order.");
+ end;
+
+OnInit:
+ bindatcmd "botcheck", "Bot Master::OnCall", 60, 60, 1;
+ end;
+
+}
+
diff --git a/localserver/npc/easteregg_blackbox.txt b/localserver/npc/easteregg_blackbox.txt
new file mode 100644
index 0000000..0335eeb
--- /dev/null
+++ b/localserver/npc/easteregg_blackbox.txt
@@ -0,0 +1,16 @@
+// TMW2 Script
+//
+// Contains Easter Egg data and triggers (some excluded)
+
+// Author: Saulc
+005-2-1,41,33,0 script Note#saxsocave NPC_PAPER_NOTE,{
+ mesc l("I leave this basement that start to be too dangerous!");
+
+ RegEasterEgg(EE_SAXSO, 5);
+ close;
+
+OnInit:
+ .distance = 2;
+ end;
+}
+
diff --git a/localserver/npc/magic_blackbox.txt b/localserver/npc/magic_blackbox.txt
new file mode 100644
index 0000000..63d5001
--- /dev/null
+++ b/localserver/npc/magic_blackbox.txt
@@ -0,0 +1,13 @@
+// TMW2 Script
+// Author:
+// Jesusalva
+// Description:
+// Magic Blackbox
+// Contains the internals for Scripture skill.
+
+// Syntax:
+// SK_Scripture( {@chat$} )
+function script SK_Scripture {
+ return;
+}
+
diff --git a/localserver/reapply.patch3 b/localserver/reapply.patch3
new file mode 100644
index 0000000..7d7ddaf
--- /dev/null
+++ b/localserver/reapply.patch3
@@ -0,0 +1,5 @@
+############
+## First postponed update
+
+## <Commit Short Summary>
+# <full commit ID>
diff --git a/localserver/reapply.patch4 b/localserver/reapply.patch4
new file mode 100644
index 0000000..bc0f612
--- /dev/null
+++ b/localserver/reapply.patch4
@@ -0,0 +1,47 @@
+############
+# HOWTO:
+# git log v2019.08.25.. --pretty=short
+
+# TODO: Instructions on how to update our hercules repo
+# git diff v2019.09.22 v2019.10.20 > ../tmw2/server-code/x.diff
+# git apply --ignore-whitespace --exclude=doc/ --exclude=tools/ --exclude=db/ --exclude=npc/ --exclude=conf/ --reject x.diff ; git checkout -- .gitlab-ci.yml
+
+############
+## v2019.10.20
+
+############
+## v2019.11.17+1
+
+############
+## v2019.12.15
+
+############
+## v2020.01.12
+
+############
+## v2020.02.09
+
+############
+## v2020.03.08+2
+
+############
+## v2020.04.05+1
+
+############
+## v2020.05.03
+
+############
+## v2020.05.31+1
+
+############
+## v2020.06.28
+
+## Changed logic of skill_get_index
+#1042e9919f18d4aac21583a675d28758ba233e03
+
+## Added ifdef on mmo.h to allow change it with CPPFLAGS
+#8a4667a265227587dcc288f75dfe948f5df46034
+
+############
+## v2020.07.26
+
diff --git a/localserver/rebuild.sh b/localserver/rebuild.sh
new file mode 100755
index 0000000..3ec97ed
--- /dev/null
+++ b/localserver/rebuild.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+export DIR=`pwd`
+
+# Skip patches applications
+
+./checktime.sh
+source ./clean.sh
+CC="gcc-9" ./build.sh old > /dev/null
diff --git a/localserver/updateconfigs.sh b/localserver/updateconfigs.sh
new file mode 100755
index 0000000..4caf378
--- /dev/null
+++ b/localserver/updateconfigs.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+export VER=$(cat versions/confver 2>/dev/null)
+export CONFDIR="../../serverdata/"
+
+if [[ -z "${VER}" ]]; then
+ export VER="0"
+ mkdir versions
+fi
+
+if [ "${VER}" -lt "1" ]; then
+ cp -f ${CONFDIR}/conf/channels.conf ${CONFDIR}/conf/channels.conf.bak
+ cp -f ${CONFDIR}/conf/channels.conf.base ${CONFDIR}/conf/channels.conf
+fi
+
+if [ "${VER}" -lt "2" ]; then
+ rm ${CONFDIR}/conf/import/*.txt
+ cp conf/* ${CONFDIR}/conf/import
+ echo "2" >versions/confver
+fi
+
+if [ "${VER}" -lt "3" ]; then
+ cp conf/constants.conf ${CONFDIR}/conf/import/
+ echo "3" >versions/confver
+fi
+
+if [ "${VER}" -lt "4" ]; then
+ cp -f npc/motd-* ${CONFDIR}/npc/commands/
+ echo "4" >versions/confver
+fi
diff --git a/localserver/updatedb.sh b/localserver/updatedb.sh
new file mode 100755
index 0000000..6e777ae
--- /dev/null
+++ b/localserver/updatedb.sh
@@ -0,0 +1,103 @@
+#!/usr/bin/env bash
+
+export VER=$(cat versions/sqlver 2>/dev/null)
+export SQLDIR="../../serverdata/sql-files/upgrades"
+export CMD="mysql -u evol -pevol evol"
+
+function run {
+ echo "Running $1"
+ $CMD <${SQLDIR}/$1
+}
+
+if [[ -z "${VER}" ]]; then
+ export VER="1"
+ mkdir versions
+fi
+
+if [ "${VER}" -lt "2" ]; then
+ run "2015-07-08--13-08.sql"
+ run "2015-08-27--20-42.sql"
+fi
+
+if [ "${VER}" -lt "3" ]; then
+ run "2015-12-16--12-57.sql"
+ run "2015-12-17--15-58.sql"
+fi
+
+if [ "${VER}" -lt "4" ]; then
+ run "2016-03-10--22-18.sql"
+fi
+
+if [ "${VER}" -lt "5" ]; then
+ run "2016-07-08--02-42.sql"
+ run "2016-07-08--02-51.sql"
+fi
+
+if [ "${VER}" -lt "6" ]; then
+ run "2016-10-03--20-27.sql"
+fi
+
+if [ "${VER}" -lt "7" ]; then
+ run "2016-10-26--10-29.sql"
+fi
+
+if [ "${VER}" -lt "8" ]; then
+ run "2017-03-02--11-40.sql"
+ run "2017-03-05--08-09.sql"
+fi
+
+if [ "${VER}" -lt "9" ]; then
+ run "2017-03-15--14-29.sql"
+fi
+
+if [ "${VER}" -lt "10" ]; then
+ run "2017-11-04--10-39.sql"
+fi
+
+if [ "${VER}" -lt "11" ]; then
+ run "2017-06-04--15-04.sql"
+ run "2017-06-04--15-05.sql"
+fi
+
+if [ "${VER}" -lt "12" ]; then
+ run "2018-02-26--15-57.sql"
+fi
+
+if [ "${VER}" -lt "13" ]; then
+ run "2018-03-10--04-06.sql"
+fi
+
+if [ "${VER}" -lt "14" ]; then
+ run "2018-06-05--12-02.sql"
+fi
+
+if [ "${VER}" -lt "15" ]; then
+ run "2018-07-24--03-23.sql"
+ run "2018-08-03--11-37.sql"
+fi
+
+if [ "${VER}" -lt "16" ]; then
+ run "2018-06-03--00-10.sql"
+ run "2018-06-03--17-16.sql"
+ run "2018-09-01--05-22.sql"
+fi
+
+if [ "${VER}" -lt "17" ]; then
+ run "2018-12-14--01-02.sql"
+ run "2018-12-29--07-51.sql"
+fi
+
+if [ "${VER}" -lt "18" ]; then
+ run "2019-04-08--21-52.sql"
+ run "2019-04-25--02-12.sql"
+fi
+
+if [ "${VER}" -lt "19" ]; then
+ run "2019-05-09--18-07.sql"
+ run "2020-08-01--00-00.sql"
+fi
+
+if [ "${VER}" -lt "19" ]; then
+ echo "19" >versions/sqlver
+fi
+
diff --git a/localserver/updater.sh b/localserver/updater.sh
new file mode 100755
index 0000000..461afa5
--- /dev/null
+++ b/localserver/updater.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+
+# Apply beta.patch if it exists to serverdata
+# It will only exist on BETA SERVERS, though
+if [ -e "./beta.patch" ]
+ then
+ echo "Apply beta.patch ........"
+ cd ../../serverdata
+ git apply ../tools/localserver/beta.patch
+ cd $DIR
+ mv beta.patch .~beta.patch
+ ls
+ echo "........ Done."
+fi
+
+# Apply beta.patch2 if it exists to server-code
+# It will only exist on BETA SERVERS, though
+if [ -e "./beta.patch2" ]
+ then
+ echo "Apply server updates ........"
+ sleep 1
+ cd ../../server-code
+ git checkout -- src
+ echo "Rolling server back to correct version"
+ #git diff master ce2dbb6acdc559ec256d1f9f9a779b8283064708 > x.diff
+ #ls
+ #head -n 25 x.diff
+ #tail -n 40 x.diff
+ #git apply --reject --whitespace=nowarn x.diff
+ git status
+ ls --recursive|grep ".rej"
+ cd ../tools/localserver
+ echo "Server code clean ........"
+ sleep 1
+ python applicator.py
+ echo "Apply beta.patch2 ........"
+ cd ../../server-code
+ git apply ../tools/localserver/beta.patch2
+ cd $DIR
+ #mv beta.patch2 .~beta.patch2
+ ls
+ echo "........ Done."
+fi
+
diff --git a/manaplus/connect_local_server.sh b/manaplus/connect_local_server.sh
new file mode 100755
index 0000000..33232cd
--- /dev/null
+++ b/manaplus/connect_local_server.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+sleep 3
+
+export MANAPLUS="../../manaplus/src/manaplus"
+if [ -f "$MANAPLUS" ]; then
+ echo "Starting local manaplus"
+else
+ echo "Starting system manaplus"
+ export MANAPLUS="manaplus"
+fi
+
+${MANAPLUS} -u -d ../../clientdata tmw2.manaplus
diff --git a/manaplus/evol.manaplus b/manaplus/evol.manaplus
new file mode 100644
index 0000000..8062529
--- /dev/null
+++ b/manaplus/evol.manaplus
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <option name="appName" value="Evol Online"/>
+ <option name="appShort" value="mana"/>
+ <option name="appIcon" value="evol/icons/evol-client"/>
+ <option name="screenshots" value="EvolOnline"/>
+ <option name="loginMusic" value="keprohm.ogg"/>
+ <option name="onlineServerList" value="http://www.evolonline.org/serverlist.xml"/>
+ <option name="onlineServerFile" value="evol.xml"/>
+ <option name="defaultServer" value="127.0.0.1"/>
+ <option name="defaultPort" value="6901"/>
+ <option name="defaultServerType" value="evol2"/>
+ <option name="font" value="fonts/dejavusans.ttf" />
+ <option name="boldFont" value="fonts/dejavusans-bold.ttf" />
+ <option name="theme" value="jewelry" />
+ <option name="wallpapersPath" value="evol/images/" />
+ <option name="wallpaperFile" value="loginwallpaper_800x600.png" />
+</configuration>
diff --git a/manaplus/force_localmanaplus.sh b/manaplus/force_localmanaplus.sh
new file mode 100755
index 0000000..1cc34cb
--- /dev/null
+++ b/manaplus/force_localmanaplus.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+export MANAPLUS="../../manaplus/src/manaplus"
+echo "Starting local manaplus"
+${MANAPLUS} -u -d ../../clientdata tmw2.manaplus
diff --git a/manaplus/loginwallpaper_800x600.png b/manaplus/loginwallpaper_800x600.png
new file mode 100644
index 0000000..4a97574
--- /dev/null
+++ b/manaplus/loginwallpaper_800x600.png
Binary files differ
diff --git a/manaplus/tmw2.manaplus b/manaplus/tmw2.manaplus
new file mode 100644
index 0000000..0a2cbb8
--- /dev/null
+++ b/manaplus/tmw2.manaplus
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <option name="appName" value="Crossroads"/>
+ <option name="appShort" value="mana"/>
+ <option name="appIcon" value="evol/icons/evol-client"/>
+ <option name="screenshots" value="TMW2"/>
+ <option name="loginMusic" value="keprohm.ogg"/>
+ <option name="onlineServerList" value="http://www.evolonline.org/serverlist.xml"/>
+ <option name="onlineServerFile" value="evol.xml"/>
+ <option name="defaultServer" value="127.0.0.1"/>
+ <option name="defaultPort" value="6901"/>
+ <option name="defaultServerType" value="evol2"/>
+ <option name="font" value="fonts/dejavusans.ttf" />
+ <option name="boldFont" value="fonts/dejavusans-bold.ttf" />
+ <option name="theme" value="jewelry" />
+ <option name="wallpapersPath" value="evol/images/" />
+ <option name="wallpaperFile" value="loginwallpaper_800x600.png" />
+</configuration>
diff --git a/testxml/testxml.py b/testxml/testxml.py
new file mode 100755
index 0000000..0b693cc
--- /dev/null
+++ b/testxml/testxml.py
@@ -0,0 +1,2439 @@
+#! /usr/bin/env python
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2010-2011 Evol Online
+# Author: Andrei Karas (4144)
+
+import array
+import os
+import re
+import datetime
+import xml
+import csv
+import ogg.vorbis
+import StringIO
+import sys
+from xml.dom import minidom
+from xml.etree import ElementTree
+from PIL import Image
+import zlib
+
+filt = re.compile(".+[.](xml|tmx|tsx)", re.IGNORECASE)
+filtmaps = re.compile(".+[.]tmx", re.IGNORECASE)
+filtimages = re.compile(".+[.]png", re.IGNORECASE)
+filtxmls = re.compile(".+[.]xml", re.IGNORECASE)
+filtogg = re.compile(".+[.]ogg", re.IGNORECASE)
+dyesplit1 = re.compile(";")
+dyesplit2 = re.compile(",")
+parentDir = "../../gittorious/clientdata-beta"
+iconsDir = "graphics/items/"
+spritesDir = "graphics/sprites/"
+particlesDir = "graphics/particles/"
+sfxDir = "sfx/"
+musicDir = "music/"
+mapsDir = "maps/"
+spriteErrorFile = "error.xml"
+levelUpEffectFile = "levelup.particle.xml"
+portalEffectFile = "warparea.particle.xml"
+minimapsDir = "graphics/minimaps/"
+wallpapersDir = "graphics/images/"
+wallpaperFile = "login_wallpaper.png"
+
+errors = 0
+warnings = 0
+errDict = set()
+safeDye = False
+borderSize = 14 # Required 18 # Original 14
+tiledVersion = 13 # Minimum Tiled version, advised "14" for Tiled 1.4
+colorsList = set()
+showAll = False
+silent = False
+stfu = False
+herc = False
+
+testBadCollisions = False
+# number of tiles difference. after this amount tiles can be counted as incorrect
+tileNumDiff = 3
+# max number of incorrect tiles. If more then tile not counted as error
+maxNumErrTiles = 5
+
+class Tileset:
+ None
+
+class Layer:
+ None
+
+def printErr(err):
+ errDict.add(err)
+ print err.encode("utf-8")
+
+def showFileErrorById(id, rootDir, fileDir):
+ rootDir = rootDir.encode("utf-8")
+ fileDir = fileDir.encode("utf-8")
+ print "error: id=" + id + ", file not found: " + fileDir + " (" + rootDir + fileDir + ")"
+
+def showFileWarningById(id, rootDir, fileDir):
+ rootDir = rootDir.encode("utf-8")
+ fileDir = fileDir.encode("utf-8")
+ print "warn: id=" + id + ", file not found: " + fileDir + " (" + rootDir + fileDir + ")"
+
+def showError(id, text):
+ text = text.encode("utf-8")
+ print "error: id=" + id + " " + text
+
+def showWarning(id, text):
+ text = text.encode("utf-8")
+ print "warn: id=" + id + " " + text
+
+def showMsg(id, text, src, iserr):
+ global errors, warnings
+ if text != "":
+ text = text + ", " + src
+ if iserr == True:
+ if text not in errDict:
+ showError(id, text)
+ errDict.add(text)
+ errors = errors + 1
+ else:
+ if text not in errDict:
+ showWarning(id, text)
+ errDict.add(text)
+ warnings = warnings + 1
+
+def showMsgSprite(file, text, iserr):
+ global errors, warnings
+ if iserr == True:
+ err = "error: sprite=" + file + " " + text
+ if err not in errDict:
+ printErr(err)
+ errors = errors + 1
+ else:
+ err = "warn: sprite=" + file + " " + text
+ if err not in errDict:
+ printErr(err)
+ warnings = warnings + 1
+
+def showMsgFile(file, text, iserr):
+ global errors, warnings
+ if iserr == True:
+ err = "error: file=" + file + " " + text
+ if err not in errDict:
+ printErr(err)
+ errors = errors + 1
+ else:
+ err = "warn: file=" + file + " " + text
+ if err not in errDict:
+ printErr(err)
+ warnings = warnings + 1
+
+def showFileMsgById(id, rootDir, fileDir, iserr):
+ global errors, warnings
+ if iserr == True:
+ showFileErrorById(id, rootDir, fileDir)
+ errors = errors + 1
+ else:
+ showFileWarningById(id, rootDir, fileDir)
+ warnings = warnings + 1
+
+def printSeparator():
+ print "--------------------------------------------------------------------------------"
+
+def showHeader():
+ print("Client Data Validator.")
+ print("Run at: " + datetime.datetime.now().isoformat())
+ print("https://git.themanaworld.org/ml/tools/blob/master/testxml/testxml.py")
+ printSeparator()
+
+def showFooter():
+ printSeparator()
+ print "Total:"
+ print " Warnings: " + str(warnings)
+ print " Errors: " + str(errors)
+
+def enumDirs(parentDir):
+ global warnings, errors
+ try:
+ files = os.listdir(parentDir)
+ except OSError:
+ print "Directory error: " + parentDir
+ if silent == False:
+ warnings = warnings + 1
+ return
+ for file1 in files:
+ if file1[0] == ".":
+ continue
+ file2 = os.path.abspath(parentDir + os.path.sep + file1)
+ if not os.path.isfile(file2):
+ enumDirs(file2)
+ else:
+ if filt.search(file1):
+ try:
+ if silent == True and not stfu:
+ print "Checking " + file2
+ minidom.parse(file2)
+ except xml.parsers.expat.ExpatError as err:
+ print "error: " + file2 + ", line=" + str(err.lineno) + ", char=" + str(err.offset)
+ errors = errors + 1
+ if file1 != "testxml.py":
+ checkFilePermission(file2)
+
+def checkFilePermission(fullName):
+ global warnings
+ if os.access(fullName, os.X_OK):
+ print "warn: execute flag on file: " + fullName
+ warnings = warnings + 1
+
+
+def loadPaths():
+ global warnings, iconsDir, spritesDir, sfxDir, particlesDir, mapsDir, spriteErrorFile, \
+ levelUpEffectFile, portalEffectFile, minimapsDir, wallpapersDir, walpaperFile, \
+ musicDir, wallpaperFile
+ try:
+ dom = minidom.parse(parentDir + "/paths.xml")
+ for node in dom.getElementsByTagName("option"):
+ if node.attributes["name"].value == "itemIcons":
+ iconsDir = node.attributes["value"].value
+ if iconsDir != "graphics/items/":
+ print "warn: itemIcons path has not default value."\
+ " Will be incampatible with old clients."
+ warnings = warnings + 1
+ elif node.attributes["name"].value == "sprites":
+ spritesDir = node.attributes["value"].value
+ if spritesDir != "graphics/sprites/":
+ print "warn: sprites path has not default value."\
+ " Will be incampatible with old clients."
+ warnings = warnings + 1
+ elif node.attributes["name"].value == "sfx":
+ sfxDir = node.attributes["value"].value
+
+ elif node.attributes["name"].value == "particles":
+ particlesDir = node.attributes["value"].value
+ if particlesDir != "graphics/particles/":
+ print "warn: particles path has not default value."\
+ " Will be incampatible with old clients."
+ warnings = warnings + 1
+ elif node.attributes["name"].value == "maps":
+ mapsDir = node.attributes["value"].value
+ if mapsDir != "maps/":
+ print "warn: maps path has not default value."\
+ " Will be incampatible with old clients."
+ warnings = warnings + 1
+ elif node.attributes["name"].value == "spriteErrorFile":
+ spriteErrorFile = node.attributes["value"].value
+ elif node.attributes["name"].value == "levelUpEffectFile":
+ levelUpEffectFile = node.attributes["value"].value
+ elif node.attributes["name"].value == "portalEffectFile":
+ portalEffectFile = node.attributes["value"].value
+ elif node.attributes["name"].value == "minimaps":
+ minimapsDir = node.attributes["value"].value
+ elif node.attributes["name"].value == "wallpapers":
+ wallpapersDir = node.attributes["value"].value
+ elif node.attributes["name"].value == "wallpaperFile":
+ wallpaperFile = node.attributes["value"].value
+ elif node.attributes["name"].value == "music":
+ musicDir = node.attributes["value"].value
+
+ except:
+ print "warn: paths.xml not found"
+ warnings = warnings + 1
+
+def splitImage(image):
+ try:
+ idx = image.find("|")
+ if idx > 0:
+ imagecolor = image[idx + 1:]
+ image = image[0:idx]
+ else:
+ imagecolor = ""
+ except:
+ image = ""
+ imagecolor = ""
+ return [image, imagecolor]
+
+def testDye(id, color, text, src, iserr):
+ if len(color) < 4:
+ showMsg(id, "dye to small size: " + text, src, iserr)
+ return
+ colors = dyesplit1.split(color)
+ for col in colors:
+ if len(col) < 4:
+ showMsg(id, "dye to small size: " + text, src, iserr)
+ continue
+
+ c = col[0];
+ if col[1] != ":":
+ showMsg(id, "incorrect dye string: " + text, src, iserr)
+ continue
+
+ if c != "R" and c != "G" and c != "B" and c != "Y" and c != "M" \
+ and c != "C" and c != "W" and c != "S":
+ showMsg(id, "incorrect dye color: " + c + " in " + text, src, iserr)
+ continue
+ if testDyeInternal(id, col[2:], text, src, iserr) == False:
+ continue
+
+
+def testDyeInternal(id, col, text, src, iserr):
+ oldPalette = col[0] == "#"
+ if oldPalette == False and col[0] != "@":
+ showMsg(id, "incorrect dye colors: " + text, src, iserr)
+ return False
+
+ if oldPalette:
+ paletes = dyesplit2.split(col[1:])
+ for palete in paletes:
+ if len(palete) != 6:
+ showMsg(id, "incorrect dye palete: " + text, src, iserr)
+ return False
+
+ for char in palete.lower():
+ if (char < '0' or char > '9') and (char < 'a' or char > 'f'):
+ showMsg(id, "incorrect dye palete: " + text, src, iserr)
+ return False
+ return True
+
+
+def testDyeColors(id, color, text, src, iserr):
+ if len(color) < 4:
+ showMsg(id, "dye to small size: " + text, src, iserr)
+ return -1
+ colors = dyesplit1.split(color)
+ for col in colors:
+ if len(col) < 4:
+ showMsg(id, "dye to small size: " + text, src, iserr)
+ continue
+ if testDyeInternal(id, col, text, src, iserr) == False:
+ continue
+ return len(colors)
+
+def testDyeChannel(file, color, text, iserr):
+ if len(color) < 1:
+ showMsgSprite(file, "dye channel size to small:" + text, iserr)
+ return -1
+ colors = dyesplit1.split(color)
+ for c in colors:
+ if len(c) != 1:
+ showMsgSprite(file, "dye channel incorrect size: " + text, iserr)
+ continue
+ if c != "R" and c != "G" and c != "B" and c != "Y" and c != "M" \
+ and c != "C" and c != "W" and c != "S":
+ showMsgSprite(file, "dye make incorrect: " + text, iserr)
+ continue
+ return len(colors)
+
+
+def testSprites(id, node, checkGender, isNormalDye, isMust, checkAction, iserr):
+ try:
+ tmp = node.getElementsByTagName("nosprite")
+ if tmp is not None and len(tmp) > 1:
+ showMsg(id, "more than one nosprite tag found", "", iserr)
+ nosprite = True
+ except:
+ nosprite = False
+
+ if isMust == False:
+ nosprite = True
+
+ try:
+ sprites = node.getElementsByTagName("sprite")
+ except:
+ sprites = None
+ if nosprite == False:
+ showMsg(id, "no sprite tag found", "", iserr)
+
+ if sprites is not None:
+ if len(sprites) == 0 or len(sprites[0].childNodes) == 0:
+ if nosprite == False:
+ showMsg(id, "no sprite tags found", "", iserr)
+ elif len(sprites) > 3 and checkGender:
+ showMsg(id, "incorrect number of sprite tags", "", iserr)
+ elif len(sprites) == 1:
+ file = sprites[0].childNodes[0].data
+ if checkGender:
+ try:
+ gender = sprites[0].attributes["gender"].value
+ except:
+ gender = ""
+
+ if gender != "" and gender != "unisex":
+ showMsg(id, "gender tag in alone sprite", "", iserr)
+
+ try:
+ variant = int(sprites[0].attributes["variant"].value)
+ except:
+ variant = 0
+
+ testSprite(id, file, variant, isNormalDye, checkAction, iserr)
+ else:
+ male = False
+ female = False
+ unisex = False
+ for sprite in sprites:
+ file = sprite.childNodes[0].data
+ if checkGender:
+ try:
+ gender = sprite.attributes["gender"].value
+ except:
+ gender = ""
+ if gender == "male":
+ if male == True:
+ showMsg(id, "double male sprite tag", "", iserr)
+ male = True
+ elif gender == "female":
+ if female == True:
+ showMsg(id, "double female sprite tag", "", iserr)
+ female = True
+ elif gender == "unisex":
+ unisex = True
+ try:
+ variant = int(sprite.attributes["variant"].value)
+ except:
+ variant = 0
+ testSprite(id, file, variant, isNormalDye, checkAction, iserr)
+ if checkGender:
+ if male == False and unisex == False:
+ showMsg(id, "no male sprite tag", "",iserr)
+ if female == False and unisex == False:
+ showMsg(id, "no female sprite tag", "", iserr)
+ if unisex == True and female == True and male == True:
+ showMsg(id, "gender sprite tag with unisex tag", "", iserr)
+ if unisex == False and male == False and female == False:
+ showMsg(id, "no any gender tags", "", iserr)
+
+def testSprite(id, file, variant, isNormalDye, checkAction, iserr):
+ global safeDye
+ tmp = splitImage(file)
+ color = tmp[1]
+ file2 = tmp[0]
+ if color != "":
+ dnum = testDyeColors(id, color, file, "", iserr)
+ else:
+ dnum = 0
+
+ fullPath = os.path.abspath(parentDir + "/" + spritesDir + file2)
+ if not os.path.isfile(fullPath) or os.path.exists(fullPath) == False:
+ showFileMsgById(id, spritesDir, file2, iserr)
+ else:
+ if not isNormalDye and color is not None and len(color) > 0:
+ showMsg(id, "sprite tag have dye string but it should not, because used colors dye", color, iserr)
+
+ oldSafe = safeDye
+ safeDye = True
+ testSpriteFile(id, fullPath, file, spritesDir + file2, dnum, variant, checkAction, iserr)
+ safeDye = oldSafe
+
+def powerOfTwo(num):
+ val = 1
+ while val < num:
+ val = val * 2
+ return val
+
+def testSpriteFile(id, fullPath, file, fileLoc, dnum, variant, checkAction, iserr):
+ global safeDye
+
+ try:
+ dom = minidom.parse(fullPath)
+ except:
+ return
+
+ if len(dom.childNodes) < 1:
+ return
+
+ try:
+ variants = dom.documentElement.attributes["variants"].value
+ except:
+ variants = 0
+
+# try:
+# variant_offset = dom.documentElement.attributes["variant_offset"].value
+# except:
+# variant_offset = 0
+
+# root = dom.childNodes[0];
+ imagesets = dom.getElementsByTagName("imageset")
+ if imagesets is None or len(imagesets) < 1:
+ showMsgSprite(fileLoc, "incorrect number of imageset tags", iserr)
+ return
+ isets = set()
+ imagesetnums = dict()
+ num = 0
+ for imageset in imagesets:
+ try:
+ name = imageset.attributes["name"].value
+ except:
+ showMsgSprite(fileLoc, "imageset don't have name attribute", iserr)
+ name = None
+
+ if name is not None:
+ if name in isets:
+ showMsgSprite(fileLoc, "imageset with name '" + name + "' already exists", iserr)
+ isets.add(name)
+
+ image = ""
+ try:
+ image = imageset.attributes["src"].value
+ image0 = image
+ img = splitImage(image)
+ image = img[0]
+ imagecolor = img[1]
+ except:
+ showMsgSprite(fileLoc, "image attribute not exist: " + image, iserr)
+ continue
+
+ try:
+ width = imageset.attributes["width"].value
+ except:
+ showMsgSprite(fileLoc, "no width attribute", iserr)
+ continue
+
+ try:
+ height = imageset.attributes["height"].value
+ except:
+ showMsgSprite(fileLoc, "no height attribute", iserr)
+
+ if imagecolor != "":
+ num = testDyeChannel(fileLoc, imagecolor, image0, iserr)
+ if safeDye == False and dnum != num:
+ if dnum > num:
+ e = iserr
+ else:
+ e = False
+ showMsgSprite(fileLoc, "dye colors size not same in sprite (" + str(num) \
+ + ") and in caller (" + str(dnum) + ", id=" + str(id) + ")", e)
+ elif safeDye == True and dnum > 0:
+ showMsgSprite(fileLoc, "dye set in sprite but not in caller (id=" + str(id) + ")", False)
+
+
+ fullPath = os.path.abspath(parentDir + "/" + image)
+ if not os.path.isfile(fullPath) or os.path.exists(fullPath) == False:
+ showMsgSprite(fileLoc, "image file not exist: " + image, iserr)
+ continue
+ sizes = testImageFile(image, fullPath, 0, " " + fileLoc, iserr)
+ s1 = int(sizes[0] / int(width)) * int(width)
+
+ sizesOGL = [0,1]
+ sizesOGL[0] = powerOfTwo(sizes[0])
+ sizesOGL[1] = powerOfTwo(sizes[1])
+
+ if s1 == 0:
+ tmp = int(width)
+ else:
+ tmp = s1
+ if sizes[0] != s1 and tmp != sizesOGL[0] and sizes[0] != sizesOGL[0]:
+ if silent != True:
+ showMsgSprite(fileLoc, "image width " + str(sizes[0]) + \
+ " (need " + str(tmp) + ") is not multiply to frame size " + width + ", image:" + image, False)
+
+ if sizes[0] != sizesOGL[0]:
+ if sizesOGL[0] > sizes[0]:
+ txt = str(sizesOGL[0] / 2) + " or "
+ else:
+ txt = ""
+
+ if showAll is True:
+ showMsgSprite(fileLoc, "image width should be power of two. If not image will be resized on the fly."\
+ "\nCurrent image width " + str(sizes[0]) + \
+ ". used in sprite width " + str(tmp) +
+ "\nallowed width " + txt + str(sizesOGL[0]) + " (" + image + ")", False)
+
+ s2 = int(sizes[1] / int(height)) * int(height)
+
+ if s2 == 0:
+ tmp = int(height)
+ else:
+ tmp = s2;
+
+ if sizes[1] != s2 and tmp != sizesOGL[1] and sizes[1] != sizesOGL[1]:
+ if silent != True:
+ showMsgSprite(fileLoc, "image height " + str(sizes[1]) + \
+ " (need " + str(tmp) + ") is not multiply to frame size " + height + ", image:" + image, False)
+
+ if sizes[1] != sizesOGL[1]:
+ if sizesOGL[1] > sizes[1]:
+ txt = str(sizesOGL[1] / 2) + " or "
+ else:
+ txt = ""
+
+ if showAll is True:
+ showMsgSprite(fileLoc, "image height should be power of two. If not image will be resized on the fly."\
+ "\nCurrent image height " + str(sizes[1]) + \
+ ". used in sprite height " + str(tmp) +
+ "\nallowed height " + txt + str(sizesOGL[1]) + " (" + image + ")", False)
+
+
+ num = (s1 / int(width)) * (s2 / int(height))
+ if variants == 0 and variant > 0:
+ showMsgSprite(fileLoc, "missing variants attribute in sprite", iserr)
+ if variants > 0 and variant >= variants:
+ showMsgSprite(fileLoc, "variant number more then in variants attribute", iserr)
+
+ if variant > 0 and variant >= num:
+ showMsgSprite(fileLoc, "to big variant number " + str(variant) \
+ + ". Frames number " + str(num) + ", id=" + str(id), iserr)
+ if num < 1:
+ showMsgSprite(fileLoc, "image have zero frames: " + image, iserr)
+ if name is not None and num > 0:
+ imagesetnums[name] = num
+
+ try:
+ includes = dom.getElementsByTagName("include")
+ for include in includes:
+ try:
+ incfile = include.attributes["file"].value
+ file2 = os.path.abspath(parentDir + os.path.sep + spritesDir + incfile)
+ if not os.path.isfile(file2):
+ showMsgSprite(fileLoc, "include file not exists " + incfile, True)
+ except:
+ showMsgSprite(fileLoc, "bad include", iserr)
+
+
+ except:
+ includes = None
+
+ #todo need parse included files
+
+ try:
+ actions = dom.getElementsByTagName("action")
+ except:
+ actions = None
+
+ if (actions == None or len(actions) == 0) and (includes == None or len(includes) == 0):
+ showMsgSprite(fileLoc, "no actions in sprite file", iserr)
+ else:
+ actset = set()
+ frameSet = set()
+ hpSet = set()
+ for action in actions:
+ try:
+ name = action.attributes["name"].value
+ except:
+ showMsgSprite(fileLoc, "no action name", iserr)
+ continue
+ try:
+ hp = action.attributes["hp"].value
+ except:
+ hp = "100"
+ try:
+ setname = action.attributes["imageset"].value
+ except:
+ setname = ""
+ if setname in imagesetnums:
+ num = imagesetnums[setname]
+ else:
+ num = 0
+ showMsgSprite(fileLoc, "using incorrect imageset name in action: " + name, iserr)
+ frameSet = frameSet | testSpriteAction(fileLoc, name, action, num, iserr)
+
+ if name + "|" + hp in actset:
+ showMsgSprite(fileLoc, "duplicate action: " + name, iserr)
+ continue
+ actset.add(name + "|" + hp)
+ hpSet.add(hp)
+
+ if len(frameSet) > 0:
+ errIds = ""
+ i = 0
+ while i < max(frameSet):
+ if i not in frameSet:
+ errIds = errIds + str(i) + ","
+ i = i + 1
+ if len(errIds) > 0:
+ if silent != True:
+ showMsgSprite(fileLoc, "unused frames: " + errIds[0:len(errIds)-1], False)
+
+ if checkAction != "":
+ for hp in hpSet:
+ if checkAction + "|" + hp not in actset:
+ showMsgSprite(fileLoc, "no attack action '" + checkAction + "' in sprite", iserr)
+
+
+def testSpriteAction(file, name, action, numframes, iserr):
+ framesid = set()
+
+ try:
+ animations = action.getElementsByTagName("animation")
+ except:
+ animations = None
+
+ if animations == None or len(animations) == 0:
+ if name != "default":
+ showMsgSprite(file, "no animation tags in action: " + name, False)
+ else:
+ return framesid
+
+ aniset = set()
+ delayTags = ("frame", "sequence", "pause")
+
+ for animation in animations:
+ lastAttack = None
+ try:
+ direction = animation.attributes["direction"].value
+ except:
+ direction = "default"
+
+ if direction is aniset:
+ showMsgSprite(file, "duplicate direction in action: " + name, iserr)
+ continue
+ aniset.add(direction)
+
+ lastIndex1 = -1
+ lastIndex2 = -1
+ lastOffsetX = 0
+ lastOffsetY = 0
+ cnt = 0
+ labels = set()
+
+ for node2 in animation.childNodes:
+ if name == "attack" and node2.nodeName != "#text":
+ lastAttack = node2.nodeName
+ if node2.nodeName in delayTags:
+ try:
+ delay = int(node2.attributes["delay"].value)
+ except:
+ delay = 0
+
+ if delay % 10 != 0 and showAll is True:
+ showMsgSprite(file, "delay " + str(delay) + " must be multiple of 10 in action: " + name + \
+ ", direction: " + direction, False)
+
+
+ if node2.nodeName == "frame" or node2.nodeName == "sequence":
+ try:
+ offsetX = int(node2.attributes["offsetX"].value)
+ except:
+ offsetX = 0
+ try:
+ offsetY = int(node2.attributes["offsetY"].value)
+ except:
+ offsetY = 0
+
+ if node2.nodeName == "frame":
+ frame = node2
+ try:
+ idx = int(frame.attributes["index"].value)
+ except:
+ showMsgSprite(file, "no frame index in action: " + name, iserr)
+
+ if idx >= numframes or idx < 0:
+ showMsgSprite(file, "incorrect frame index " + str(idx) + \
+ " action: " + name + ", direction: "\
+ + direction, iserr)
+ else:
+ framesid.add(idx)
+ if lastIndex1 == idx and lastIndex2 == -1 and offsetX == lastOffsetX \
+ and offsetY == lastOffsetY:
+ showMsgSprite(file, "duplicate frame animation for frame index=" \
+ + str(idx) + " action: " + name + \
+ ", direction: " + direction + "\n" + node2.toxml(), False)
+ #print node2.toxml()
+ else:
+ lastIndex1 = idx
+ lastIndex2 = -1
+ lastOffsetX = offsetX
+ lastOffsetY = offsetY
+
+ framesid.add(idx)
+ cnt = cnt + 1
+ elif node2.nodeName == "sequence":
+ sequence = node2
+ try:
+ sframes = dyesplit2.split(sequence.attributes["value"].value)
+ except:
+ sframes = None
+ if sframes is not None:
+ for frm in sframes:
+ if frm != "p":
+ k = frm.find("-")
+ if k == 0 or k == len(frm) - 1:
+ showMsgSprite(file, "incorrect sequence value " + \
+ name + ", direction: " + direction, iserr)
+ elif k == -1:
+ #same as frame
+ idx = int(frm)
+ if idx >= numframes or idx < 0:
+ showMsgSprite(file, "incorrect frame index " + str(idx) + \
+ " action: " + name + ", direction: "\
+ + direction, iserr)
+ else:
+ framesid.add(idx)
+ else:
+ #same as simple sequence
+ i1 = int(frm[:k])
+ i2 = int(frm[k + 1:])
+ if i1 >= numframes or i1 < 0:
+ showMsgSprite(file, "incorrect start sequence index " + str(i1) + \
+ " action: " + name + ", direction: " + direction, iserr)
+ if i2 >= numframes or i2 < 0:
+ showMsgSprite(file, "incorrect end sequence index " + str(i2) + \
+ " action: " + name + ", direction: " + direction, iserr)
+ if i1 == i2:
+ showMsgSprite(file, "start and end sequence index is same. " \
+ + "May be better use frame? action: " + \
+ name + ", direction: " + direction, False)
+
+ for i in range(i1,i2 + 1):
+ framesid.add(i)
+ cnt = cnt + 1
+ continue
+
+ try:
+ i1 = int(sequence.attributes["start"].value)
+ i2 = int(sequence.attributes["end"].value)
+ except:
+ showMsgSprite(file, "no sequence start or end index action: " + \
+ name + ", direction: " + direction, iserr)
+# try:
+# repeat = int(sequence.attributes["repeat"].value)
+# except:
+# repeat = 1
+
+ if i1 >= numframes or i1 < 0:
+ showMsgSprite(file, "incorrect start sequence index " + str(i1) + \
+ " action: " + name + ", direction: " + direction, iserr)
+ if i2 >= numframes or i2 < 0:
+ showMsgSprite(file, "incorrect end sequence index " + str(i2) + \
+ " action: " + name + ", direction: " + direction, iserr)
+ if i1 == i2:
+ showMsgSprite(file, "start and end sequence index is same. " \
+ + "May be better use frame? action: " + \
+ name + ", direction: " + direction, False)
+
+ if lastIndex1 == i1 and lastIndex2 == i2 and offsetX == lastOffsetX \
+ and offsetY == lastOffsetY:
+ showMsgSprite(file, "duplicate sequence animation. May be need use repeat attribue? for start=" \
+ + str(i1) + ", end=" + str(i2) + " action: " + \
+ name + ", direction: " + direction + "\n" + node2.toxml(), False)
+ else:
+ lastIndex1 = i1
+ lastIndex2 = i2
+ lastOffsetX = offsetX
+ lastOffsetY = offsetY
+
+ cnt = cnt + 1
+ for i in range(i1,i2 + 1):
+ framesid.add(i)
+ elif node2.nodeName == "end" or node2.nodeName == "jump" or node2.nodeName == "label" or node2.nodeName == "goto":
+ lastIndex1 = -1
+ lastIndex2 = -1
+ lastOffsetX = 0
+ lastOffsetY = 0
+ cnt = cnt + 1
+ elif node2.nodeName == "pause":
+ try:
+ delay = int(node2.attributes["delay"].value)
+ except:
+ delay = 0
+ if delay <= 0:
+ showMsgSprite(file, "incorrect delay in pause tag " + name, iserr)
+
+ elif node2.nodeName == "#text" or node2.nodeName == "#comment":
+ None
+ else:
+ showMsgSprite(file, "unknown animation tag: " + node2.nodeName + ", " + name, False)
+
+ if node2.nodeName == "jump":
+ try:
+ jaction = node2.attributes["action"].value
+ except:
+ jaction = ""
+ if jaction == "" or jaction is None:
+ showMsgSprite(file, "no action attribute in jump tag " + name, iserr)
+ elif node2.nodeName == "label":
+ try:
+ label = node2.attributes["name"].value
+ except:
+ label = ""
+ if label == "" or label is None:
+ showMsgSprite(file, "no name attribute in label tag " + name, iserr)
+ else:
+ if label in labels:
+ showMsgSprite(file, "duplicate label " + label + " " + name + "\n" \
+ + node2.toxml(), iserr)
+ else:
+ labels.add(label)
+ elif node2.nodeName == "goto":
+ try:
+ label = node2.attributes["label"].value
+ except:
+ label = ""
+ if label == "" or label is None:
+ showMsgSprite(file, "no label attribute in goto tag " + name, iserr)
+ if cnt == 0:
+ showMsgSprite(file, "no frames or sequences in action: " + name, iserr)
+
+ if name == "attack":
+ if lastAttack is not None and lastAttack != "end":
+ showMsgSprite(file, "last attack tag should be <end/> or attack animation "\
+ "can be infinite. direction: " + direction, False)
+
+
+ if "default" not in aniset:
+ if "down" not in aniset:
+ showMsgSprite(file, "no down direction in animation: " + name, iserr)
+ if "up" not in aniset:
+ showMsgSprite(file, "no up direction in animation: " + name, iserr)
+ if "left" not in aniset:
+ showMsgSprite(file, "no left direction in animation: " + name, iserr)
+ if "right" not in aniset:
+ showMsgSprite(file, "no right direction in animation: " + name, iserr)
+
+ if name == "dead" and len(animations) > 0:
+ lastani = animations[len(animations) - 1]
+ lastNode = None
+ nc = 0
+ for node in lastani.childNodes:
+ if node.nodeName == "frame":
+ lastNode = node
+ nc = nc + 1
+ if node.nodeName == "sequence":
+ lastNode = node
+ nc = nc + 2
+ if nc > 1:
+ try:
+ cont = int(lastNode.attributes["continue"].value)
+ except:
+ cont = 0;
+ if cont == 0:
+ try:
+ delay = int(lastNode.attributes["delay"].value)
+ except:
+ delay = 0
+ if delay > 0 and delay < 5000:
+ showMsgSprite(file, "last frame\sequence in dead animation have to low limit. Need zero or >5000: " + name, False)
+
+ return framesid
+
+
+def testImageFile(file, fullPath, sz, src, iserr):
+ try:
+ img = Image.open(fullPath, "r")
+ img.load()
+ except:
+ showMsgFile(file, "incorrect image format" + src, iserr)
+ return
+
+ if img.format != "PNG":
+ showMsgFile(file, "image format is not png" + src, False)
+
+ sizes = img.size
+ if sz != 0:
+ if sizes[0] > sz or sizes[1] > sz:
+ showMsgFile(file, "image size incorrect (" + str(sizes[0]) \
+ + "x" + str(sizes[1]) + ") should be (" + str(sz) + "x" \
+ + str(sz) + ")", iserr)
+ elif sizes[0] < sz or sizes[1] < sz:
+ showMsgFile(file, "possible image size incorrect (" + str(sizes[0]) \
+ + "x" + str(sizes[1]) + ") should be (" + str(sz) + "x" \
+ + str(sz) + ")", False)
+
+ return sizes
+
+def testSound(file, sfxDir, msg):
+ fullPath = parentDir + "/" + sfxDir + file
+ if not os.path.isfile(fullPath) or os.path.exists(fullPath) == False:
+ print "error:" + fullPath
+ if msg != "":
+ showMsgFile(file, "sound file not found: " + msg , True)
+ else:
+ showMsgFile(file, "sound file not found", True)
+ return
+ try:
+ ogg.vorbis.VorbisFile(fullPath)
+ except ogg.vorbis.VorbisError as e:
+ showMsgFile(file, "sound file incorrect error: " + str(e), True)
+
+
+def testParticle(id, file, src):
+ fullPath = parentDir + "/" + file
+ if not os.path.isfile(fullPath) or os.path.exists(fullPath) == False:
+ showMsgFile(file, "particle file not found", True)
+ return
+ try:
+ dom = minidom.parse(fullPath)
+ except:
+ showMsgFile(file, "incorrect particle xml file", True)
+ return
+
+ nodes = dom.getElementsByTagName("particle")
+ if len(nodes) < 1:
+ showMsgFile(file, "missing particle tags", False)
+ else:
+ for node in nodes:
+ testEmitters(id, file, node, file)
+
+
+def testEmitters(id, file, parentNode, src):
+ for node in parentNode.getElementsByTagName("property"):
+ try:
+ name = node.attributes["name"].value
+ except:
+ showMsgFile(file, "missing attribute name in emitter" \
+ " in particle file", True)
+ continue
+ try:
+ value = node.attributes["value"].value
+ except:
+ value = None
+
+ if name == "image":
+ if value == None:
+ showMsgFile(file, "missing attribute value in emitter" \
+ " image attribute", True)
+ img = splitImage(value)
+ image = img[0]
+ imagecolor = img[1]
+ if imagecolor != None and len(imagecolor) > 0:
+ testDye(id, imagecolor, "image=" + image, src, True)
+ fullName = parentDir + "/" + image
+ if not os.path.isfile(fullName) or os.path.exists(fullName) == False:
+ showMsgFile(file, "image file not exist: " + image, True)
+ else:
+ testImageFile(image, fullName, 0, " " + file,True)
+ for node in parentNode.getElementsByTagName("emitter"):
+ testEmitters(id, file, node, src)
+
+
+
+def testItems(fileName, imgDir):
+ global warnings, errors, safeDye
+ if not stfu:
+ print "Checking " + fileName
+ try:
+ dom = minidom.parse(parentDir + "/" + fileName)
+ except Exception as err:
+ print "error: " + fileName + ": corrupted"
+ print err
+ errors = errors + 1
+ return
+ idset = set()
+ oldId = None
+ for node in dom.documentElement.childNodes:
+ if node.nodeName == "include":
+ try:
+ name = node.attributes["name"].value
+ if name == "":
+ errors = errors + 1
+ print "error: " + fileName + ": Empty include name";
+ testItems(name, imgDir)
+ except:
+ errors = errors + 1
+ print "error: " + fileName + ": Broken include tag";
+ continue
+ if node.nodeName != "item":
+ continue
+
+ if node.parentNode != dom.documentElement:
+ continue
+
+ try:
+ id = node.attributes["id"].value
+ except:
+ if oldId is None:
+ print "error: " + fileName + ": item without id"
+ else:
+ print "error: " + fileName + ": item without id. Last id was: " + oldId
+ errors = errors + 1
+ continue
+ oldId = id
+ if id in idset:
+ print "error: " + fileName + ": duplicated id=" + id
+ errors = errors + 1
+ else:
+ idset.add(id)
+
+ idI = int(id)
+
+ try:
+ colors = node.attributes["colors"].value
+ except:
+ colors = None
+
+ try:
+ type = node.attributes["type"].value
+ except:
+ type = ""
+ print "warn: " + fileName + ": no type attribute for id=" + id
+ warnings = warnings + 1
+ try:
+ image = node.attributes["image"].value
+ image0 = image
+ img = splitImage(image)
+ image = img[0]
+ imagecolor = img[1]
+ except:
+ image = ""
+ image0 = ""
+ imagecolor = ""
+
+ try:
+ floor = node.attributes["floor"].value
+ floor0 = floor
+ flr = splitImage(floor)
+ floor = flr[0]
+ floorcolor = flr[1]
+ except:
+ floor = None
+ floor0 = None
+ floorcolor = None
+
+ try:
+ description = node.attributes["description"].value
+ except:
+ description = ""
+
+ try:
+ missile = node.attributes["missile-particle"].value
+ except:
+ missile = ""
+
+ try:
+ drawBefore = node.attributes["drawBefore"].value
+ except:
+ drawBefore = ""
+
+ try:
+ drawAfter = node.attributes["drawAfter"].value
+ except:
+ drawAfter = ""
+
+# try:
+# drawPriority = int(node.attributes["drawPriority"].value)
+# except:
+# drawPriority = 0
+
+ if type == "hairsprite":
+ if idI >= 0:
+ print "error: " + fileName + ": hairsprite with id=" + id
+ errors = errors + 1
+ elif idI < -100:
+ print "error: " + fileName + ": hairsprite override player sprites"
+ errors = errors + 1
+
+ safeDye = True
+ testSprites(id, node, True, True, True, "", True)
+ safeDye = False
+
+ elif type == "racesprite":
+ if idI >= 0:
+ print "error: " + fileName + ": racesprite with id=" + id
+ errors = errors + 1
+ elif idI > -100:
+ print "error: " + fileName + ": racesprite override player hair"
+ errors = errors + 1
+ elif type == "usable" or type == "unusable" or type == "generic" \
+ or type == "equip-necklace" or type == "equip-torso" or type == "equip-feet" \
+ or type == "equip-arms" or type == "equip-legs" or type == "equip-head" \
+ or type == "equip-shield" or type == "equip-1hand" or type == "equip-2hand" \
+ or type == "equip-charm" or type == "equip-ammo" or type == "equip-neck" \
+ or type == "equip-ring" or type == "card":
+ if image == "":
+ print "error: " + fileName + ": missing image attribute on id=" + id
+ errors = errors + 1
+ continue
+ elif len(imagecolor) > 0:
+ if colors is None:
+ testDye(id, imagecolor, "image=" + image0, fileName, True)
+ else:
+ testDyeChannel(id, imagecolor, "image=" + image0, True)
+ if colors not in colorsList:
+ print "error: " + fileName + ": colors value " + colors + " not found in itemcolors.xml"
+ errors = errors + 1
+
+ if floorcolor != None and len(floorcolor) > 0:
+ if colors is None:
+ testDye(id, floorcolor, "floor=" + floor0, fileName, True)
+ else:
+ testDyeChannel(id, imagecolor, "floor=" + floor0, True);
+ if colors not in colorsList:
+ print "error: " + fileName + ": colors value " + colors + " not found in itemcolors.xml"
+ errors = errors + 1
+
+ if description == "":
+ print "warn: " + fileName + ": missing description attribute on id=" + id
+ warnings = warnings + 1
+ elif description == ".":
+ print "warn: " + fileName + ": broken description attribute on id=" + id
+ warnings = warnings + 1
+
+ if missile != "":
+ testParticle(id, missile, fileName)
+
+ testSounds(id, node, "item")
+
+ try:
+ floorSprite = node.getElementsByTagName("floor")[0]
+ except:
+ floorSprite = None
+ if floorSprite != None:
+ if floor != None:
+ print "error: " + fileName + ": found attribute floor and tag floor. " + \
+ "Should be only one tag or attribute. id=" + id
+ errors = errors + 1
+ testSprites(id, floorSprite, False, colors is None, True, "", err)
+
+ fullPath = os.path.abspath(parentDir + "/" + imgDir + image)
+ if not os.path.isfile(fullPath) or os.path.exists(fullPath) == False:
+ showFileErrorById (id, imgDir, image)
+ errors = errors + 1
+ else:
+ testImageFile(imgDir + image, fullPath, 32, "", True)
+
+ if floor != None:
+ fullPath = os.path.abspath(parentDir + "/" + imgDir + floor)
+ if not os.path.isfile(fullPath) or os.path.exists(fullPath) == False:
+ showFileErrorById (id, imgDir, floor)
+ errors = errors + 1
+ else:
+ testImageFile(imgDir + floor, fullPath, 0, "", True)
+
+ testItemReplace(id, node, "replace")
+ if drawBefore != "":
+ checkSpriteName(id, drawBefore)
+ if drawAfter != "":
+ checkSpriteName(id, drawAfter)
+
+ try:
+ attackaction = node.attributes["attack-action"].value
+ except:
+ attackaction = ""
+
+ testSprites(id, node, True, colors is None, False, attackaction, True)
+
+ if type != "usable" and type != "unusable" and type != "generic" \
+ and type != "equip-necklace" and type != "equip-1hand" \
+ and type != "equip-2hand" and type != "equip-ammo" \
+ and type != "equip-charm" and type != "equip-neck":
+ err = type != "equip-shield"
+ testSprites(id, node, True, colors is None, True, "", err)
+ elif type == "other":
+ None
+ elif type != "":
+ print "warn: " + fileName + ": unknown type '" + type + "' for id=" + id
+ warnings = warnings + 1
+
+
+def testItemReplace(id, rootNode, name):
+ global warnings, errors
+ for node in rootNode.getElementsByTagName(name):
+ if node.parentNode != rootNode:
+ continue
+ try:
+ sprite = node.attributes["sprite"].value
+ except:
+ if len(node.attributes) != 0:
+ print "error: reading replace sprite name, id=" + str(id)
+ errors = errors + 1
+ continue
+ checkSpriteName(id, sprite)
+ for itemNode in node.getElementsByTagName("item"):
+ if itemNode.parentNode != node:
+ continue
+ #TODO here need check "from" and "to" for correct item id
+
+
+def checkSpriteName(id, name):
+ global warnings, errors
+ if name != "race" and name != "type" and name != "shoes" and name != "boot" and \
+ name != "boots" and name != "bottomclothes" \
+ and name != "bottom" and name != "pants" and name != "topclothes" and \
+ name != "top" and name != "torso" and name != "body" and name != "misc1" \
+ and name != "misc2" and name != "scarf" and name != "scarfs" and \
+ name != "hair" and name != "hat" and name != "hats" and name != "wings" \
+ and name != "glove" and name != "gloves" and name != "weapon" and \
+ name != "weapons" and name != "shield" and name != "shields" and \
+ name != "amulet" and name != "amulets" and name != "ring" and name != "rings":
+ print "error: unknown sprite name " + name + ", id=" + str(id)
+ errors = errors + 1
+
+
+def testMonsters(fileName):
+ global warnings, errors
+ if not stfu:
+ print "Checking " + fileName
+ dom = minidom.parse(parentDir + "/" + fileName)
+ idset = set()
+ for node in dom.documentElement.childNodes:
+ if node.nodeName == "include":
+ try:
+ name = node.attributes["name"].value
+ if name == "":
+ errors = errors + 1
+ print "error: " + fileName + ": Empty include name";
+ testMonsters(name)
+ except:
+ errors = errors + 1
+ print "error: " + fileName + ": Broken include tag";
+ continue
+ if node.nodeName == "monster":
+ try:
+ id = node.attributes["id"].value
+ except:
+ print "error: " + fileName + ": no id for monster"
+ errors = errors + 1
+ continue
+
+ if id in idset:
+ print "error: " + fileName + ": duplicate id=" + id
+ errors = errors + 1
+ else:
+ idset.add(id)
+
+ try:
+ name = node.attributes["name"].value
+ except:
+ print "error: " + fileName + ": no name for id=" + id
+ errors = errors + 1
+ name = ""
+
+ testTargetCursor(id, node, fileName)
+ testSprites(id, node, False, True, True, "", True)
+ testSounds(id, node, "monster")
+ testParticles(id, node, "particlefx", fileName)
+
+def testTargetCursor(id, node, file):
+ try:
+ targetCursor = node.attributes["targetCursor"].value
+ if targetCursor != "small" and targetCursor != "medium" and targetCursor != "large":
+ showMsgFile(id, "unknown target cursor " + targetCursor, True)
+ except:
+ None
+
+def testParticles(id, node, nodeName, src):
+ particles = node.getElementsByTagName(nodeName)
+ for particle in particles:
+ try:
+ particlefx = particle.childNodes[0].data
+ except:
+ showMsgFile(id, "particle tag have incorrect data", True)
+
+ testParticle(id, particlefx, src)
+
+
+
+def testSounds(id, node, type):
+ global errors
+ havemiss = False
+ for sound in node.getElementsByTagName("sound"):
+ try:
+ event = sound.attributes["event"].value
+ except:
+ print "error: no sound event name in id=" + id
+ errors = errors + 1
+
+ if type == "monster":
+ if event != "hit" and event != "miss" and event != "hurt" and event != "die" \
+ and event != "move" and event != "sit" and event != "spawn":
+ print "error: incorrect sound event name " + event + " in id=" + id
+ errors = errors + 1
+ elif type == "item":
+ if event != "hit" and event != "strike" and event != "miss":
+ print "error: incorrect sound event name " + event + " in id=" + id
+ errors = errors + 1
+ if event == "strike" or event == "miss":
+ if havemiss:
+ print "error: miss and strike attributes at same time in id=" + id
+ errors = errors + 1
+ havemiss = True
+
+ testSound(sound.childNodes[0].data, sfxDir, "")
+
+def testNpcs(file):
+ global warnings, errors
+ if not stfu:
+ print "Checking " + file
+ dom = minidom.parse(parentDir + "/" + file)
+ idset = set()
+ for node in dom.documentElement.childNodes:
+ if node.nodeName == "include":
+ try:
+ name = node.attributes["name"].value
+ if name == "":
+ errors = errors + 1
+ print "error: " + file + ": Empty include name";
+ testNpcs(name)
+ except:
+ errors = errors + 1
+ print "error: " + file + ": Broken include tag";
+ continue
+ if node.nodeName != "npc":
+ continue
+
+ try:
+ id = node.attributes["id"].value
+ except:
+ print "error: " + file + ": no id for npc"
+ errors = errors + 1
+ continue
+
+ if id in idset:
+ print "error: " + file + ": duplicate npc id=" + id
+ errors = errors + 1
+ else:
+ idset.add(id)
+
+ testSprites(id, node, False, True, True, "", True)
+ testParticles(id, node, "particlefx", file)
+
+def readAttrI(node, attr, dv, msg, iserr):
+ return int(readAttr(node, attr, dv, msg, iserr))
+
+def readAttr(node, attr, dv, msg, iserr):
+ global warnings, errors
+ try:
+ return node.attributes[attr].value
+ except:
+ print msg
+ if iserr:
+ errors = errors + 1
+ else:
+ warnings = warnings + 1
+ return dv
+
+def readAttr2(node, attr, dv):
+ try:
+ return node.attributes[attr].value
+ except:
+ return dv
+
+
+def testMap(mapName, file, path):
+ global warnings, errors
+ fullPath = parentDir + "/" + path
+ dom = minidom.parse(fullPath)
+ root = dom.documentElement
+ mapWidth = readAttrI(root, "width", 0, "error: missing map width: " + file, True)
+ mapHeight = readAttrI(root, "height", 0, "error: missing map height: " + file, True)
+ mapTileWidth = readAttrI(root, "tilewidth", 0, "error: missing tile width: " + file, True)
+ mapTileHeight = readAttrI(root, "tileheight", 0, "error: missing tile height: " + file, True)
+ mapVersion = readAttr(root, "version", "1.0", "error: missing map version: " + file, True)
+
+ if mapWidth == 0 or mapHeight == 0 or mapTileWidth == 0 or mapTileHeight == 0:
+ return
+
+ mapVersion = mapVersion.replace(".", "")
+ try:
+ mapVersion = int(mapVersion)
+ except:
+ showMsgFile(file, "Invalid map version: " + str(mapVersion), False)
+
+ if mapVersion < tiledVersion:
+ showMsgFile(file, "Outdated map version: " + str(mapVersion), False)
+
+ if mapWidth < borderSize * 2 + 1:
+ if silent == False or file.find("maps/test") != 0:
+ showMsgFile(file, "map width to small: " + str(mapWidth), False)
+ if mapHeight < borderSize * 2 + 1:
+ if silent == False or file.find("maps/test") != 0:
+ showMsgFile(file, "map height to small: " + str(mapHeight), False)
+
+ if len(dom.getElementsByTagName("properties")) < 1:
+ showMsgFile(file, "missing map properties", True)
+ return
+
+ for props in dom.getElementsByTagName("properties"):
+ for prop in props.getElementsByTagName("property"):
+ try:
+ name = prop.attributes["name"].value
+ except:
+ name = ""
+ if name == "":
+ showMsgFile(file, "wrong property", True)
+ continue
+ try:
+ value = prop.attributes["value"].value
+ except:
+ value = ""
+ if value == "" and name == "name":
+ showMsgFile(file, "empty map name property", True)
+ continue
+
+ # Total minimum required width
+ if mapWidth < 60:
+ name1=file.find("maps/test")
+ name2=file.find("maps/000-1")
+ if name1 == 0 or name2 == 0:
+ pass
+ else:
+ showMsgFile(file, "total map width to small: " + str(mapWidth), False)
+
+ tilesMap = dict()
+
+ for tileset0 in dom.getElementsByTagName("tileset"):
+ tileset = tileset0
+ try:
+ firstGid = int(tileset.attributes["firstgid"].value)
+ except:
+ firstGid = 0
+
+ try:
+ source = tileset.attributes["source"].value
+ if source is not None and source != "":
+ file2 = os.path.abspath(parentDir + os.path.sep + mapsDir + source)
+ if not os.path.isfile(file2):
+ showMsgFile(file, "missing source file in tileset " + source, True)
+ except:
+ source = ""
+
+ tile = Tileset()
+ tile.firstGid = firstGid
+ tile.lastGid = 0
+
+ if source[-4:] == ".tsx":
+ relativePath = parentDir + "/" + mapsDir + source
+ try:
+ dom2 = minidom.parse(relativePath)
+ tileset = dom2.documentElement
+ idx = relativePath.rfind("/")
+ relativePath = relativePath[:idx+1]
+ relativePath2 = source
+ idx = relativePath2.rfind("/")
+ relativePath2 = relativePath2[:idx+1]
+ except:
+ showMsgFile(file, "tsx not found: " + source, True)
+ relativePath = ""
+ relativePath2 = ""
+ else:
+ relativePath = ""
+ relativePath2 = ""
+
+ name = readAttr(tileset, "name", "", "warning: missing tile name: " + file, False)
+ tileWidth = readAttrI(tileset, "tilewidth", mapTileWidth, \
+ "error: missing tile width in tileset: " + name + ", " + file, True)
+ tileHeight = readAttrI(tileset, "tileheight", mapTileHeight, \
+ "error: missing tile height in tileset: " + name + ", " + file, True)
+ if firstGid in tilesMap:
+ showMsgFile(file, "tile with firstgid " + str(firstGid) + \
+ " already exist: " + name + ", " + file, True)
+ continue
+
+ tile.width = tileWidth
+ tile.tileWidth = tileWidth
+ tile.height = tileHeight
+ tile.tileHeight = tileHeight
+ tile.name = name
+
+ images = tileset.getElementsByTagName("image")
+ if images == None or len(images) == 0:
+ showMsgFile(file, "missing image tags in tile " + name, True)
+ continue
+ elif len(images) > 1:
+ showMsgFile(file, "to many image tags in tile " + name, True)
+ continue
+
+ image = images[0]
+ source = readAttr(image, "source", None, "error: missing source in image tag in tile " \
+ + name + ": " + file, True)
+
+ if source != None:
+ if relativePath == "":
+ imagePath = os.path.abspath(parentDir + "/" + mapsDir + source)
+ else:
+ imagePath = os.path.abspath(relativePath + source)
+
+ img = splitImage(imagePath)
+ imagePath = img[0]
+ imagecolor = img[1]
+
+ tile.image = imagePath
+ tile.color = imagecolor
+
+ if not os.path.isfile(imagePath) or os.path.exists(imagePath) == False:
+ showMsgFile(file, "image file not exist: " + mapsDir + source + ", " + \
+ name, True)
+ continue
+
+ if imagecolor != "":
+ testDye("", imagecolor, source, file, True)
+
+ sz = testImageFile(file, imagePath, 0, "", True)
+ width = sz[0]
+ height = sz[1]
+
+ if width == 0 or height == 0:
+ continue
+
+ if width < tileWidth:
+ showMsgFile(file, "tile width more than image width in tile: " + \
+ name, True)
+ continue
+ if height < tileHeight:
+ showMsgFile(file, "tile height more than image height in tile: " + \
+ name, True)
+ continue
+
+ s1 = int(width / int(tileWidth)) * int(tileWidth)
+
+ if width != s1:
+ if s1 == 0:
+ s1 = int(tileWidth)
+ showMsgFile(file, "image width " + str(width) + \
+ " (need " + str(s1) + ") is not multiply to tile size " + \
+ str(tileWidth) + ". " + source + ", " + name, False)
+
+ s2 = int(height / int(tileHeight)) * int(tileHeight)
+
+ tile.lastGid = tile.firstGid + (int(width / int(tileWidth)) * int(height / int(tileHeight))) - 1
+ if height != s2:
+ if s2 == 0:
+ s2 = int(tileHeight)
+ showMsgFile(file, "image width " + str(height) + \
+ " (need " + str(s2) + ") is not multiply to tile size " + \
+ str(tileHeight) + ". " + source + ", " + name, False)
+
+ tile.source = relativePath2 + source
+ # hack to change relative back path to normal relative path
+ if len(tile.source) > 3 and tile.source[:11] == "../graphics":
+ tile.source = tile.source[3:]
+ tilesMap[tile.firstGid] = tile
+
+ #if mapName not in mapToAtlas:
+ # showMsgFile(file, "map dont have atlas", True)
+
+ tileset = tileset0
+
+ testTiles(mapName, file, tilesMap)
+ layers = dom.getElementsByTagName("layer")
+ objects = dom.getElementsByTagName("object")
+ if layers == None or len(layers) == 0:
+ showMsgFile(file, "map dont have layers", True)
+ return
+
+ fringe = None
+ collision = None
+ lowLayers = []
+ overLayers = []
+ beforeFringe = True
+ haveHeight = False
+
+ for layer in layers:
+ name = readAttr(layer, "name", None, "layer dont have name", True)
+ if name == None:
+ continue
+ if name.lower() == "height" or name.lower() == "heights":
+ haveHeight=True
+ obj = Layer()
+ obj.name = name
+ if name.lower() == "fringe":
+ if fringe is not None:
+ showMsgFile(file, "duplicate Fringe layer", True)
+ fringe = obj
+ beforeFringe = False
+ elif name.lower() == "collision":
+ if collision is not None:
+ showMsgFile(file, "duplicate Collision layer", True)
+ collision = obj
+ elif beforeFringe == True:
+ lowLayers.append(obj)
+ else:
+ overLayers.append(obj)
+
+ width = readAttrI(layer, "width", 0, "error: missing layer width: " + name + \
+ ", " + file, True)
+ height = readAttrI(layer, "height", 0, "error: missing layer height: " + name + \
+ ", " + file, True)
+ if width == 0 or height == 0:
+ continue
+
+ obj.width = width
+ obj.height = height
+
+ if mapWidth < width:
+ showMsgFile(file, "layer width " + str(width) + " more than map width " + \
+ str(mapWidth) + ": " + name, True)
+ if mapHeight < height:
+ showMsgFile(file, "layer height " + str(height) + " more then map height " + \
+ str(mapHeight) + ": " + name, True)
+
+ obj = testLayer(file, layer, name, width, height, obj, tilesMap)
+ testOverSizedTiles(obj, tilesMap, file)
+
+ if fringe == None:
+ showMsgFile(file, "missing fringe layer", True)
+ if collision == None:
+ showMsgFile(file, "missing collision layer", True)
+ elif mapName != "test.tmx" and mapName != "testbg.tmx":
+ ids = testCollisionLayer(file, collision, tilesMap)
+ if ids[0] != None and len(ids[0]) > 0:
+ if silent == False or file.find("maps/test") != 0:
+ showLayerErrors(file, ids[0], "empty tiles in collision border", False)
+ if ids[1] != None and len(ids[1]) > 0:
+ if silent == False or file.find("maps/test") != 0:
+ showLayerErrors(file, ids[1], "incorrect tileset index in collision layer", False)
+
+ if len(lowLayers) < 1:
+ showMsgFile(file, "missing low layers", False)
+ if len(overLayers) < 1:
+ if (silent == False or file.find("maps/test") != 0) and herc == False:
+ showMsgFile(file, "missing over layers", False)
+
+ if not haveHeight:
+ showMsgFile(file, "missing height layer", False)
+
+ if fringe != None:
+ lowLayers.append(fringe)
+ warn1 = None
+
+ if len(overLayers) > 0:
+ testData = dict()
+ warn1 = testLayerGroups(file, lowLayers, collision, None, tilesMap, False)
+ lowLayers.extend(overLayers)
+ err1 = testLayerGroups(file, lowLayers, collision, testData, tilesMap, False)
+ reportAboutTiles(file, testData)
+ else:
+ testData = dict()
+ err1 = testLayerGroups(file, lowLayers, collision, testData, tilesMap, False)
+ reportAboutTiles(file, testData)
+
+ if warn1 != None and err1 != None:
+ warn1 = warn1 - err1
+ if warn1 != None and len(warn1) > 0:
+ if silent != True:
+ showLayerErrors(file, warn1, "empty tile in lower layers", False)
+ if err1 != None and len(err1) > 0:
+ showLayerErrors(file, err1, "empty tile in all layers", True)
+
+ for objx in objects:
+ x = readAttr(objx, "x", 0, "object in invalid X position", False)
+ y = readAttr(objx, "y", 0, "object in invalid Y position", False)
+ w = readAttr2(objx, "width", 0)
+ h = readAttr2(objx, "height", 0)
+ try:
+ fs=False
+ if (float(x) != int(x)):
+ showMsgFile(file, "Invalid object X pos - must be integer", False)
+ fs=1
+ if (float(y) != int(y)):
+ showMsgFile(file, "Invalid object Y pos - must be integer", False)
+ fs=1
+ if (float(w) != int(w)):
+ showMsgFile(file, "Invalid object Width - must be integer", False)
+ fs=1
+ if (float(h) != int(h)):
+ showMsgFile(file, "Invalid object Height - must be integer", False)
+ fs=1
+ if fs:
+ id1=readAttr(objx, "id", "?", "invalid object ID", False)
+ name1=readAttr(objx, "name", "?", "invalid object name", False)
+ type1=readAttr(objx, "type", "?", "invalid object type", False)
+ showMsgFile(file, "Broken object: id %s name %s (%s,%s,%s,%s) type %s" % (id1, name1, x, y, w, h, type1), True);
+ except:
+ id1=readAttr(objx, "id", "?", "invalid object ID", False)
+ showMsgFile(file, "Broken object ID %s - x/y/h/w corrupted data detected" % id1, True)
+
+def testOverSizedTiles(layer, tiles, file):
+ global warnings, errors
+
+ oversizeErrList = []
+ ignoreErrList = []
+ ignoreTilesetList = set()
+ ignoredFiles = []
+ if "ignored" in atlasToFiles:
+ ignoredFiles = atlasToFiles["ignored"]
+ for x in range(0, layer.width):
+ for y in range(0, layer.height):
+ idx = ((y * layer.width) + x) * 4
+ val = getLDV(layer.arr, idx)
+ if val == 0:
+ continue
+
+ tile, tilesetName = findTileByGid(tiles, val)
+ if layer.name.lower() not in ("collision", "heights", "height") and tilesetName in ignoredFiles:
+ ignoreErrList.append((x, y))
+ ignoreTilesetList.add(tilesetName)
+ if layer.name.lower() == "fringe":
+ continue
+ if tile is None:
+ # now ignoring, this happend because layer parser
+ # not support includes
+ None
+ elif tile.tileWidth > 32 and x + 1 < layer.width:
+ for x2 in range(x + 1, x + 1 + int(tile.width / 32), 1):
+ idx = ((y * layer.width) + x2) * 4
+ val = getLDV(layer.arr, idx)
+ tile, _ = findTileByGid(tiles, val)
+ if val > 0:
+ oversizeErrList.append((x, y))
+ if silent != True:
+ warnings = warnings + 1
+ elif tile.tileHeight > 32 and y - 1 > 0:
+ for y2 in range(y - 1, y - 1 - int(tile.height / 32), -1):
+ idx = ((y2 * layer.width) + x) * 4
+ val = getLDV(layer.arr, idx)
+ tile, _ = findTileByGid(tiles, val)
+ if val > 0:
+ oversizeErrList.append((x, y))
+ if silent != True:
+ warnings = warnings + 1
+
+ if len(oversizeErrList) > 0 and silent != True:
+ print "error: " + file + ": Oversized tile overlapped to next tile in layer " + layer.name + \
+ ". Possible incorrect map drawing"
+ errors = errors + 1
+ errStr = ""
+ k = 0
+ for err in oversizeErrList:
+ errStr = errStr + str(err) + ", "
+ k = k + 1
+ if k > 100:
+ errStr = errStr + "..."
+ break
+ print errStr
+
+ if len(ignoreErrList) > 0:
+ errStr = ""
+ for err in ignoreTilesetList:
+ if errStr != "":
+ errStr = errStr + ", "
+ errStr = errStr + err
+ print("error: {0}: Tiles from ignored atlas used in layer {1}. Tilesets: {2}. "
+ "Possible incorrect map drawing".format(file, layer.name, errStr))
+ errors = errors + 1
+ errStr = ""
+ k = 0
+ for err in ignoreErrList:
+ errStr = errStr + str(err) + ", "
+ k = k + 1
+ if k > 100:
+ errStr = errStr + "..."
+ break
+ print errStr
+
+
+def testTiles(mapName, file, tilesMap):
+ ignoredFiles = []
+ if "ignored" in atlasToFiles:
+ ignoredFiles = atlasToFiles["ignored"]
+ for firstGid in tilesMap:
+ tile1 = tilesMap[firstGid]
+ if mapName in mapToAtlas:
+ atlasName = mapToAtlas[mapName]
+ if atlasName in atlasToFiles:
+ files = atlasToFiles[atlasName]
+ """
+ if tile1.source not in files and tile1.source not in ignoredFiles:
+ showMsgFile(file, "tileset '{0} ({1})' not present in atlas '{2}'".format(
+ tile1.name,
+ tile1.source,
+ atlasName),
+ True)
+ """
+ for gid2 in tilesMap:
+ if firstGid != gid2:
+ tile2 = tilesMap[gid2]
+ if (tile1.firstGid >= tile2.firstGid and tile1.firstGid <= tile2.lastGid) or \
+ (tile1.lastGid >= tile2.firstGid and tile1.lastGid <= tile2.lastGid):
+ showMsgFile(file, "overlaping tilesets gids \"" + tile1.name \
+ + "\" and \"" + tile2.name + "\"", True)
+
+
+def reportAboutTiles(file, data):
+ if testBadCollisions == False:
+ return
+ for k in data:
+ d = data[k]
+ if d[0] != 0 and d[2] != 0:
+ #print file + ": " + str(k) + ": " + str(d)
+ testCollisionPoints(file, k, d, 1, 3, \
+ "possible tiles should be without collision: ", \
+ "because no collision: ", False)
+ testCollisionPoints(file, k, d, 3, 1, \
+ "possible tiles should be with collision: ", \
+ "because collision: ", False)
+
+
+def testCollisionPoints(file, tileId, data, idx1, idx2, msg1, msg2, iserr):
+ #print "test: " + str(idx1) + ", " + str(idx2)
+ cnt1 = 0
+ cnt2 = 0
+ for point in data[idx1]:
+ if point[2] > 0:
+ cnt1 = cnt1 + 1
+ for point in data[idx2]:
+ if point[2] > 0:
+ cnt2 = cnt2 + 1
+
+ ln1 = len(data[idx1])
+ ln2 = len(data[idx2])
+ #print "cnt1=" + str(cnt1) + ", cnt2=" + str(cnt2) + ", ln1=" + str(ln1) + ", ln2=" + str(ln2)
+ if ln1 > 0 and ln2 > 0 and cnt2 > 0 and cnt2 < cnt1 - tileNumDiff and cnt2 < maxNumErrTiles:
+ text = msg1
+ c = 0
+ for point in data[idx2]:
+ if point[2] > 0:
+ if c > 100:
+ break
+ text = text + "(" + str(point[0]) + ", " + str(point[1]) + "), "
+ c = c + 1
+ text = text[:len(text)-2] + " " + msg2
+ c = 0
+ for point in data[idx1]:
+ if c > 100:
+ break
+ text = text + "(" + str(point[0]) + ", " + str(point[1]) + "), "
+ c = c + 1
+ showMsgFile(file, text[:len(text)-2], iserr)
+
+
+def testCollisionLayer(file, layer, tiles):
+ haveTiles = False
+ tileset = set()
+ badtiles = set()
+ arr = layer.arr
+ x1 = borderSize
+ y1 = borderSize
+ x2 = layer.width - borderSize
+ y2 = layer.height - borderSize
+ if x2 < 0:
+ x2 = 0
+ if y2 < 0:
+ y2 = 0
+
+ if arr is None :
+ return (set(), set())
+
+ for x in range(0, layer.width):
+ for y in range(0, layer.height):
+ idx = ((y * layer.width) + x) * 4
+ val = getLDV(arr, idx)
+ if val != 0:
+ haveTiles = True
+ tile, tilesetName = findTileByGid(tiles, val)
+ if tile is not None:
+ idx = val - tile.firstGid
+ if idx > 6: # 6 - max collision type
+ badtiles.add(((x, y), idx))
+ else:
+ badtiles.add(((x, y), "+{0}".format(val)))
+ if val == 0 and (x < x1 or x > x2 or y < y1 or y > y2):
+ tileset.add((x, y))
+
+ if haveTiles == False:
+ if silent == False or file.find("maps/test") != 0:
+ showMsgFile(file, "empty collision layer", False)
+ return (set(), set())
+
+ return (tileset, badtiles)
+
+
+def findTileByGid(tiles, gid):
+ for firstGid in tiles:
+ if firstGid <= gid:
+ tile = tiles[firstGid]
+ if tile.lastGid >= gid:
+ return (tile, tile.source)
+ return (None, None)
+
+
+def showLayerErrors(file, points, msg, iserr):
+ txt = ""
+ cnt = 0
+ for point in points:
+ txt = txt + " " + str(point) + ","
+ cnt = cnt + 1
+ if cnt > 100:
+ txt = txt + " ... "
+ break
+ showMsgFile(file, msg + txt[0:len(txt)-1], iserr)
+
+
+def getLDV(arr, index):
+ return arr[index] | (arr[index + 1] << 8) | (arr[index + 2] << 16) \
+ | (arr[index + 3] << 24)
+
+
+def getLDV2(arr, x, y, width, height, tilesMap):
+ ptr = ((y * width) + x) * 4
+ res = getLDV(arr, ptr)
+ yend = height - 1
+ if yend - y > 5:
+ yend = y + 5
+ for y2 in range(height - 1, y, -1):
+ x0 = x - 3
+ if x0 < 0:
+ x0 = 0
+ for x2 in range(x0, x + 1):
+ ptr = ((y2 * width) + x2) * 4
+ val = getLDV(arr, ptr)
+ tile, _ = findTileByGid(tilesMap, val)
+ if tile is not None:
+ if (tile.tileHeight > 32 or y2 == y) and (tile.tileWidth > 32 or x2 == x):
+ hg = tile.tileHeight / 32
+ wg = tile.tileWidth / 32
+ if (y2 - y < hg or y2 == y) and (x2 - x < wg or x2 == x):
+ res = val
+
+ return res
+
+
+def testLayer(file, node, name, width, height, layer, tiles):
+ datas = node.getElementsByTagName("data")
+ if datas == None or len(datas) == 0:
+ showMsgFile(file, "missing data tag in layer: " + name, True)
+ return
+ layer.arr = None
+ for data in datas:
+ try:
+ encoding = data.attributes["encoding"].value
+ except:
+ encoding = ""
+ try:
+ compression = data.attributes["compression"].value
+ except:
+ compression = ""
+ if encoding == "base64":
+# if compression != "gzip":
+# if compression != "zlib":
+# showMsgFile(file, "invalid compression " + compression + \
+# " in layer: " + name, True)
+# continue
+# else:
+# showMsgFile(file, "not supported compression by old clients " \
+# + compression + " in layer: " + name, False)
+ binData = data.childNodes[0].data.strip()
+ binData = binData.decode('base64')
+ if compression == "gzip":
+ dc = zlib.decompressobj(16 + zlib.MAX_WBITS)
+ else:
+ dc = zlib.decompressobj()
+ layerData = dc.decompress(binData)
+ arr = array.array("B")
+ arr.fromstring(layerData)
+ layer.arr = arr
+# print file
+# for item in arr:
+# print item
+ elif encoding == "csv":
+ if compression != "":
+ showMsgFile(file, "not supported compression " + compression + \
+ " for csv layer format:" + name, True)
+ binData = data.childNodes[0].data.strip()
+ f = StringIO.StringIO(binData)
+ arr = list(csv.reader(f, delimiter=',', quotechar='|'))
+ layer.arr = []
+# print file
+ for row in arr:
+ try:
+ for item in row:
+ if item != "":
+ nums = splitBytes(int(item))
+ layer.arr.append(nums[0])
+ layer.arr.append(nums[1])
+ layer.arr.append(nums[2])
+ layer.arr.append(nums[3])
+ except:
+ None
+
+ f.close()
+ arr = array.array('i', (layer.arr))
+ layer.arr = arr
+# for item in arr:
+# print item
+
+ elif encoding == "":
+ if compression != "":
+ showMsgFile(file, "not supported compression " + compression + \
+ " for xml layer format:" + name, True)
+
+ layer.arr = []
+ tiles = data.getElementsByTagName("tile")
+# print file
+ for tile in tiles:
+ try:
+ gid = int(tile.attributes["gid"].value)
+ except:
+ showMsgFile(file, "incorrect xml layer format: " + name, True)
+ return layer
+ nums = splitBytes(gid)
+ layer.arr.append(nums[0])
+ layer.arr.append(nums[1])
+ layer.arr.append(nums[2])
+ layer.arr.append(nums[3])
+
+ arr = array.array('i', (layer.arr))
+ layer.arr = arr
+# for item in arr:
+# print item
+
+
+ # here may be i should check is tiles correct or not, but i will trust to tiled
+ return layer
+
+
+def splitBytes(num):
+ i1 = int(num % 256)
+ i2 = int(((num % 65536) - i1) / 256)
+ i3 = int(((num % 16777216) - i2 - i1) / 65536)
+ i4 = int(((num % 4294967296) - i3 - i2 - i1) / 16777216)
+ return (i1, i2, i3, i4)
+
+def testLayerGroups(file, layers, collision, tileInfo, tilesMap, iserr):
+ width = 0
+ height = 0
+ errset = set()
+ for layer in layers:
+ if layer.width > width:
+ width = layer.width
+ if layer.height > height:
+ height = layer.height
+
+ for x in range(0, width):
+ for y in range(0, height):
+ good = False
+ lastTileId = 0
+ for layer in layers:
+ if layer.arr != None and x < layer.width \
+ and y < layer.height:
+ arr = layer.arr
+ ptr = ((y * layer.width) + x) * 4
+ if testBadCollisions == True:
+ val = getLDV2(arr, x, y, layer.width, layer.height, tilesMap)
+ else:
+ val = 0
+ val1 = getLDV(arr, ptr)
+ if val1 != 0:
+ good = True
+ if val == val1 and testBadCollisions == True:
+ lastTileId = val
+ if good == False:
+ errset.add((x,y))
+ elif testBadCollisions == True and collision != None and tileInfo != None:
+ if lastTileId not in tileInfo:
+ tileInfo[lastTileId] = [0, set(), 0, set()]
+ ti = tileInfo[lastTileId]
+ flg = getLDV(collision.arr, ((y * collision.width) + x) * 4)
+ cnt = countCollisionsNear(collision, x, y)
+ k = 0
+ if flg > 0:
+ if cnt[1] < cnt[0] and cnt[0] - cnt[1] > 5:
+ k = 1
+ ti[2] = ti[2] + 1
+ ti[3].add((x, y, k))
+ else:
+ if cnt[0] > cnt[1] and cnt[0] - cnt[1] > 5:
+ k = 1
+ ti[0] = ti[0] + 1
+ ti[1].add((x, y, k))
+
+ return errset
+
+
+def countCollisionsNear(layer, x, y):
+ arr = layer.arr
+ x1 = x - 1
+ y1 = y - 1
+ x2 = x + 1
+ y2 = y + 1
+ col = 0
+ nor = 0
+
+ if x1 < 0:
+ x1 = 0
+ if x2 >= layer.width:
+ x2 = layer.width - 1
+ if y1 < 0:
+ y1 = 0
+ if y2 >= layer.height:
+ y2 = layer.height - 1
+
+ for f in range(x1, x2 + 1):
+ for d in range(y1, y2 + 1):
+ if f != x or d != y:
+ val = getLDV(arr, ((d * layer.width) + f) * 4)
+ if val == 0:
+ nor = nor + 1
+ else:
+ col = col + 1
+ return (nor, col)
+
+
+def testMaps(dir):
+ global warnings, errors
+ fullPath = parentDir + "/" + dir
+ print "Checking maps"
+ if not os.path.isdir(fullPath) or not os.path.exists(fullPath):
+ print "error: maps dir not found: " + dir
+ errors = errors + 1
+ return
+
+ for file in os.listdir(fullPath):
+ if filtmaps.search(file):
+ testMap(file, mapsDir + file, dir + file)
+
+def testDirExists(path):
+ global errors
+ fullName = parentDir + "/" + path
+ if not os.path.exists(fullName):
+ print "error: path '" + path + "' not exists."
+ errors = errors + 1
+ elif not os.path.isdir(fullName):
+ print "error: path '" + path + "' is incorrect directory."
+ errors = errors + 1
+
+def testDefaultFiles():
+ global warnings
+ print "Checking default files"
+ testDirExists(iconsDir)
+ testDirExists(spritesDir)
+ testDirExists(particlesDir)
+ testDirExists(minimapsDir)
+ testDirExists(mapsDir)
+ testDirExists(sfxDir)
+ if silent != True:
+ testDirExists(musicDir)
+ testDirExists(wallpapersDir)
+
+ testSprite("0", spriteErrorFile, 0, True, "", True)
+ testParticle("0", particlesDir + levelUpEffectFile, "levelUpEffectFile")
+ testParticle("0", particlesDir + portalEffectFile, "portalEffectFile")
+ fullName = parentDir + "/" + wallpapersDir + wallpaperFile
+ if not os.path.isdir(fullName) and os.path.exists(fullName):
+ testImageFile(wallpapersDir + wallpaperFile, fullName, 0, "", False)
+
+
+def testMinimapsDir():
+ global errors, warnings
+
+ print "Checking minimaps"
+ fullPath = parentDir + "/" + minimapsDir
+ if not os.path.isdir(fullPath) or not os.path.exists(fullPath):
+ print "warn: minimaps dir not exist"
+ warnings = warnings + 1
+ return
+ for file in os.listdir(fullPath):
+ if filtimages.search(file):
+ fullName = parentDir + "/" + minimapsDir + file
+ testImageFile(minimapsDir + file, fullName, 0, "", True)
+
+
+def testImagesDir(imagesDir, sz):
+ global errors, warnings
+
+ fullPath = parentDir + "/" + imagesDir
+ if not os.path.isdir(fullPath) or not os.path.exists(fullPath):
+ return
+ for file in os.listdir(fullPath):
+ file2 = fullPath + "/" + file
+ if file[0] == ".":
+ continue
+ if not os.path.isfile(file2):
+ testImagesDir(imagesDir + file + "/", sz)
+ if filtimages.search(file):
+ fullName = parentDir + "/" + imagesDir + file
+ testImageFile(imagesDir + file, fullName, sz, "", True)
+
+
+def testSpritesDir(dir):
+ global errors, warnings, safeDye
+
+ fullPath = parentDir + "/" + spritesDir + dir
+ if not os.path.isdir(fullPath) or not os.path.exists(fullPath):
+ return
+
+ for file in os.listdir(fullPath):
+ file2 = fullPath + "/" + file
+ if file[0] == ".":
+ continue
+ if not os.path.isfile(file2):
+ testSpritesDir(dir + file + "/")
+ if filtimages.search(file):
+ fullName = parentDir + "/" + spritesDir + dir + file
+ testImageFile(spritesDir + dir, fullName, 0, spritesDir + dir + file, True)
+ elif filtxmls.search(file):
+ fullName = dir + file
+ safeDye = True
+ testSprite("0", dir + file, 0, True, "", True)
+ safeDye = False
+
+
+
+def testParticlesDir(dir):
+ global errors, warnings, safeDye
+
+ fullPath = parentDir + "/" + dir
+ if not os.path.isdir(fullPath) or not os.path.exists(fullPath):
+ return
+ for file in os.listdir(fullPath):
+ file2 = fullPath + "/" + file
+ if file[0] == ".":
+ continue
+ if not os.path.isfile(file2):
+ testParticlesDir(dir + file + "/")
+ if filtimages.search(file):
+ fullName = parentDir + "/" + dir + file
+ testImageFile(dir + file, fullName, 0, "", True)
+ elif filtxmls.search(file):
+ fullName = dir + file
+ safeDye = True
+ testParticle("0", dir + file, "")
+ safeDye = False
+
+
+def testSoundsDir(dir, sfxDir):
+ global errors, warnings
+
+ fullPath = parentDir + "/" + sfxDir + dir
+ if not os.path.isdir(fullPath) or not os.path.exists(fullPath):
+ print "warn: directory " + sfxDir + " not exist"
+ warnings = warnings + 1
+ return
+ for file in os.listdir(fullPath):
+ file2 = fullPath + "/" + file
+ if file[0] == ".":
+ continue
+ if not os.path.isfile(file2):
+ testSoundsDir(dir + file + "/", sfxDir)
+ elif filtogg.search(file):
+ testSound(dir + file, sfxDir, "")
+
+
+def testItemColors(fileName):
+ global warnings, errors, safeDye, colorLists
+ print "Checking itemcolors.xml"
+ try:
+ dom = minidom.parse(parentDir + "/" + fileName)
+ except:
+ return
+
+ for node in dom.getElementsByTagName("list"):
+ if node.parentNode != dom.documentElement:
+ continue
+
+ try:
+ name = node.attributes["name"].value
+ except:
+ print "error: colors list dont have name"
+ errors = errors + 1
+ continue
+ if name in colorsList:
+ print "error: duplicate color list: " + name
+ errors = errors + 1
+ continue
+ colorsList.add(name)
+ colors = set()
+ names = set()
+ for colorNode in node.getElementsByTagName("color"):
+ if colorNode.parentNode != node:
+ continue
+ try:
+ id = colorNode.attributes["id"].value
+ except:
+ print "error: getting id in list: " + name
+ errors = errors + 1
+ continue
+ try:
+ colorName = colorNode.attributes["name"].value
+ except:
+ print "error: getting name in list: " + name
+ errors = errors + 1
+ continue
+ try:
+ colorDye = colorNode.attributes["value"].value
+ except:
+ print "error: getting color in list: " + name
+ errors = errors + 1
+ if id in colors:
+ print "error: color with id " + str(id) + " already in list: " + name
+ errors = errors + 1
+ else:
+ colors.add(id)
+ if colorName in names:
+ print "error: color with name \"" + colorName + "\" already in list: " + name
+ errors = errors + 1
+ else:
+ names.add(colorName)
+ testDyeColors(id, colorDye, colorDye, name, True)
+
+def loadMapAtlases(fileName):
+ mapToAtlas = dict()
+ atlasToFiles = dict()
+ try:
+ root = ElementTree.parse(parentDir + "/" + fileName).getroot()
+ except:
+ showMsgFile(fileName, "load xml error. Probably file missing", True)
+ return (mapToAtlas, atlasToFiles)
+
+ for node in root.findall("map"):
+ mapName = node.attrib["name"]
+ atlasNode = node.find("atlas")
+ if atlasNode == None:
+ continue
+ atlasName = atlasNode.attrib["name"]
+ mapToAtlas[mapName] = atlasName
+ for node in root.findall("atlas"):
+ atlasName = node.attrib["name"]
+ files = []
+ for fileNode in node.findall("file"):
+ fileName = fileNode.attrib["name"]
+ files.append(fileName)
+ atlasToFiles[atlasName] = files
+ for mapName in mapToAtlas:
+ atlasName = mapToAtlas[mapName]
+ if atlasName not in atlasToFiles:
+ showMsgFile(fileName, "atlas '{0}' assigned to map not present in maps.xml".format(atlasName), True)
+
+ return (mapToAtlas, atlasToFiles)
+
+def haveXml(dir):
+ if not os.path.isdir(dir) or not os.path.exists(dir):
+ return False
+ for file in os.listdir(dir):
+ if filt.search(file):
+ return True
+ return False
+
+
+def detectClientData(dirs):
+ global parentDir
+
+ for dir in dirs:
+ if haveXml(dir):
+ print "Detected client data directory in: " + dir
+ parentDir = dir
+ return True
+
+ print "Cant detect client data directory"
+ exit(1)
+
+
+if len(sys.argv) == 2:
+ if sys.argv[1] == "all":
+ showAll = True
+ elif sys.argv[1] == "silent":
+ silent = True
+ elif sys.argv[1] == "stfu":
+ silent = True
+ stfu = True
+ elif sys.argv[1] == "herc":
+ silent = True
+ herc = True
+
+showHeader()
+print "Detecting clientdata dir"
+detectClientData([".", "..", "../../clientdata", parentDir])
+print "Checking xml file syntax"
+enumDirs(parentDir)
+loadPaths()
+(mapToAtlas, atlasToFiles) = loadMapAtlases("/maps.xml")
+testDefaultFiles()
+testItemColors("/itemcolors.xml")
+testItems("/items.xml", iconsDir)
+testMonsters("/monsters.xml")
+testNpcs("/npcs.xml")
+testMaps(mapsDir)
+testMinimapsDir()
+print "Checking images dir"
+testImagesDir(wallpapersDir, 0)
+print "Checking icons dir"
+testImagesDir(iconsDir, 32)
+print "Checking sprites dir"
+testSpritesDir("")
+print "Checking particles dir"
+testParticlesDir(particlesDir)
+print "Checking sfx dir"
+testSoundsDir("", sfxDir)
+print "Checking music dir"
+if silent != True:
+ testSoundsDir("", musicDir)
+showFooter()
+if errors > 0 or warnings > 0:
+ exit(1)
diff --git a/testxml/xsd/XMLSchema.xsd b/testxml/xsd/XMLSchema.xsd
new file mode 100644
index 0000000..3711a93
--- /dev/null
+++ b/testxml/xsd/XMLSchema.xsd
@@ -0,0 +1,2262 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- XML Schema schema for XML Schemas: Part 1: Structures -->
+<!-- Note this schema is NOT the normative structures schema. -->
+<!-- The prose copy in the structures REC is the normative -->
+<!-- version (which shouldn't differ from this one except for -->
+<!-- this comment and entity expansions, but just in case -->
+<!DOCTYPE xs:schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" "XMLSchema.dtd" [
+<!-- provide ID type information even for parsers which only read the
+ internal subset --><!ATTLIST xs:schema id ID #IMPLIED>
+<!ATTLIST xs:complexType id ID #IMPLIED>
+<!ATTLIST xs:complexContent id ID #IMPLIED>
+<!ATTLIST xs:simpleContent id ID #IMPLIED>
+<!ATTLIST xs:extension id ID #IMPLIED>
+<!ATTLIST xs:element id ID #IMPLIED>
+<!ATTLIST xs:group id ID #IMPLIED>
+<!ATTLIST xs:all id ID #IMPLIED>
+<!ATTLIST xs:choice id ID #IMPLIED>
+<!ATTLIST xs:sequence id ID #IMPLIED>
+<!ATTLIST xs:any id ID #IMPLIED>
+<!ATTLIST xs:anyAttribute id ID #IMPLIED>
+<!ATTLIST xs:attribute id ID #IMPLIED>
+<!ATTLIST xs:attributeGroup id ID #IMPLIED>
+<!ATTLIST xs:unique id ID #IMPLIED>
+<!ATTLIST xs:key id ID #IMPLIED>
+<!ATTLIST xs:keyref id ID #IMPLIED>
+<!ATTLIST xs:selector id ID #IMPLIED>
+<!ATTLIST xs:field id ID #IMPLIED>
+<!ATTLIST xs:include id ID #IMPLIED>
+<!ATTLIST xs:import id ID #IMPLIED>
+<!ATTLIST xs:redefine id ID #IMPLIED>
+<!ATTLIST xs:notation id ID #IMPLIED>
+<!--
+ keep this schema XML1.0 DTD valid
+ --><!ENTITY % schemaAttrs "xmlns:hfp CDATA #IMPLIED">
+<!ELEMENT hfp:hasFacet EMPTY>
+<!ATTLIST hfp:hasFacet name NMTOKEN #REQUIRED>
+<!ELEMENT hfp:hasProperty EMPTY>
+<!ATTLIST hfp:hasProperty name NMTOKEN #REQUIRED>
+<!ATTLIST hfp:hasProperty value CDATA #REQUIRED>
+<!--
+ Make sure that processors that do not read the external
+ subset will know about the various IDs we declare
+ --><!ATTLIST xs:simpleType id ID #IMPLIED>
+<!ATTLIST xs:maxExclusive id ID #IMPLIED>
+<!ATTLIST xs:minExclusive id ID #IMPLIED>
+<!ATTLIST xs:maxInclusive id ID #IMPLIED>
+<!ATTLIST xs:minInclusive id ID #IMPLIED>
+<!ATTLIST xs:totalDigits id ID #IMPLIED>
+<!ATTLIST xs:fractionDigits id ID #IMPLIED>
+<!ATTLIST xs:length id ID #IMPLIED>
+<!ATTLIST xs:minLength id ID #IMPLIED>
+<!ATTLIST xs:maxLength id ID #IMPLIED>
+<!ATTLIST xs:enumeration id ID #IMPLIED>
+<!ATTLIST xs:pattern id ID #IMPLIED>
+<!ATTLIST xs:appinfo id ID #IMPLIED>
+<!ATTLIST xs:documentation id ID #IMPLIED>
+<!ATTLIST xs:list id ID #IMPLIED>
+<!ATTLIST xs:union id ID #IMPLIED>
+]>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:hfp="http://www.w3.org/2001/XMLSchema-hasFacetAndProperty" targetNamespace="http://www.w3.org/2001/XMLSchema" blockDefault="#all" elementFormDefault="qualified" version="1.0" xml:lang="EN">
+ <xs:annotation>
+ <xs:documentation>
+ Part 1 version: Id: structures.xsd,v 1.2 2004/01/15 11:34:25 ht Exp
+ Part 2 version: Id: datatypes.xsd,v 1.3 2004/01/23 18:11:13 ht Exp
+ </xs:documentation>
+ </xs:annotation>
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/2004/PER-xmlschema-1-20040318/structures.html">
+ The schema corresponding to this document is normative,
+ with respect to the syntactic constraints it expresses in the
+ XML Schema language. The documentation (within &lt;documentation&gt; elements)
+ below, is not normative, but rather highlights important aspects of
+ the W3C Recommendation of which this is a part</xs:documentation>
+ </xs:annotation>
+ <xs:annotation>
+ <xs:documentation>
+ The simpleType element and all of its members are defined
+ towards the end of this schema document</xs:documentation>
+ </xs:annotation>
+ <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="xml.xsd">
+ <xs:annotation>
+ <xs:documentation>
+ Get access to the xml: attribute groups for xml:lang
+ as declared on 'schema' and 'documentation' below
+ </xs:documentation>
+ </xs:annotation>
+ </xs:import>
+ <xs:complexType name="openAttrs">
+ <xs:annotation>
+ <xs:documentation>
+ This type is extended by almost all schema types
+ to allow attributes from other namespaces to be
+ added to user schemas.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:restriction base="xs:anyType">
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="annotated">
+ <xs:annotation>
+ <xs:documentation>
+ This type is extended by all types which allow annotation
+ other than &lt;schema&gt; itself
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:extension base="xs:openAttrs">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:group name="schemaTop">
+ <xs:annotation>
+ <xs:documentation>
+ This group is for the
+ elements which occur freely at the top level of schemas.
+ All of their types are based on the "annotated" type by extension.</xs:documentation>
+ </xs:annotation>
+ <xs:choice>
+ <xs:group ref="xs:redefinable"/>
+ <xs:element ref="xs:element"/>
+ <xs:element ref="xs:attribute"/>
+ <xs:element ref="xs:notation"/>
+ </xs:choice>
+ </xs:group>
+ <xs:group name="redefinable">
+ <xs:annotation>
+ <xs:documentation>
+ This group is for the
+ elements which can self-redefine (see &lt;redefine&gt; below).</xs:documentation>
+ </xs:annotation>
+ <xs:choice>
+ <xs:element ref="xs:simpleType"/>
+ <xs:element ref="xs:complexType"/>
+ <xs:element ref="xs:group"/>
+ <xs:element ref="xs:attributeGroup"/>
+ </xs:choice>
+ </xs:group>
+ <xs:simpleType name="formChoice">
+ <xs:annotation>
+ <xs:documentation>
+ A utility type, not for public use</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="qualified"/>
+ <xs:enumeration value="unqualified"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="reducedDerivationControl">
+ <xs:annotation>
+ <xs:documentation>
+ A utility type, not for public use</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:derivationControl">
+ <xs:enumeration value="extension"/>
+ <xs:enumeration value="restriction"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="derivationSet">
+ <xs:annotation>
+ <xs:documentation>
+ A utility type, not for public use</xs:documentation>
+ <xs:documentation>
+ #all or (possibly empty) subset of {extension, restriction}</xs:documentation>
+ </xs:annotation>
+ <xs:union>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="#all"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType>
+ <xs:list itemType="xs:reducedDerivationControl"/>
+ </xs:simpleType>
+ </xs:union>
+ </xs:simpleType>
+ <xs:simpleType name="typeDerivationControl">
+ <xs:annotation>
+ <xs:documentation>
+ A utility type, not for public use</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:derivationControl">
+ <xs:enumeration value="extension"/>
+ <xs:enumeration value="restriction"/>
+ <xs:enumeration value="list"/>
+ <xs:enumeration value="union"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="fullDerivationSet">
+ <xs:annotation>
+ <xs:documentation>
+ A utility type, not for public use</xs:documentation>
+ <xs:documentation>
+ #all or (possibly empty) subset of {extension, restriction, list, union}</xs:documentation>
+ </xs:annotation>
+ <xs:union>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="#all"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType>
+ <xs:list itemType="xs:typeDerivationControl"/>
+ </xs:simpleType>
+ </xs:union>
+ </xs:simpleType>
+ <xs:element name="schema" id="schema">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-schema"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="xs:openAttrs">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="xs:include"/>
+ <xs:element ref="xs:import"/>
+ <xs:element ref="xs:redefine"/>
+ <xs:element ref="xs:annotation"/>
+ </xs:choice>
+ <xs:sequence minOccurs="0" maxOccurs="unbounded">
+ <xs:group ref="xs:schemaTop"/>
+ <xs:element ref="xs:annotation" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:sequence>
+ <xs:attribute name="targetNamespace" type="xs:anyURI"/>
+ <xs:attribute name="version" type="xs:token"/>
+ <xs:attribute name="finalDefault" type="xs:fullDerivationSet" use="optional" default=""/>
+ <xs:attribute name="blockDefault" type="xs:blockSet" use="optional" default=""/>
+ <xs:attribute name="attributeFormDefault" type="xs:formChoice" use="optional" default="unqualified"/>
+ <xs:attribute name="elementFormDefault" type="xs:formChoice" use="optional" default="unqualified"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ <xs:attribute ref="xml:lang"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:key name="element">
+ <xs:selector xpath="xs:element"/>
+ <xs:field xpath="@name"/>
+ </xs:key>
+ <xs:key name="attribute">
+ <xs:selector xpath="xs:attribute"/>
+ <xs:field xpath="@name"/>
+ </xs:key>
+ <xs:key name="type">
+ <xs:selector xpath="xs:complexType|xs:simpleType"/>
+ <xs:field xpath="@name"/>
+ </xs:key>
+ <xs:key name="group">
+ <xs:selector xpath="xs:group"/>
+ <xs:field xpath="@name"/>
+ </xs:key>
+ <xs:key name="attributeGroup">
+ <xs:selector xpath="xs:attributeGroup"/>
+ <xs:field xpath="@name"/>
+ </xs:key>
+ <xs:key name="notation">
+ <xs:selector xpath="xs:notation"/>
+ <xs:field xpath="@name"/>
+ </xs:key>
+ <xs:key name="identityConstraint">
+ <xs:selector xpath=".//xs:key|.//xs:unique|.//xs:keyref"/>
+ <xs:field xpath="@name"/>
+ </xs:key>
+ </xs:element>
+ <xs:simpleType name="allNNI">
+ <xs:annotation>
+ <xs:documentation>
+ for maxOccurs</xs:documentation>
+ </xs:annotation>
+ <xs:union memberTypes="xs:nonNegativeInteger">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="unbounded"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:union>
+ </xs:simpleType>
+ <xs:attributeGroup name="occurs">
+ <xs:annotation>
+ <xs:documentation>
+ for all particles</xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="minOccurs" type="xs:nonNegativeInteger" use="optional" default="1"/>
+ <xs:attribute name="maxOccurs" type="xs:allNNI" use="optional" default="1"/>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="defRef">
+ <xs:annotation>
+ <xs:documentation>
+ for element, group and attributeGroup,
+ which both define and reference</xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="name" type="xs:NCName"/>
+ <xs:attribute name="ref" type="xs:QName"/>
+ </xs:attributeGroup>
+ <xs:group name="typeDefParticle">
+ <xs:annotation>
+ <xs:documentation>
+ 'complexType' uses this</xs:documentation>
+ </xs:annotation>
+ <xs:choice>
+ <xs:element name="group" type="xs:groupRef"/>
+ <xs:element ref="xs:all"/>
+ <xs:element ref="xs:choice"/>
+ <xs:element ref="xs:sequence"/>
+ </xs:choice>
+ </xs:group>
+ <xs:group name="nestedParticle">
+ <xs:choice>
+ <xs:element name="element" type="xs:localElement"/>
+ <xs:element name="group" type="xs:groupRef"/>
+ <xs:element ref="xs:choice"/>
+ <xs:element ref="xs:sequence"/>
+ <xs:element ref="xs:any"/>
+ </xs:choice>
+ </xs:group>
+ <xs:group name="particle">
+ <xs:choice>
+ <xs:element name="element" type="xs:localElement"/>
+ <xs:element name="group" type="xs:groupRef"/>
+ <xs:element ref="xs:all"/>
+ <xs:element ref="xs:choice"/>
+ <xs:element ref="xs:sequence"/>
+ <xs:element ref="xs:any"/>
+ </xs:choice>
+ </xs:group>
+ <xs:complexType name="attribute">
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:sequence>
+ <xs:element name="simpleType" minOccurs="0" type="xs:localSimpleType"/>
+ </xs:sequence>
+ <xs:attributeGroup ref="xs:defRef"/>
+ <xs:attribute name="type" type="xs:QName"/>
+ <xs:attribute name="use" use="optional" default="optional">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="prohibited"/>
+ <xs:enumeration value="optional"/>
+ <xs:enumeration value="required"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="default" type="xs:string"/>
+ <xs:attribute name="fixed" type="xs:string"/>
+ <xs:attribute name="form" type="xs:formChoice"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="topLevelAttribute">
+ <xs:complexContent>
+ <xs:restriction base="xs:attribute">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:element name="simpleType" minOccurs="0" type="xs:localSimpleType"/>
+ </xs:sequence>
+ <xs:attribute name="ref" use="prohibited"/>
+ <xs:attribute name="form" use="prohibited"/>
+ <xs:attribute name="use" use="prohibited"/>
+ <xs:attribute name="name" use="required" type="xs:NCName"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:group name="attrDecls">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="attribute" type="xs:attribute"/>
+ <xs:element name="attributeGroup" type="xs:attributeGroupRef"/>
+ </xs:choice>
+ <xs:element ref="xs:anyAttribute" minOccurs="0"/>
+ </xs:sequence>
+ </xs:group>
+ <xs:element name="anyAttribute" type="xs:wildcard" id="anyAttribute">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-anyAttribute"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:group name="complexTypeModel">
+ <xs:choice>
+ <xs:element ref="xs:simpleContent"/>
+ <xs:element ref="xs:complexContent"/>
+ <xs:sequence>
+ <xs:annotation>
+ <xs:documentation>
+ This branch is short for
+ &lt;complexContent&gt;
+ &lt;restriction base="xs:anyType"&gt;
+ ...
+ &lt;/restriction&gt;
+ &lt;/complexContent&gt;</xs:documentation>
+ </xs:annotation>
+ <xs:group ref="xs:typeDefParticle" minOccurs="0"/>
+ <xs:group ref="xs:attrDecls"/>
+ </xs:sequence>
+ </xs:choice>
+ </xs:group>
+ <xs:complexType name="complexType" abstract="true">
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:group ref="xs:complexTypeModel"/>
+ <xs:attribute name="name" type="xs:NCName">
+ <xs:annotation>
+ <xs:documentation>
+ Will be restricted to required or forbidden</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="mixed" type="xs:boolean" use="optional" default="false">
+ <xs:annotation>
+ <xs:documentation>
+ Not allowed if simpleContent child is chosen.
+ May be overriden by setting on complexContent child.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="abstract" type="xs:boolean" use="optional" default="false"/>
+ <xs:attribute name="final" type="xs:derivationSet"/>
+ <xs:attribute name="block" type="xs:derivationSet"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="topLevelComplexType">
+ <xs:complexContent>
+ <xs:restriction base="xs:complexType">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:group ref="xs:complexTypeModel"/>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:NCName" use="required"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="localComplexType">
+ <xs:complexContent>
+ <xs:restriction base="xs:complexType">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:group ref="xs:complexTypeModel"/>
+ </xs:sequence>
+ <xs:attribute name="name" use="prohibited"/>
+ <xs:attribute name="abstract" use="prohibited"/>
+ <xs:attribute name="final" use="prohibited"/>
+ <xs:attribute name="block" use="prohibited"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="restrictionType">
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:sequence>
+ <xs:choice minOccurs="0">
+ <xs:group ref="xs:typeDefParticle"/>
+ <xs:group ref="xs:simpleRestrictionModel"/>
+ </xs:choice>
+ <xs:group ref="xs:attrDecls"/>
+ </xs:sequence>
+ <xs:attribute name="base" type="xs:QName" use="required"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="complexRestrictionType">
+ <xs:complexContent>
+ <xs:restriction base="xs:restrictionType">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:choice minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>This choice is added simply to
+ make this a valid restriction per the REC</xs:documentation>
+ </xs:annotation>
+ <xs:group ref="xs:typeDefParticle"/>
+ </xs:choice>
+ <xs:group ref="xs:attrDecls"/>
+ </xs:sequence>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="extensionType">
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:sequence>
+ <xs:group ref="xs:typeDefParticle" minOccurs="0"/>
+ <xs:group ref="xs:attrDecls"/>
+ </xs:sequence>
+ <xs:attribute name="base" type="xs:QName" use="required"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="complexContent" id="complexContent">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-complexContent"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:choice>
+ <xs:element name="restriction" type="xs:complexRestrictionType"/>
+ <xs:element name="extension" type="xs:extensionType"/>
+ </xs:choice>
+ <xs:attribute name="mixed" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation>
+ Overrides any setting on complexType parent.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="simpleRestrictionType">
+ <xs:complexContent>
+ <xs:restriction base="xs:restrictionType">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:choice minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>This choice is added simply to
+ make this a valid restriction per the REC</xs:documentation>
+ </xs:annotation>
+ <xs:group ref="xs:simpleRestrictionModel"/>
+ </xs:choice>
+ <xs:group ref="xs:attrDecls"/>
+ </xs:sequence>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="simpleExtensionType">
+ <xs:complexContent>
+ <xs:restriction base="xs:extensionType">
+ <xs:sequence>
+ <xs:annotation>
+ <xs:documentation>
+ No typeDefParticle group reference</xs:documentation>
+ </xs:annotation>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:group ref="xs:attrDecls"/>
+ </xs:sequence>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="simpleContent" id="simpleContent">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-simpleContent"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:choice>
+ <xs:element name="restriction" type="xs:simpleRestrictionType"/>
+ <xs:element name="extension" type="xs:simpleExtensionType"/>
+ </xs:choice>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="complexType" type="xs:topLevelComplexType" id="complexType">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-complexType"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:simpleType name="blockSet">
+ <xs:annotation>
+ <xs:documentation>
+ A utility type, not for public use</xs:documentation>
+ <xs:documentation>
+ #all or (possibly empty) subset of {substitution, extension,
+ restriction}</xs:documentation>
+ </xs:annotation>
+ <xs:union>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="#all"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType>
+ <xs:list>
+ <xs:simpleType>
+ <xs:restriction base="xs:derivationControl">
+ <xs:enumeration value="extension"/>
+ <xs:enumeration value="restriction"/>
+ <xs:enumeration value="substitution"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:list>
+ </xs:simpleType>
+ </xs:union>
+ </xs:simpleType>
+ <xs:complexType name="element" abstract="true">
+ <xs:annotation>
+ <xs:documentation>
+ The element element can be used either
+ at the top level to define an element-type binding globally,
+ or within a content model to either reference a globally-defined
+ element or type or declare an element-type binding locally.
+ The ref form is not allowed at the top level.</xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:sequence>
+ <xs:choice minOccurs="0">
+ <xs:element name="simpleType" type="xs:localSimpleType"/>
+ <xs:element name="complexType" type="xs:localComplexType"/>
+ </xs:choice>
+ <xs:group ref="xs:identityConstraint" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attributeGroup ref="xs:defRef"/>
+ <xs:attribute name="type" type="xs:QName"/>
+ <xs:attribute name="substitutionGroup" type="xs:QName"/>
+ <xs:attributeGroup ref="xs:occurs"/>
+ <xs:attribute name="default" type="xs:string"/>
+ <xs:attribute name="fixed" type="xs:string"/>
+ <xs:attribute name="nillable" type="xs:boolean" use="optional" default="false"/>
+ <xs:attribute name="abstract" type="xs:boolean" use="optional" default="false"/>
+ <xs:attribute name="final" type="xs:derivationSet"/>
+ <xs:attribute name="block" type="xs:blockSet"/>
+ <xs:attribute name="form" type="xs:formChoice"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="topLevelElement">
+ <xs:complexContent>
+ <xs:restriction base="xs:element">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:choice minOccurs="0">
+ <xs:element name="simpleType" type="xs:localSimpleType"/>
+ <xs:element name="complexType" type="xs:localComplexType"/>
+ </xs:choice>
+ <xs:group ref="xs:identityConstraint" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="ref" use="prohibited"/>
+ <xs:attribute name="form" use="prohibited"/>
+ <xs:attribute name="minOccurs" use="prohibited"/>
+ <xs:attribute name="maxOccurs" use="prohibited"/>
+ <xs:attribute name="name" use="required" type="xs:NCName"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="localElement">
+ <xs:complexContent>
+ <xs:restriction base="xs:element">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:choice minOccurs="0">
+ <xs:element name="simpleType" type="xs:localSimpleType"/>
+ <xs:element name="complexType" type="xs:localComplexType"/>
+ </xs:choice>
+ <xs:group ref="xs:identityConstraint" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="substitutionGroup" use="prohibited"/>
+ <xs:attribute name="final" use="prohibited"/>
+ <xs:attribute name="abstract" use="prohibited"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="element" type="xs:topLevelElement" id="element">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-element"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:complexType name="group" abstract="true">
+ <xs:annotation>
+ <xs:documentation>
+ group type for explicit groups, named top-level groups and
+ group references</xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:group ref="xs:particle" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:attributeGroup ref="xs:defRef"/>
+ <xs:attributeGroup ref="xs:occurs"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="realGroup">
+ <xs:complexContent>
+ <xs:restriction base="xs:group">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:choice minOccurs="0" maxOccurs="1">
+ <xs:element ref="xs:all"/>
+ <xs:element ref="xs:choice"/>
+ <xs:element ref="xs:sequence"/>
+ </xs:choice>
+ </xs:sequence>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="namedGroup">
+ <xs:complexContent>
+ <xs:restriction base="xs:realGroup">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:choice minOccurs="1" maxOccurs="1">
+ <xs:element name="all">
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:restriction base="xs:all">
+ <xs:group ref="xs:allModel"/>
+ <xs:attribute name="minOccurs" use="prohibited"/>
+ <xs:attribute name="maxOccurs" use="prohibited"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="choice" type="xs:simpleExplicitGroup"/>
+ <xs:element name="sequence" type="xs:simpleExplicitGroup"/>
+ </xs:choice>
+ </xs:sequence>
+ <xs:attribute name="name" use="required" type="xs:NCName"/>
+ <xs:attribute name="ref" use="prohibited"/>
+ <xs:attribute name="minOccurs" use="prohibited"/>
+ <xs:attribute name="maxOccurs" use="prohibited"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="groupRef">
+ <xs:complexContent>
+ <xs:restriction base="xs:realGroup">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="ref" use="required" type="xs:QName"/>
+ <xs:attribute name="name" use="prohibited"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="explicitGroup">
+ <xs:annotation>
+ <xs:documentation>
+ group type for the three kinds of group</xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:restriction base="xs:group">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:group ref="xs:nestedParticle" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:NCName" use="prohibited"/>
+ <xs:attribute name="ref" type="xs:QName" use="prohibited"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="simpleExplicitGroup">
+ <xs:complexContent>
+ <xs:restriction base="xs:explicitGroup">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:group ref="xs:nestedParticle" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="minOccurs" use="prohibited"/>
+ <xs:attribute name="maxOccurs" use="prohibited"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:group name="allModel">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>This choice with min/max is here to
+ avoid a pblm with the Elt:All/Choice/Seq
+ Particle derivation constraint</xs:documentation>
+ </xs:annotation>
+ <xs:element name="element" type="xs:narrowMaxMin"/>
+ </xs:choice>
+ </xs:sequence>
+ </xs:group>
+ <xs:complexType name="narrowMaxMin">
+ <xs:annotation>
+ <xs:documentation>restricted max/min</xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:restriction base="xs:localElement">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:choice minOccurs="0">
+ <xs:element name="simpleType" type="xs:localSimpleType"/>
+ <xs:element name="complexType" type="xs:localComplexType"/>
+ </xs:choice>
+ <xs:group ref="xs:identityConstraint" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="minOccurs" use="optional" default="1">
+ <xs:simpleType>
+ <xs:restriction base="xs:nonNegativeInteger">
+ <xs:enumeration value="0"/>
+ <xs:enumeration value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="maxOccurs" use="optional" default="1">
+ <xs:simpleType>
+ <xs:restriction base="xs:allNNI">
+ <xs:enumeration value="0"/>
+ <xs:enumeration value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="all">
+ <xs:annotation>
+ <xs:documentation>
+ Only elements allowed inside</xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:restriction base="xs:explicitGroup">
+ <xs:group ref="xs:allModel"/>
+ <xs:attribute name="minOccurs" use="optional" default="1">
+ <xs:simpleType>
+ <xs:restriction base="xs:nonNegativeInteger">
+ <xs:enumeration value="0"/>
+ <xs:enumeration value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="maxOccurs" use="optional" default="1">
+ <xs:simpleType>
+ <xs:restriction base="xs:allNNI">
+ <xs:enumeration value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="all" id="all" type="xs:all">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-all"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="choice" type="xs:explicitGroup" id="choice">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-choice"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="sequence" type="xs:explicitGroup" id="sequence">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-sequence"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="group" type="xs:namedGroup" id="group">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-group"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:complexType name="wildcard">
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:attribute name="namespace" type="xs:namespaceList" use="optional" default="##any"/>
+ <xs:attribute name="processContents" use="optional" default="strict">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="skip"/>
+ <xs:enumeration value="lax"/>
+ <xs:enumeration value="strict"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="any" id="any">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-any"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="xs:wildcard">
+ <xs:attributeGroup ref="xs:occurs"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:annotation>
+ <xs:documentation>
+ simple type for the value of the 'namespace' attr of
+ 'any' and 'anyAttribute'</xs:documentation>
+ </xs:annotation>
+ <xs:annotation>
+ <xs:documentation>
+ Value is
+ ##any - - any non-conflicting WFXML/attribute at all
+
+ ##other - - any non-conflicting WFXML/attribute from
+ namespace other than targetNS
+
+ ##local - - any unqualified non-conflicting WFXML/attribute
+
+ one or - - any non-conflicting WFXML/attribute from
+ more URI the listed namespaces
+ references
+ (space separated)
+
+ ##targetNamespace or ##local may appear in the above list, to
+ refer to the targetNamespace of the enclosing
+ schema or an absent targetNamespace respectively</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType name="namespaceList">
+ <xs:annotation>
+ <xs:documentation>
+ A utility type, not for public use</xs:documentation>
+ </xs:annotation>
+ <xs:union>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="##any"/>
+ <xs:enumeration value="##other"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType>
+ <xs:list>
+ <xs:simpleType>
+ <xs:union memberTypes="xs:anyURI">
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="##targetNamespace"/>
+ <xs:enumeration value="##local"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:union>
+ </xs:simpleType>
+ </xs:list>
+ </xs:simpleType>
+ </xs:union>
+ </xs:simpleType>
+ <xs:element name="attribute" type="xs:topLevelAttribute" id="attribute">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-attribute"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:complexType name="attributeGroup" abstract="true">
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:group ref="xs:attrDecls"/>
+ <xs:attributeGroup ref="xs:defRef"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="namedAttributeGroup">
+ <xs:complexContent>
+ <xs:restriction base="xs:attributeGroup">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:group ref="xs:attrDecls"/>
+ </xs:sequence>
+ <xs:attribute name="name" use="required" type="xs:NCName"/>
+ <xs:attribute name="ref" use="prohibited"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="attributeGroupRef">
+ <xs:complexContent>
+ <xs:restriction base="xs:attributeGroup">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="ref" use="required" type="xs:QName"/>
+ <xs:attribute name="name" use="prohibited"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="attributeGroup" type="xs:namedAttributeGroup" id="attributeGroup">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-attributeGroup"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="include" id="include">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-include"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:attribute name="schemaLocation" type="xs:anyURI" use="required"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="redefine" id="redefine">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-redefine"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="xs:openAttrs">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="xs:annotation"/>
+ <xs:group ref="xs:redefinable"/>
+ </xs:choice>
+ <xs:attribute name="schemaLocation" type="xs:anyURI" use="required"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="import" id="import">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-import"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:attribute name="namespace" type="xs:anyURI"/>
+ <xs:attribute name="schemaLocation" type="xs:anyURI"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="selector" id="selector">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-selector"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:attribute name="xpath" use="required">
+ <xs:simpleType>
+ <xs:annotation>
+ <xs:documentation>A subset of XPath expressions for use
+in selectors</xs:documentation>
+ <xs:documentation>A utility type, not for public
+use</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:token">
+ <xs:annotation>
+ <xs:documentation>The following pattern is intended to allow XPath
+ expressions per the following EBNF:
+ Selector ::= Path ( '|' Path )*
+ Path ::= ('.//')? Step ( '/' Step )*
+ Step ::= '.' | NameTest
+ NameTest ::= QName | '*' | NCName ':' '*'
+ child:: is also allowed
+ </xs:documentation>
+ </xs:annotation>
+ <xs:pattern value="(\.//)?(((child::)?((\i\c*:)?(\i\c*|\*)))|\.)(/(((child::)?((\i\c*:)?(\i\c*|\*)))|\.))*(\|(\.//)?(((child::)?((\i\c*:)?(\i\c*|\*)))|\.)(/(((child::)?((\i\c*:)?(\i\c*|\*)))|\.))*)*">
+ </xs:pattern>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="field" id="field">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-field"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:attribute name="xpath" use="required">
+ <xs:simpleType>
+ <xs:annotation>
+ <xs:documentation>A subset of XPath expressions for use
+in fields</xs:documentation>
+ <xs:documentation>A utility type, not for public
+use</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:token">
+ <xs:annotation>
+ <xs:documentation>The following pattern is intended to allow XPath
+ expressions per the same EBNF as for selector,
+ with the following change:
+ Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
+ </xs:documentation>
+ </xs:annotation>
+ <xs:pattern value="(\.//)?((((child::)?((\i\c*:)?(\i\c*|\*)))|\.)/)*((((child::)?((\i\c*:)?(\i\c*|\*)))|\.)|((attribute::|@)((\i\c*:)?(\i\c*|\*))))(\|(\.//)?((((child::)?((\i\c*:)?(\i\c*|\*)))|\.)/)*((((child::)?((\i\c*:)?(\i\c*|\*)))|\.)|((attribute::|@)((\i\c*:)?(\i\c*|\*)))))*">
+ </xs:pattern>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="keybase">
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:sequence>
+ <xs:element ref="xs:selector"/>
+ <xs:element ref="xs:field" minOccurs="1" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:NCName" use="required"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:group name="identityConstraint">
+ <xs:annotation>
+ <xs:documentation>The three kinds of identity constraints, all with
+ type of or derived from 'keybase'.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:choice>
+ <xs:element ref="xs:unique"/>
+ <xs:element ref="xs:key"/>
+ <xs:element ref="xs:keyref"/>
+ </xs:choice>
+ </xs:group>
+ <xs:element name="unique" type="xs:keybase" id="unique">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-unique"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="key" type="xs:keybase" id="key">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-key"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="keyref" id="keyref">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-keyref"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="xs:keybase">
+ <xs:attribute name="refer" type="xs:QName" use="required"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="notation" id="notation">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-notation"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:attribute name="name" type="xs:NCName" use="required"/>
+ <xs:attribute name="public" type="xs:public"/>
+ <xs:attribute name="system" type="xs:anyURI"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:simpleType name="public">
+ <xs:annotation>
+ <xs:documentation>
+ A utility type, not for public use</xs:documentation>
+ <xs:documentation>
+ A public identifier, per ISO 8879</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:token"/>
+ </xs:simpleType>
+ <xs:element name="appinfo" id="appinfo">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-appinfo"/>
+ </xs:annotation>
+ <xs:complexType mixed="true">
+ <xs:sequence minOccurs="0" maxOccurs="unbounded">
+ <xs:any processContents="lax"/>
+ </xs:sequence>
+ <xs:attribute name="source" type="xs:anyURI"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="documentation" id="documentation">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-documentation"/>
+ </xs:annotation>
+ <xs:complexType mixed="true">
+ <xs:sequence minOccurs="0" maxOccurs="unbounded">
+ <xs:any processContents="lax"/>
+ </xs:sequence>
+ <xs:attribute name="source" type="xs:anyURI"/>
+ <xs:attribute ref="xml:lang"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="annotation" id="annotation">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-annotation"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="xs:openAttrs">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="xs:appinfo"/>
+ <xs:element ref="xs:documentation"/>
+ </xs:choice>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:annotation>
+ <xs:documentation>
+ notations for use within XML Schema schemas</xs:documentation>
+ </xs:annotation>
+ <xs:notation name="XMLSchemaStructures" public="structures" system="http://www.w3.org/2000/08/XMLSchema.xsd"/>
+ <xs:notation name="XML" public="REC-xml-19980210" system="http://www.w3.org/TR/1998/REC-xml-19980210"/>
+ <xs:complexType name="anyType" mixed="true">
+ <xs:annotation>
+ <xs:documentation>
+ Not the real urType, but as close an approximation as we can
+ get in the XML representation</xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
+ </xs:sequence>
+ <xs:anyAttribute processContents="lax"/>
+ </xs:complexType>
+ <xs:annotation>
+ <xs:documentation>
+ First the built-in primitive datatypes. These definitions are for
+ information only, the real built-in definitions are magic.
+ </xs:documentation>
+ <xs:documentation>
+ For each built-in datatype in this schema (both primitive and
+ derived) can be uniquely addressed via a URI constructed
+ as follows:
+ 1) the base URI is the URI of the XML Schema namespace
+ 2) the fragment identifier is the name of the datatype
+
+ For example, to address the int datatype, the URI is:
+
+ http://www.w3.org/2001/XMLSchema#int
+
+ Additionally, each facet definition element can be uniquely
+ addressed via a URI constructed as follows:
+ 1) the base URI is the URI of the XML Schema namespace
+ 2) the fragment identifier is the name of the facet
+
+ For example, to address the maxInclusive facet, the URI is:
+
+ http://www.w3.org/2001/XMLSchema#maxInclusive
+
+ Additionally, each facet usage in a built-in datatype definition
+ can be uniquely addressed via a URI constructed as follows:
+ 1) the base URI is the URI of the XML Schema namespace
+ 2) the fragment identifier is the name of the datatype, followed
+ by a period (".") followed by the name of the facet
+
+ For example, to address the usage of the maxInclusive facet in
+ the definition of int, the URI is:
+
+ http://www.w3.org/2001/XMLSchema#int.maxInclusive
+
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType name="string" id="string">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="length"/>
+ <hfp:hasFacet name="minLength"/>
+ <hfp:hasFacet name="maxLength"/>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasProperty name="ordered" value="false"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#string"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="preserve" id="string.preserve"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="boolean" id="boolean">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasProperty name="ordered" value="false"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="finite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#boolean"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="boolean.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="float" id="float">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="maxInclusive"/>
+ <hfp:hasFacet name="maxExclusive"/>
+ <hfp:hasFacet name="minInclusive"/>
+ <hfp:hasFacet name="minExclusive"/>
+ <hfp:hasProperty name="ordered" value="total"/>
+ <hfp:hasProperty name="bounded" value="true"/>
+ <hfp:hasProperty name="cardinality" value="finite"/>
+ <hfp:hasProperty name="numeric" value="true"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#float"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="float.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="double" id="double">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="maxInclusive"/>
+ <hfp:hasFacet name="maxExclusive"/>
+ <hfp:hasFacet name="minInclusive"/>
+ <hfp:hasFacet name="minExclusive"/>
+ <hfp:hasProperty name="ordered" value="total"/>
+ <hfp:hasProperty name="bounded" value="true"/>
+ <hfp:hasProperty name="cardinality" value="finite"/>
+ <hfp:hasProperty name="numeric" value="true"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#double"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="double.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="decimal" id="decimal">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="totalDigits"/>
+ <hfp:hasFacet name="fractionDigits"/>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="maxInclusive"/>
+ <hfp:hasFacet name="maxExclusive"/>
+ <hfp:hasFacet name="minInclusive"/>
+ <hfp:hasFacet name="minExclusive"/>
+ <hfp:hasProperty name="ordered" value="total"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="true"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#decimal"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="decimal.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="duration" id="duration">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="maxInclusive"/>
+ <hfp:hasFacet name="maxExclusive"/>
+ <hfp:hasFacet name="minInclusive"/>
+ <hfp:hasFacet name="minExclusive"/>
+ <hfp:hasProperty name="ordered" value="partial"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#duration"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="duration.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="dateTime" id="dateTime">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="maxInclusive"/>
+ <hfp:hasFacet name="maxExclusive"/>
+ <hfp:hasFacet name="minInclusive"/>
+ <hfp:hasFacet name="minExclusive"/>
+ <hfp:hasProperty name="ordered" value="partial"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#dateTime"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="dateTime.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="time" id="time">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="maxInclusive"/>
+ <hfp:hasFacet name="maxExclusive"/>
+ <hfp:hasFacet name="minInclusive"/>
+ <hfp:hasFacet name="minExclusive"/>
+ <hfp:hasProperty name="ordered" value="partial"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#time"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="time.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="date" id="date">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="maxInclusive"/>
+ <hfp:hasFacet name="maxExclusive"/>
+ <hfp:hasFacet name="minInclusive"/>
+ <hfp:hasFacet name="minExclusive"/>
+ <hfp:hasProperty name="ordered" value="partial"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#date"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="date.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="gYearMonth" id="gYearMonth">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="maxInclusive"/>
+ <hfp:hasFacet name="maxExclusive"/>
+ <hfp:hasFacet name="minInclusive"/>
+ <hfp:hasFacet name="minExclusive"/>
+ <hfp:hasProperty name="ordered" value="partial"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#gYearMonth"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="gYearMonth.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="gYear" id="gYear">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="maxInclusive"/>
+ <hfp:hasFacet name="maxExclusive"/>
+ <hfp:hasFacet name="minInclusive"/>
+ <hfp:hasFacet name="minExclusive"/>
+ <hfp:hasProperty name="ordered" value="partial"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#gYear"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="gYear.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="gMonthDay" id="gMonthDay">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="maxInclusive"/>
+ <hfp:hasFacet name="maxExclusive"/>
+ <hfp:hasFacet name="minInclusive"/>
+ <hfp:hasFacet name="minExclusive"/>
+ <hfp:hasProperty name="ordered" value="partial"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#gMonthDay"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="gMonthDay.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="gDay" id="gDay">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="maxInclusive"/>
+ <hfp:hasFacet name="maxExclusive"/>
+ <hfp:hasFacet name="minInclusive"/>
+ <hfp:hasFacet name="minExclusive"/>
+ <hfp:hasProperty name="ordered" value="partial"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#gDay"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="gDay.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="gMonth" id="gMonth">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="maxInclusive"/>
+ <hfp:hasFacet name="maxExclusive"/>
+ <hfp:hasFacet name="minInclusive"/>
+ <hfp:hasFacet name="minExclusive"/>
+ <hfp:hasProperty name="ordered" value="partial"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#gMonth"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="gMonth.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="hexBinary" id="hexBinary">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="length"/>
+ <hfp:hasFacet name="minLength"/>
+ <hfp:hasFacet name="maxLength"/>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasProperty name="ordered" value="false"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#binary"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="hexBinary.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="base64Binary" id="base64Binary">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="length"/>
+ <hfp:hasFacet name="minLength"/>
+ <hfp:hasFacet name="maxLength"/>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasProperty name="ordered" value="false"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#base64Binary"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="base64Binary.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="anyURI" id="anyURI">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="length"/>
+ <hfp:hasFacet name="minLength"/>
+ <hfp:hasFacet name="maxLength"/>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasProperty name="ordered" value="false"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#anyURI"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="anyURI.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="QName" id="QName">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="length"/>
+ <hfp:hasFacet name="minLength"/>
+ <hfp:hasFacet name="maxLength"/>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasProperty name="ordered" value="false"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#QName"/>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="QName.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="NOTATION" id="NOTATION">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="length"/>
+ <hfp:hasFacet name="minLength"/>
+ <hfp:hasFacet name="maxLength"/>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasProperty name="ordered" value="false"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#NOTATION"/>
+ <xs:documentation>
+ NOTATION cannot be used directly in a schema; rather a type
+ must be derived from it by specifying at least one enumeration
+ facet whose value is the name of a NOTATION declared in the
+ schema.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:anySimpleType">
+ <xs:whiteSpace value="collapse" fixed="true" id="NOTATION.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:annotation>
+ <xs:documentation>
+ Now the derived primitive types
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType name="normalizedString" id="normalizedString">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#normalizedString"/>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:whiteSpace value="replace" id="normalizedString.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="token" id="token">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#token"/>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:whiteSpace value="collapse" id="token.whiteSpace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="language" id="language">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#language"/>
+ </xs:annotation>
+ <xs:restriction base="xs:token">
+ <xs:pattern value="[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*" id="language.pattern">
+ <xs:annotation>
+ <xs:documentation source="http://www.ietf.org/rfc/rfc3066.txt">
+ pattern specifies the content of section 2.12 of XML 1.0e2
+ and RFC 3066 (Revised version of RFC 1766).
+ </xs:documentation>
+ </xs:annotation>
+ </xs:pattern>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="IDREFS" id="IDREFS">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="length"/>
+ <hfp:hasFacet name="minLength"/>
+ <hfp:hasFacet name="maxLength"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasProperty name="ordered" value="false"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#IDREFS"/>
+ </xs:annotation>
+ <xs:restriction>
+ <xs:simpleType>
+ <xs:list itemType="xs:IDREF"/>
+ </xs:simpleType>
+ <xs:minLength value="1" id="IDREFS.minLength"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="ENTITIES" id="ENTITIES">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="length"/>
+ <hfp:hasFacet name="minLength"/>
+ <hfp:hasFacet name="maxLength"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasProperty name="ordered" value="false"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#ENTITIES"/>
+ </xs:annotation>
+ <xs:restriction>
+ <xs:simpleType>
+ <xs:list itemType="xs:ENTITY"/>
+ </xs:simpleType>
+ <xs:minLength value="1" id="ENTITIES.minLength"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="NMTOKEN" id="NMTOKEN">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#NMTOKEN"/>
+ </xs:annotation>
+ <xs:restriction base="xs:token">
+ <xs:pattern value="\c+" id="NMTOKEN.pattern">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/REC-xml#NT-Nmtoken">
+ pattern matches production 7 from the XML spec
+ </xs:documentation>
+ </xs:annotation>
+ </xs:pattern>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="NMTOKENS" id="NMTOKENS">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasFacet name="length"/>
+ <hfp:hasFacet name="minLength"/>
+ <hfp:hasFacet name="maxLength"/>
+ <hfp:hasFacet name="enumeration"/>
+ <hfp:hasFacet name="whiteSpace"/>
+ <hfp:hasFacet name="pattern"/>
+ <hfp:hasProperty name="ordered" value="false"/>
+ <hfp:hasProperty name="bounded" value="false"/>
+ <hfp:hasProperty name="cardinality" value="countably infinite"/>
+ <hfp:hasProperty name="numeric" value="false"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#NMTOKENS"/>
+ </xs:annotation>
+ <xs:restriction>
+ <xs:simpleType>
+ <xs:list itemType="xs:NMTOKEN"/>
+ </xs:simpleType>
+ <xs:minLength value="1" id="NMTOKENS.minLength"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="Name" id="Name">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#Name"/>
+ </xs:annotation>
+ <xs:restriction base="xs:token">
+ <xs:pattern value="\i\c*" id="Name.pattern">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/REC-xml#NT-Name">
+ pattern matches production 5 from the XML spec
+ </xs:documentation>
+ </xs:annotation>
+ </xs:pattern>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="NCName" id="NCName">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#NCName"/>
+ </xs:annotation>
+ <xs:restriction base="xs:Name">
+ <xs:pattern value="[\i-[:]][\c-[:]]*" id="NCName.pattern">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/REC-xml-names/#NT-NCName">
+ pattern matches production 4 from the Namespaces in XML spec
+ </xs:documentation>
+ </xs:annotation>
+ </xs:pattern>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="ID" id="ID">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#ID"/>
+ </xs:annotation>
+ <xs:restriction base="xs:NCName"/>
+ </xs:simpleType>
+ <xs:simpleType name="IDREF" id="IDREF">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#IDREF"/>
+ </xs:annotation>
+ <xs:restriction base="xs:NCName"/>
+ </xs:simpleType>
+ <xs:simpleType name="ENTITY" id="ENTITY">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#ENTITY"/>
+ </xs:annotation>
+ <xs:restriction base="xs:NCName"/>
+ </xs:simpleType>
+ <xs:simpleType name="integer" id="integer">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#integer"/>
+ </xs:annotation>
+ <xs:restriction base="xs:decimal">
+ <xs:fractionDigits value="0" fixed="true" id="integer.fractionDigits"/>
+ <xs:pattern value="[\-+]?[0-9]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="nonPositiveInteger" id="nonPositiveInteger">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#nonPositiveInteger"/>
+ </xs:annotation>
+ <xs:restriction base="xs:integer">
+ <xs:maxInclusive value="0" id="nonPositiveInteger.maxInclusive"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="negativeInteger" id="negativeInteger">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#negativeInteger"/>
+ </xs:annotation>
+ <xs:restriction base="xs:nonPositiveInteger">
+ <xs:maxInclusive value="-1" id="negativeInteger.maxInclusive"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="long" id="long">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasProperty name="bounded" value="true"/>
+ <hfp:hasProperty name="cardinality" value="finite"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#long"/>
+ </xs:annotation>
+ <xs:restriction base="xs:integer">
+ <xs:minInclusive value="-9223372036854775808" id="long.minInclusive"/>
+ <xs:maxInclusive value="9223372036854775807" id="long.maxInclusive"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="int" id="int">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#int"/>
+ </xs:annotation>
+ <xs:restriction base="xs:long">
+ <xs:minInclusive value="-2147483648" id="int.minInclusive"/>
+ <xs:maxInclusive value="2147483647" id="int.maxInclusive"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="short" id="short">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#short"/>
+ </xs:annotation>
+ <xs:restriction base="xs:int">
+ <xs:minInclusive value="-32768" id="short.minInclusive"/>
+ <xs:maxInclusive value="32767" id="short.maxInclusive"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="byte" id="byte">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#byte"/>
+ </xs:annotation>
+ <xs:restriction base="xs:short">
+ <xs:minInclusive value="-128" id="byte.minInclusive"/>
+ <xs:maxInclusive value="127" id="byte.maxInclusive"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="nonNegativeInteger" id="nonNegativeInteger">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#nonNegativeInteger"/>
+ </xs:annotation>
+ <xs:restriction base="xs:integer">
+ <xs:minInclusive value="0" id="nonNegativeInteger.minInclusive"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="unsignedLong" id="unsignedLong">
+ <xs:annotation>
+ <xs:appinfo>
+ <hfp:hasProperty name="bounded" value="true"/>
+ <hfp:hasProperty name="cardinality" value="finite"/>
+ </xs:appinfo>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#unsignedLong"/>
+ </xs:annotation>
+ <xs:restriction base="xs:nonNegativeInteger">
+ <xs:maxInclusive value="18446744073709551615" id="unsignedLong.maxInclusive"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="unsignedInt" id="unsignedInt">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#unsignedInt"/>
+ </xs:annotation>
+ <xs:restriction base="xs:unsignedLong">
+ <xs:maxInclusive value="4294967295" id="unsignedInt.maxInclusive"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="unsignedShort" id="unsignedShort">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#unsignedShort"/>
+ </xs:annotation>
+ <xs:restriction base="xs:unsignedInt">
+ <xs:maxInclusive value="65535" id="unsignedShort.maxInclusive"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="unsignedByte" id="unsignedByte">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#unsignedByte"/>
+ </xs:annotation>
+ <xs:restriction base="xs:unsignedShort">
+ <xs:maxInclusive value="255" id="unsignedByte.maxInclusive"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="positiveInteger" id="positiveInteger">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#positiveInteger"/>
+ </xs:annotation>
+ <xs:restriction base="xs:nonNegativeInteger">
+ <xs:minInclusive value="1" id="positiveInteger.minInclusive"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="derivationControl">
+ <xs:annotation>
+ <xs:documentation>
+ A utility type, not for public use</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="substitution"/>
+ <xs:enumeration value="extension"/>
+ <xs:enumeration value="restriction"/>
+ <xs:enumeration value="list"/>
+ <xs:enumeration value="union"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:group name="simpleDerivation">
+ <xs:choice>
+ <xs:element ref="xs:restriction"/>
+ <xs:element ref="xs:list"/>
+ <xs:element ref="xs:union"/>
+ </xs:choice>
+ </xs:group>
+ <xs:simpleType name="simpleDerivationSet">
+ <xs:annotation>
+ <xs:documentation>
+ #all or (possibly empty) subset of {restriction, union, list}
+ </xs:documentation>
+ <xs:documentation>
+ A utility type, not for public use</xs:documentation>
+ </xs:annotation>
+ <xs:union>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="#all"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType>
+ <xs:list>
+ <xs:simpleType>
+ <xs:restriction base="xs:derivationControl">
+ <xs:enumeration value="list"/>
+ <xs:enumeration value="union"/>
+ <xs:enumeration value="restriction"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:list>
+ </xs:simpleType>
+ </xs:union>
+ </xs:simpleType>
+ <xs:complexType name="simpleType" abstract="true">
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:group ref="xs:simpleDerivation"/>
+ <xs:attribute name="final" type="xs:simpleDerivationSet"/>
+ <xs:attribute name="name" type="xs:NCName">
+ <xs:annotation>
+ <xs:documentation>
+ Can be restricted to required or forbidden
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="topLevelSimpleType">
+ <xs:complexContent>
+ <xs:restriction base="xs:simpleType">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:group ref="xs:simpleDerivation"/>
+ </xs:sequence>
+ <xs:attribute name="name" use="required" type="xs:NCName">
+ <xs:annotation>
+ <xs:documentation>
+ Required at the top level
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="localSimpleType">
+ <xs:complexContent>
+ <xs:restriction base="xs:simpleType">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ <xs:group ref="xs:simpleDerivation"/>
+ </xs:sequence>
+ <xs:attribute name="name" use="prohibited">
+ <xs:annotation>
+ <xs:documentation>
+ Forbidden when nested
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="final" use="prohibited"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="simpleType" type="xs:topLevelSimpleType" id="simpleType">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-simpleType"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:group name="facets">
+ <xs:annotation>
+ <xs:documentation>
+ We should use a substitution group for facets, but
+ that's ruled out because it would allow users to
+ add their own, which we're not ready for yet.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:choice>
+ <xs:element ref="xs:minExclusive"/>
+ <xs:element ref="xs:minInclusive"/>
+ <xs:element ref="xs:maxExclusive"/>
+ <xs:element ref="xs:maxInclusive"/>
+ <xs:element ref="xs:totalDigits"/>
+ <xs:element ref="xs:fractionDigits"/>
+ <xs:element ref="xs:length"/>
+ <xs:element ref="xs:minLength"/>
+ <xs:element ref="xs:maxLength"/>
+ <xs:element ref="xs:enumeration"/>
+ <xs:element ref="xs:whiteSpace"/>
+ <xs:element ref="xs:pattern"/>
+ </xs:choice>
+ </xs:group>
+ <xs:group name="simpleRestrictionModel">
+ <xs:sequence>
+ <xs:element name="simpleType" type="xs:localSimpleType" minOccurs="0"/>
+ <xs:group ref="xs:facets" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:group>
+ <xs:element name="restriction" id="restriction">
+ <xs:complexType>
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-restriction">
+ base attribute and simpleType child are mutually
+ exclusive, but one or other is required
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:group ref="xs:simpleRestrictionModel"/>
+ <xs:attribute name="base" type="xs:QName" use="optional"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="list" id="list">
+ <xs:complexType>
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-list">
+ itemType attribute and simpleType child are mutually
+ exclusive, but one or other is required
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:sequence>
+ <xs:element name="simpleType" type="xs:localSimpleType" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="itemType" type="xs:QName" use="optional"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="union" id="union">
+ <xs:complexType>
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-union">
+ memberTypes attribute must be non-empty or there must be
+ at least one simpleType child
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:sequence>
+ <xs:element name="simpleType" type="xs:localSimpleType" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="memberTypes" use="optional">
+ <xs:simpleType>
+ <xs:list itemType="xs:QName"/>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="facet">
+ <xs:complexContent>
+ <xs:extension base="xs:annotated">
+ <xs:attribute name="value" use="required"/>
+ <xs:attribute name="fixed" type="xs:boolean" use="optional" default="false"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="noFixedFacet">
+ <xs:complexContent>
+ <xs:restriction base="xs:facet">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="fixed" use="prohibited"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="minExclusive" id="minExclusive" type="xs:facet">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-minExclusive"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="minInclusive" id="minInclusive" type="xs:facet">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-minInclusive"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="maxExclusive" id="maxExclusive" type="xs:facet">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-maxExclusive"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="maxInclusive" id="maxInclusive" type="xs:facet">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-maxInclusive"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:complexType name="numFacet">
+ <xs:complexContent>
+ <xs:restriction base="xs:facet">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="value" type="xs:nonNegativeInteger" use="required"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:element name="totalDigits" id="totalDigits">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-totalDigits"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:restriction base="xs:numFacet">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="value" type="xs:positiveInteger" use="required"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="fractionDigits" id="fractionDigits" type="xs:numFacet">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-fractionDigits"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="length" id="length" type="xs:numFacet">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-length"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="minLength" id="minLength" type="xs:numFacet">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-minLength"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="maxLength" id="maxLength" type="xs:numFacet">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-maxLength"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="enumeration" id="enumeration" type="xs:noFixedFacet">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-enumeration"/>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="whiteSpace" id="whiteSpace">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-whiteSpace"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:restriction base="xs:facet">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="value" use="required">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="preserve"/>
+ <xs:enumeration value="replace"/>
+ <xs:enumeration value="collapse"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="pattern" id="pattern">
+ <xs:annotation>
+ <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-pattern"/>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:restriction base="xs:noFixedFacet">
+ <xs:sequence>
+ <xs:element ref="xs:annotation" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="value" type="xs:string" use="required"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:restriction>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
diff --git a/testxml/xsd/checkfile.sh b/testxml/xsd/checkfile.sh
new file mode 100755
index 0000000..90df1f1
--- /dev/null
+++ b/testxml/xsd/checkfile.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+xmllint --format --schema tmw.xsd "${1}" 2>&1 >/dev/null | \
+ grep -v ": Skipping import of schema located at " | \
+ grep -v ".xml validates"
diff --git a/testxml/xsd/its.xsd b/testxml/xsd/its.xsd
new file mode 100644
index 0000000..9dbb432
--- /dev/null
+++ b/testxml/xsd/its.xsd
@@ -0,0 +1,926 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://www.w3.org/2005/11/its" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its">
+ <!--
+ Schema generated from ODD source 2007-04-02T11:42:44+09:00. This schema has been developed using the ODD (One Document Does it
+ all) language of the Text Encoding Initiative (). This is a literate programming language for writing XML schemas, with three
+ characteristics: (1) The element and attribute set is specified using
+ an XML vocabulary which includes support for macros
+ (like DTD entities, or schema patterns), a hierarchical
+ class system for attributes and elements, and creation
+ of modules. (2) The content models for elements and attributes is
+ written using embedded RELAX NG XML notation. (3) Documentation for elements, attributes, value
+ lists etc. is written inline, along with examples and
+ other supporting material. XSLT transformations
+ are provided by the TEI to extract documentation in
+ HTML, XSL FO or LaTeX forms, and to generate RELAX NG
+ documents and DTD. From the RELAX NG documents, James
+ Clark's trang
+ can be used to create XML Schema documents.
+ -->
+ <xs:import namespace="http://www.w3.org/1999/xlink" schemaLocation="xlink.xsd"/>
+ <xs:element name="rules">
+ <xs:annotation>
+ <xs:documentation>Container for global rules.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="its:its-rules.content">
+ <xs:attributeGroup ref="its:its-rules.attributes"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="its-rules.content">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="its:translateRule"/>
+ <xs:element ref="its:locNoteRule"/>
+ <xs:element ref="its:termRule"/>
+ <xs:element ref="its:dirRule"/>
+ <xs:element ref="its:rubyRule"/>
+ <xs:element ref="its:langRule"/>
+ <xs:element ref="its:withinTextRule"/>
+ </xs:choice>
+ </xs:complexType>
+ <xs:attributeGroup name="its-rules.attributes">
+ <xs:attribute name="version" use="required" type="xs:float">
+ <xs:annotation>
+ <xs:documentation>Version of the ITS schema.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="xlink:its-type"/>
+ <xs:attributeGroup ref="xlink:its-href"/>
+ <!--
+ <xs:attribute ref="xlink:href"/>
+ <xs:attribute ref="xlink:type"/> -->
+ </xs:attributeGroup>
+ <xs:attributeGroup name="its-att.selector.attributes">
+ <xs:attributeGroup ref="its:att.selector.attribute.selector"/>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.selector.attribute.selector">
+ <xs:attribute name="selector" use="required" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>XPath expression identifying the nodes to be selected.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="its-att.version.attributes">
+ <xs:attributeGroup ref="its:att.version.attribute.version"/>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.version.attribute.version">
+ <xs:attribute name="version" use="required" form="qualified" type="xs:float">
+ <xs:annotation>
+ <xs:documentation>Version of the ITS schema.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="its-att.local.no-ns.attributes">
+ <xs:attributeGroup ref="its:att.local.no-ns.attribute.translate"/>
+ <xs:attributeGroup ref="its:att.local.no-ns.attribute.locNote"/>
+ <xs:attributeGroup ref="its:att.local.no-ns.attribute.locNoteType"/>
+ <xs:attributeGroup ref="its:att.local.no-ns.attribute.locNoteRef"/>
+ <xs:attributeGroup ref="its:att.local.no-ns.attribute.termInfoRef"/>
+ <xs:attributeGroup ref="its:att.local.no-ns.attribute.term"/>
+ <xs:attributeGroup ref="its:att.local.no-ns.attribute.dir"/>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.no-ns.attribute.translate">
+ <xs:attribute name="translate">
+ <xs:annotation>
+ <xs:documentation>The Translate data category information to be attached to
+ the current node.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="yes">
+ <xs:annotation>
+ <xs:documentation>The nodes need to be translated.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="no">
+ <xs:annotation>
+ <xs:documentation>The nodes must not be translated.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.no-ns.attribute.locNote">
+ <xs:attribute name="locNote" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Localization note.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.no-ns.attribute.locNoteType">
+ <xs:attribute name="locNoteType">
+ <xs:annotation>
+ <xs:documentation>The type of localization note.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="alert">
+ <xs:annotation>
+ <xs:documentation>Localization note is an alert.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="description">
+ <xs:annotation>
+ <xs:documentation>Localization note is a description.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.no-ns.attribute.locNoteRef">
+ <xs:attribute name="locNoteRef" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>URI referring to the location of the localization note.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.no-ns.attribute.termInfoRef">
+ <xs:attribute name="termInfoRef" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>Pointer to a resource containing
+ information about the term.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.no-ns.attribute.term">
+ <xs:attribute name="term">
+ <xs:annotation>
+ <xs:documentation>Indicates a term locally.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="yes">
+ <xs:annotation>
+ <xs:documentation>The value 'yes' means that this is a term.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="no">
+ <xs:annotation>
+ <xs:documentation>The value 'no' means that this is not a term.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.no-ns.attribute.dir">
+ <xs:attribute name="dir">
+ <xs:annotation>
+ <xs:documentation>The text direction for the context.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="ltr">
+ <xs:annotation>
+ <xs:documentation>Left-to-right text.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="rtl">
+ <xs:annotation>
+ <xs:documentation>Right-to-left text.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="lro">
+ <xs:annotation>
+ <xs:documentation>Left-to-right override.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="rlo">
+ <xs:annotation>
+ <xs:documentation>Right-to-left override.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="its-att.local.with-ns.attributes">
+ <xs:attributeGroup ref="its:att.local.with-ns.attribute.translate"/>
+ <xs:attributeGroup ref="its:att.local.with-ns.attribute.locNote"/>
+ <xs:attributeGroup ref="its:att.local.with-ns.attribute.locNoteType"/>
+ <xs:attributeGroup ref="its:att.local.with-ns.attribute.locNoteRef"/>
+ <xs:attributeGroup ref="its:att.local.with-ns.attribute.termInfoRef"/>
+ <xs:attributeGroup ref="its:att.local.with-ns.attribute.term"/>
+ <xs:attributeGroup ref="its:att.local.with-ns.attribute.dir"/>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.with-ns.attribute.translate">
+ <xs:attribute name="translate" form="qualified">
+ <xs:annotation>
+ <xs:documentation>The Translate data category information to be attached to
+ the current node.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="yes">
+ <xs:annotation>
+ <xs:documentation>The nodes need to be translated.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="no">
+ <xs:annotation>
+ <xs:documentation>The nodes must not be translated.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.with-ns.attribute.locNote">
+ <xs:attribute name="locNote" form="qualified" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Localization note.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.with-ns.attribute.locNoteType">
+ <xs:attribute name="locNoteType" form="qualified">
+ <xs:annotation>
+ <xs:documentation>The type of localization note.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="alert">
+ <xs:annotation>
+ <xs:documentation>Localization note is an alert.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="description">
+ <xs:annotation>
+ <xs:documentation>Localization note is a description.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.with-ns.attribute.locNoteRef">
+ <xs:attribute name="locNoteRef" form="qualified" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>URI referring to the location of the localization note.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.with-ns.attribute.termInfoRef">
+ <xs:attribute name="termInfoRef" form="qualified" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>Pointer to a resource containing
+ information about the term.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.with-ns.attribute.term">
+ <xs:attribute name="term" form="qualified">
+ <xs:annotation>
+ <xs:documentation>Indicates a term locally.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="yes">
+ <xs:annotation>
+ <xs:documentation>The value 'yes' means that this is a term.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="no">
+ <xs:annotation>
+ <xs:documentation>The value 'no' means that this is not a term.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.local.with-ns.attribute.dir">
+ <xs:attribute name="dir" form="qualified">
+ <xs:annotation>
+ <xs:documentation>The text direction for the context.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="ltr">
+ <xs:annotation>
+ <xs:documentation>Left-to-right text.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="rtl">
+ <xs:annotation>
+ <xs:documentation>Right-to-left text.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="lro">
+ <xs:annotation>
+ <xs:documentation>Left-to-right override.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="rlo">
+ <xs:annotation>
+ <xs:documentation>Right-to-left override.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:element name="span">
+ <xs:annotation>
+ <xs:documentation>Inline element to contain ITS information.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="its:its-span.content">
+ <xs:attributeGroup ref="its:its-span.attributes"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="its-span.content" mixed="true">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="its:ruby"/>
+ <xs:element ref="its:span"/>
+ </xs:choice>
+ </xs:complexType>
+ <xs:attributeGroup name="its-span.attributes">
+ <xs:attributeGroup ref="its:its-att.local.no-ns.attributes"/>
+ </xs:attributeGroup>
+ <xs:element name="translateRule">
+ <xs:annotation>
+ <xs:documentation>Rule about the Translate data category.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attributeGroup ref="its:its-translateRule.attributes"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:attributeGroup name="its-translateRule.attributes">
+ <xs:attributeGroup ref="its:its-att.selector.attributes"/>
+ <xs:attribute name="translate" use="required">
+ <xs:annotation>
+ <xs:documentation>The Translate data category information to be
+ applied to selected nodes.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="yes">
+ <xs:annotation>
+ <xs:documentation>The nodes need to be translated.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="no">
+ <xs:annotation>
+ <xs:documentation>The nodes must not be translated.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="its-att.translate.attributes">
+ <xs:attributeGroup ref="its:att.translate.attribute.translate"/>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.translate.attribute.translate">
+ <xs:attribute name="translate" form="qualified">
+ <xs:annotation>
+ <xs:documentation>The Translate data category information to be attached to
+ the current node.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="yes">
+ <xs:annotation>
+ <xs:documentation>The nodes need to be translated.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="no">
+ <xs:annotation>
+ <xs:documentation>The nodes must not be translated.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:element name="locNoteRule">
+ <xs:annotation>
+ <xs:documentation>Rule about the Localization Note data category.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="its:its-locNoteRule.content">
+ <xs:attributeGroup ref="its:its-locNoteRule.attributes"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="its-locNoteRule.content">
+ <xs:sequence>
+ <xs:element minOccurs="0" ref="its:locNote"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:attributeGroup name="its-locNoteRule.attributes">
+ <xs:attributeGroup ref="its:its-att.selector.attributes"/>
+ <xs:attribute name="locNotePointer" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Relative XPath expression pointing to a node that holds the localization note.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="locNoteType" use="required">
+ <xs:annotation>
+ <xs:documentation>The type of localization note.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="alert">
+ <xs:annotation>
+ <xs:documentation>Localization note is an alert.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="description">
+ <xs:annotation>
+ <xs:documentation>Localization note is a description.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="locNoteRef" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>URI referring to the location of the localization note.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="locNoteRefPointer" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Relative XPath expression pointing to a node that holds the URI referring to the location of the localization note.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:element name="locNote">
+ <xs:annotation>
+ <xs:documentation>Contains a localization note.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="its:its-locNote.content">
+ <xs:attributeGroup ref="its:its-locNote.attributes"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="its-locNote.content" mixed="true">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="its:ruby"/>
+ <xs:element ref="its:span"/>
+ </xs:choice>
+ </xs:complexType>
+ <xs:attributeGroup name="its-locNote.attributes">
+ <xs:attributeGroup ref="its:its-att.local.no-ns.attributes"/>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="its-att.locNote.attributes">
+ <xs:attributeGroup ref="its:att.locNote.attribute.locNote"/>
+ <xs:attributeGroup ref="its:att.locNote.attribute.locNoteType"/>
+ <xs:attributeGroup ref="its:att.locNote.attribute.locNoteRef"/>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.locNote.attribute.locNote">
+ <xs:attribute name="locNote" form="qualified" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Localization note.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.locNote.attribute.locNoteType">
+ <xs:attribute name="locNoteType" form="qualified">
+ <xs:annotation>
+ <xs:documentation>The type of localization note.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="alert">
+ <xs:annotation>
+ <xs:documentation>Localization note is an alert.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="description">
+ <xs:annotation>
+ <xs:documentation>Localization note is a description.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.locNote.attribute.locNoteRef">
+ <xs:attribute name="locNoteRef" form="qualified" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>URI referring to the location of the localization note.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:element name="termRule">
+ <xs:annotation>
+ <xs:documentation>Rule about the Terminology data category.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attributeGroup ref="its:its-termRule.attributes"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:attributeGroup name="its-termRule.attributes">
+ <xs:attributeGroup ref="its:its-att.selector.attributes"/>
+ <xs:attribute name="term" use="required">
+ <xs:annotation>
+ <xs:documentation>Indicates whether the selection is a term or not.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="yes">
+ <xs:annotation>
+ <xs:documentation>The value 'yes' means that this is a term.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="no">
+ <xs:annotation>
+ <xs:documentation>The value 'no' means that this is not a term.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="termInfoRef" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>URI referring to the resource providing information about the term.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="termInfoRefPointer" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Relative XPath expression pointing to a node containing a URI referring to the resource providing information about the term.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="termInfoPointer" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Relative XPath expression pointing to a node containing
+ information about the term.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="its-att.term.attributes">
+ <xs:attributeGroup ref="its:att.term.attribute.termInfoRef"/>
+ <xs:attributeGroup ref="its:att.term.attribute.term"/>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.term.attribute.termInfoRef">
+ <xs:attribute name="termInfoRef" form="qualified" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation>Pointer to a resource containing
+ information about the term.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.term.attribute.term">
+ <xs:attribute name="term" form="qualified">
+ <xs:annotation>
+ <xs:documentation>Indicates a term locally.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="yes">
+ <xs:annotation>
+ <xs:documentation>The value 'yes' means that this is a term.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="no">
+ <xs:annotation>
+ <xs:documentation>The value 'no' means that this is not a term.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:element name="dirRule">
+ <xs:annotation>
+ <xs:documentation>Rule about the Directionality data category.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attributeGroup ref="its:its-dirRule.attributes"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:attributeGroup name="its-dirRule.attributes">
+ <xs:attributeGroup ref="its:its-att.selector.attributes"/>
+ <xs:attribute name="dir" use="required">
+ <xs:annotation>
+ <xs:documentation>The text direction for the selection.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="ltr">
+ <xs:annotation>
+ <xs:documentation>Left-to-right text.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="rtl">
+ <xs:annotation>
+ <xs:documentation>Right-to-left text.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="lro">
+ <xs:annotation>
+ <xs:documentation>Left-to-right override.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="rlo">
+ <xs:annotation>
+ <xs:documentation>Right-to-left override.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="its-att.dir.attributes">
+ <xs:attributeGroup ref="its:att.dir.attribute.dir"/>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="att.dir.attribute.dir">
+ <xs:attribute name="dir" form="qualified">
+ <xs:annotation>
+ <xs:documentation>The text direction for the context.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="ltr">
+ <xs:annotation>
+ <xs:documentation>Left-to-right text.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="rtl">
+ <xs:annotation>
+ <xs:documentation>Right-to-left text.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="lro">
+ <xs:annotation>
+ <xs:documentation>Left-to-right override.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="rlo">
+ <xs:annotation>
+ <xs:documentation>Right-to-left override.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:element name="rubyRule">
+ <xs:annotation>
+ <xs:documentation>Rule about the Ruby data category.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="its:its-rubyRule.content">
+ <xs:attributeGroup ref="its:its-rubyRule.attributes"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="its-rubyRule.content">
+ <xs:sequence>
+ <xs:element minOccurs="0" ref="its:rubyText"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:attributeGroup name="its-rubyRule.attributes">
+ <xs:attributeGroup ref="its:its-att.selector.attributes"/>
+ <xs:attribute name="rubyPointer" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Relative XPath expression pointing to a node that corresponds to a ruby element</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="rtPointer" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Relative XPath expression pointing to a node that
+ corresponds to a rt element</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="rpPointer" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Relative XPath expression pointing to a node that
+ corresponds to a rp element</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="rbcPointer" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Relative XPath expression pointing to a node that
+ corresponds to a rbc element</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="rtcPointer" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Relative XPath expression pointing to a node that
+ corresponds to a rtc element</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="rbspanPointer" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Relative XPath expression pointing to a node that corresponds to a rbspan attribute.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:element name="rubyText">
+ <xs:annotation>
+ <xs:documentation>Ruby text.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType mixed="true">
+ <xs:attributeGroup ref="its:its-rubyText.attributes"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:attributeGroup name="its-rubyText.attributes">
+ <xs:attributeGroup ref="its:its-att.local.no-ns.attributes"/>
+ <xs:attribute name="rbspan" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Allows an rt element to span multiple rb elements in complex ruby markup.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:element name="ruby">
+ <xs:annotation>
+ <xs:documentation>Ruby markup.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="its:its-ruby.content">
+ <xs:attributeGroup ref="its:its-ruby.attributes"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="its-ruby.content">
+ <xs:choice>
+ <xs:sequence>
+ <xs:element ref="its:rb"/>
+ <xs:choice>
+ <xs:element ref="its:rt"/>
+ <xs:sequence>
+ <xs:element ref="its:rp"/>
+ <xs:element ref="its:rt"/>
+ <xs:element ref="its:rp"/>
+ </xs:sequence>
+ </xs:choice>
+ </xs:sequence>
+ <xs:sequence>
+ <xs:element ref="its:rbc"/>
+ <xs:element ref="its:rtc"/>
+ <xs:element minOccurs="0" ref="its:rtc"/>
+ </xs:sequence>
+ </xs:choice>
+ </xs:complexType>
+ <xs:attributeGroup name="its-ruby.attributes">
+ <xs:attributeGroup ref="its:its-att.local.no-ns.attributes"/>
+ </xs:attributeGroup>
+ <xs:element name="rb">
+ <xs:annotation>
+ <xs:documentation>Ruby base text.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="its:its-rb.content">
+ <xs:attributeGroup ref="its:its-rb.attributes"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="its-rb.content" mixed="true">
+ <xs:sequence>
+ <xs:element minOccurs="0" maxOccurs="unbounded" ref="its:span"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:attributeGroup name="its-rb.attributes">
+ <xs:attributeGroup ref="its:its-att.local.no-ns.attributes"/>
+ </xs:attributeGroup>
+ <xs:element name="rt">
+ <xs:annotation>
+ <xs:documentation>Ruby text.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="its:its-rt.content">
+ <xs:attributeGroup ref="its:its-rt.attributes"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="its-rt.content" mixed="true">
+ <xs:sequence>
+ <xs:element minOccurs="0" maxOccurs="unbounded" ref="its:span"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:attributeGroup name="its-rt.attributes">
+ <xs:attributeGroup ref="its:its-att.local.no-ns.attributes"/>
+ <xs:attribute name="rbspan" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Allows an rt element to span multiple rb elements in complex ruby markup.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:element name="rbc">
+ <xs:annotation>
+ <xs:documentation>Container for rb elements in the case of complex ruby markup.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="its:its-rbc.content">
+ <xs:attributeGroup ref="its:its-rbc.attributes"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="its-rbc.content">
+ <xs:sequence>
+ <xs:element maxOccurs="unbounded" ref="its:rb"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:attributeGroup name="its-rbc.attributes">
+ <xs:attributeGroup ref="its:its-att.local.no-ns.attributes"/>
+ </xs:attributeGroup>
+ <xs:element name="rtc">
+ <xs:annotation>
+ <xs:documentation>Container for rt elements in the case of complex ruby markup. </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="its:its-rtc.content">
+ <xs:attributeGroup ref="its:its-rtc.attributes"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="its-rtc.content">
+ <xs:sequence>
+ <xs:element maxOccurs="unbounded" ref="its:rt"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:attributeGroup name="its-rtc.attributes">
+ <xs:attributeGroup ref="its:its-att.local.no-ns.attributes"/>
+ </xs:attributeGroup>
+ <xs:element name="rp">
+ <xs:annotation>
+ <xs:documentation>Used in the case of simple ruby markup to specify characters that can denote the beginning and end of ruby text when user agents do not have other ways to present ruby text distinctively from the base text.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType mixed="true">
+ <xs:attributeGroup ref="its:its-rp.attributes"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:attributeGroup name="its-rp.attributes">
+ <xs:attributeGroup ref="its:its-att.local.no-ns.attributes"/>
+ </xs:attributeGroup>
+ <xs:element name="langRule">
+ <xs:annotation>
+ <xs:documentation>Rule about the Language Information data category.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attributeGroup ref="its:its-langRule.attributes"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:attributeGroup name="its-langRule.attributes">
+ <xs:attributeGroup ref="its:its-att.selector.attributes"/>
+ <xs:attribute name="langPointer" use="required" type="xs:string">
+ <xs:annotation>
+ <xs:documentation>Relative XPath expression pointing to a node that contains language information.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:element name="withinTextRule">
+ <xs:annotation>
+ <xs:documentation>Rule about the Elements Within Text data category.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attributeGroup ref="its:its-withinTextRule.attributes"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:attributeGroup name="its-withinTextRule.attributes">
+ <xs:attributeGroup ref="its:its-att.selector.attributes"/>
+ <xs:attribute name="withinText" use="required">
+ <xs:annotation>
+ <xs:documentation>States whether current context is regarded as
+ "within text".</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="yes">
+ <xs:annotation>
+ <xs:documentation>The element and its content are part of the flow of its parent element.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="no">
+ <xs:annotation>
+ <xs:documentation>The element splits the text flow of its parent element and its content is an independent text flow.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="nested">
+ <xs:annotation>
+ <xs:documentation>The element is part of the flow of its parent element, its content is an independent flow.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+</xs:schema>
diff --git a/testxml/xsd/tmw.xsd b/testxml/xsd/tmw.xsd
new file mode 100644
index 0000000..a8b5cf2
--- /dev/null
+++ b/testxml/xsd/tmw.xsd
@@ -0,0 +1,2117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) -2016 The Mana World Development Team
+ Copyright (C) 2016 Evol Online developers
+ Copyright (C) 2020 TMW2: Moubootaur Legends Team
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:its="http://www.w3.org/2005/11/its" elementFormDefault="qualified">
+ <xs:import namespace="http://www.w3.org/2005/11/its" schemaLocation="its.xsd"/>
+ <!-- stuff up here is used everywhere-->
+ <xs:element name="include" type="include"/>
+ <xs:complexType name="include">
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="empty"/>
+ <!--
+ XML::getBoolProperty
+ -->
+ <xs:simpleType name="bool">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="true"/>
+ <xs:enumeration value="false"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!-- roots, with their children nearby -->
+ <!--
+ EffectManager::loadXmlFile
+ effectsFile: effects.xml
+ effectsPatchFile: effects_patch.xml
+ effectsPatchDir: effects.d/*.xml
+ -->
+ <xs:element name="being-effects" type="root_being-effects"/>
+ <xs:complexType name="root_being-effects">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="effect">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="particle" type="xs:string" use="optional"/>
+ <xs:attribute name="audio" type="xs:string" use="optional"/>
+ <xs:attribute name="sprite" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <!--
+ Configuration::init/ConfigurationObject::initFromXML
+ branding
+ portable.xml
+ command-line
+ config
+ <configdir>/test.xml
+ <configdir>/config.xml
+ serverConfig
+ <configdir>/<server>/config.xml
+ paths
+ paths.xml
+ features
+ featuresFile features.xml
+ -->
+ <xs:element name="configuration" type="root_configuration"/>
+ <xs:complexType name="root_configuration">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="list">
+ <xs:complexType>
+ <!-- what manaplus does is actually impossible to represent -->
+ <!-- TODO in a future version fix this horrible abuse of XML -->
+ <xs:choice>
+ <xs:element name="nested_conf_1" type="root_configuration" maxOccurs="unbounded"/>
+ <xs:element name="nested_conf_2" type="root_configuration" maxOccurs="unbounded"/>
+ </xs:choice>
+ <xs:attribute name="name" type="nested_configuration" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="option">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="value" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="nested_configuration">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="nested_conf_1"/>
+ <xs:enumeration value="nested_conf_2"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!--
+ Particle::addEffect(file|dye, x, y, rot)
+ -->
+ <xs:element name="effect" type="root_effect"/>
+ <xs:complexType name="root_effect">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="particle">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:group ref="particle_extras" minOccurs="0" maxOccurs="unbounded"/>
+ <!-- this should probably be required to go first -->
+ <xs:choice minOccurs="0">
+ <xs:element name="animation" type="animation_1"/>
+ <xs:element name="rotation" type="animation_1"/>
+ <xs:element name="image" type="xs:string"/>
+ </xs:choice>
+ <xs:group ref="particle_extras" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="position-x" type="xs:float" default="0"/>
+ <xs:attribute name="position-y" type="xs:float" default="0"/>
+ <xs:attribute name="position-z" type="xs:float" default="0"/>
+ <!-- TODO is this really? It was so unusual, but it's common. -->
+ <xs:attribute name="lifetime" type="xs:int" use="optional"/>
+ <xs:attribute name="size-adjustable" type="bool" default="false"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:group name="particle_extras">
+ <xs:choice>
+ <xs:element name="emitter" type="particle_emitter"/>
+ <xs:element name="deatheffect" type="death_effect"/>
+ </xs:choice>
+ </xs:group>
+ <xs:complexType name="death_effect">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="on-floor" type="bool" default="true"/>
+ <xs:attribute name="on-sky" type="bool" default="true"/>
+ <xs:attribute name="on-other" type="bool" default="false"/>
+ <xs:attribute name="on-impact" type="bool" default="true"/>
+ <xs:attribute name="on-timeout" type="bool" default="true"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ <!--
+ SimpleAnimation::initializeAnimation
+ -->
+ <xs:complexType name="animation_1">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="frame">
+ <xs:complexType>
+ <xs:attributeGroup ref="frame_or_sequence_attrs"/>
+ <xs:attribute name="index" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="sequence">
+ <xs:complexType>
+ <xs:attributeGroup ref="frame_or_sequence_attrs"/>
+ <xs:attribute name="start" type="xs:int" use="required"/>
+ <xs:attribute name="end" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="end" type="empty"/>
+ </xs:choice>
+ <xs:attribute name="imageset" type="xs:string" use="required"/>
+ <xs:attribute name="width" type="xs:int" use="required"/>
+ <xs:attribute name="height" type="xs:int" use="required"/>
+ </xs:complexType>
+ <!--
+ ParticleEmitter::ParticleEmitter
+ -->
+ <xs:complexType name="animation_2">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="frame">
+ <xs:complexType>
+ <xs:attributeGroup ref="frame_or_sequence_attrs"/>
+ <xs:attribute name="index" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="sequence">
+ <xs:complexType>
+ <xs:attributeGroup ref="frame_or_sequence_attrs"/>
+ <xs:attribute name="start" type="xs:int" use="required"/>
+ <xs:attribute name="end" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="end" type="empty"/>
+ </xs:choice>
+ <xs:attribute name="imageset" type="xs:string" use="required"/>
+ <xs:attribute name="width" type="xs:int" use="required"/>
+ <xs:attribute name="height" type="xs:int" use="required"/>
+ <xs:attribute name="subX" type="xs:int" use="optional"/>
+ <xs:attribute name="subY" type="xs:int" use="optional"/>
+ <xs:attribute name="subWidth" type="xs:int" use="optional"/>
+ <xs:attribute name="subHeight" type="xs:int" use="optional"/>
+ </xs:complexType>
+ <!--
+ SpriteDef::loadAnimation
+ -->
+ <xs:complexType name="animation_3">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="frame">
+ <xs:complexType>
+ <xs:attributeGroup ref="frame_or_sequence_attrs"/>
+ <xs:attribute name="index" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="sequence">
+ <xs:complexType>
+ <xs:attributeGroup ref="frame_or_sequence_attrs"/>
+ <xs:attribute name="start" type="xs:int" use="optional"/>
+ <xs:attribute name="end" type="xs:int" use="optional"/>
+ <xs:attribute name="value" type="xs:string" use="optional"/>
+ <xs:attribute name="repeat" type="xs:int" default="1"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="end" type="empty"/>
+ <xs:element name="jump">
+ <xs:complexType>
+ <xs:attribute name="action" type="xs:string" use="required"/>
+ <xs:attribute name="rand" type="xs:int" default="100"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="label">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="goto">
+ <xs:complexType>
+ <xs:attribute name="label" type="xs:string" use="required"/>
+ <xs:attribute name="rand" type="xs:int" default="100"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ <xs:attribute name="direction" default="default">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="default"/>
+ <xs:enumeration value="up"/>
+ <xs:enumeration value="left"/>
+ <xs:enumeration value="right"/>
+ <xs:enumeration value="down"/>
+ <xs:enumeration value="upleft"/>
+ <xs:enumeration value="upright"/>
+ <xs:enumeration value="downleft"/>
+ <xs:enumeration value="downright"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:complexType>
+ <xs:attributeGroup name="frame_or_sequence_attrs">
+ <xs:attribute name="delay" type="xs:int" default="0"/>
+ <xs:attribute name="offsetX" type="xs:int" default="0"/>
+ <xs:attribute name="offsetY" type="xs:int" default="0"/>
+ <xs:attribute name="rand" type="xs:int" default="100"/>
+ </xs:attributeGroup>
+ <!--
+ ParticleEmitter::ParticleEmitter
+ -->
+ <xs:complexType name="particle_emitter">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="property">
+ <xs:complexType>
+ <!-- TODO in a future version fix this horrible abuse of XML -->
+ <xs:attribute name="name" type="particle_property" use="required"/>
+ <!-- type varies depending on property -->
+ <xs:attribute name="value" type="xs:string" use="optional"/>
+ <xs:attribute name="min" type="xs:float" use="optional"/>
+ <xs:attribute name="max" type="xs:float" use="optional"/>
+ <xs:attribute name="change-func" default="none">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="none"/>
+ <xs:enumeration value="saw"/>
+ <xs:enumeration value="sawtooth"/>
+ <xs:enumeration value="sine"/>
+ <xs:enumeration value="sinewave"/>
+ <xs:enumeration value="triangle"/>
+ <xs:enumeration value="square"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="change-amplitude" type="xs:float" use="optional"/>
+ <xs:attribute name="change-period" type="xs:int" use="optional"/>
+ <xs:attribute name="change-phase" type="xs:int" use="optional"/>
+ <!-- only for subimage -->
+ <xs:attribute name="x" type="xs:int" use="optional"/>
+ <xs:attribute name="y" type="xs:int" use="optional"/>
+ <xs:attribute name="width" type="xs:int" use="optional"/>
+ <xs:attribute name="height" type="xs:int" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="emitter" type="particle_emitter"/>
+ <xs:element name="rotation" type="animation_2"/>
+ <xs:element name="animation" type="animation_2"/>
+ <xs:element name="deatheffect" type="death_effect"/>
+ </xs:choice>
+ </xs:complexType>
+ <xs:simpleType name="particle_property">
+ <xs:restriction base="xs:string">
+ <!-- xs:float -->
+ <xs:enumeration value="position-x"/>
+ <!-- xs:float -->
+ <xs:enumeration value="position-y"/>
+ <!-- xs:float -->
+ <xs:enumeration value="position-z"/>
+ <!-- xs:string -->
+ <xs:enumeration value="image"/>
+ <!-- xs:string for value, also xs:int for x/w/width/height -->
+ <xs:enumeration value="subimage"/>
+ <!-- xs:float -->
+ <xs:enumeration value="horizontal-angle"/>
+ <!-- xs:float -->
+ <xs:enumeration value="vertical-angle"/>
+ <!-- xs:float -->
+ <xs:enumeration value="power"/>
+ <!-- xs:float -->
+ <xs:enumeration value="gravity"/>
+ <!-- xs:int -->
+ <xs:enumeration value="randomness"/>
+ <!-- xs:int -->
+ <xs:enumeration value="randomnes"/>
+ <!-- xs:float -->
+ <xs:enumeration value="bounce"/>
+ <!-- xs:int -->
+ <xs:enumeration value="lifetime"/>
+ <!-- xs:int -->
+ <xs:enumeration value="output"/>
+ <!-- xs:int -->
+ <xs:enumeration value="output-pause"/>
+ <!-- xs:float -->
+ <xs:enumeration value="acceleration"/>
+ <!-- xs:float -->
+ <xs:enumeration value="die-distance"/>
+ <!-- xs:float -->
+ <xs:enumeration value="momentum"/>
+ <!-- xs:int -->
+ <xs:enumeration value="fade-out"/>
+ <!-- xs:int -->
+ <xs:enumeration value="fade-in"/>
+ <!-- xs:float -->
+ <xs:enumeration value="alpha"/>
+ <!-- ignored -->
+ <xs:enumeration value="follow-parent"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!--
+ QuestsWindow::loadXmlFile
+ questsFile: quests.xml
+ questsPatchFile: quests_patch.xml
+ questsPatchDir: quests.d/*.xml
+ -->
+ <xs:element name="quests" type="root_quests"/>
+ <xs:complexType name="root_quests">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="var">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="quest" type="quests_var_quest"/>
+ <xs:element name="effect" type="quests_var_effect"/>
+ </xs:choice>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="nowiki" type="xs:int" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <!--
+ QuestsWindow::loadQuest
+ -->
+ <xs:complexType name="quests_var_quest">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="text" type="quests_quest_text"/>
+ <xs:element name="name" type="xs:string"/>
+ <xs:element name="reward" type="quests_quest_text"/>
+ <xs:element name="giver" type="xs:string"/>
+ <xs:element name="questgiver" type="xs:string"/>
+ <xs:element name="coordinates" type="quests_quest_coordinates"/>
+ <xs:element name="npc" type="xs:string"/>
+ <xs:element name="level" type="xs:string"/>
+ <xs:element name="wiki" type="xs:string"/>
+ </xs:choice>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="group" type="xs:string" use="required"/>
+ <!--
+ For all known quests, exactly one of these is provided, with a single
+ integer value. However, manaplus actually parses both, and as a
+ comma-separated list. (xsd spec says they should be space-separated).
+ -->
+ <!-- TODO fix this horrible abuse of XML-->
+ <xs:attribute name="complete" type="xs:string" use="optional"/>
+ <xs:attribute name="incomplete" type="xs:string" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="quests_quest_coordinates">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="x" type="xs:int" use="required"/>
+ <xs:attribute name="y" type="xs:int" use="required"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ <xs:complexType name="quests_quest_text">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="text1" type="xs:string" use="optional"/>
+ <xs:attribute name="text2" type="xs:string" use="optional"/>
+ <xs:attribute name="text3" type="xs:string" use="optional"/>
+ <xs:attribute name="text4" type="xs:string" use="optional"/>
+ <xs:attribute name="text5" type="xs:string" use="optional"/>
+ <xs:attribute name="text6" type="xs:string" use="optional"/>
+ <xs:attribute name="text7" type="xs:string" use="optional"/>
+ <xs:attribute name="text8" type="xs:string" use="optional"/>
+ <xs:attribute name="text9" type="xs:string" use="optional"/>
+ <xs:attribute name="text10" type="xs:string" use="optional"/>
+ <xs:attribute name="text11" type="xs:string" use="optional"/>
+ <xs:attribute name="text12" type="xs:string" use="optional"/>
+ <xs:attribute name="text13" type="xs:string" use="optional"/>
+ <xs:attribute name="text14" type="xs:string" use="optional"/>
+ <xs:attribute name="text15" type="xs:string" use="optional"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ <!--
+ QuestsWindow::loadEffect
+ -->
+ <xs:complexType name="quests_var_effect">
+ <xs:attribute name="map" type="xs:string" use="required"/>
+ <xs:attribute name="npc" type="xs:int" use="required"/>
+ <xs:attribute name="effect" type="xs:int" use="required"/>
+ <!-- Really a comma-separated list, but xsd wants space-separated -->
+ <!-- TODO fix this horrible abuse of XML-->
+ <xs:attribute name="value" type="xs:string" use="required"/>
+ </xs:complexType>
+ <!--
+ SkillDialog::loadXmlFile
+ skillsFile or skillsFile2: skills.xml or ea-skills.xml (but these were backwards before v1.4.1.18)
+ skillsPatchFile: skills_patch.xml
+ skillsPatchDir: skills.d/*.xml
+ -->
+ <xs:simpleType name="skillType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="list"/>
+ <xs:enumeration value="vertical"/>
+ <xs:enumeration value="rectangle"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:element name="skills" type="root_skills"/>
+ <xs:complexType name="root_skills">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="set">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="skill">
+ <xs:complexType>
+ <!-- By code, sometimes 'id' is 'var', but I don't see it in the wild. -->
+ <xs:attribute name="id" type="xs:int" default="-1"/>
+ <xs:attribute name="var" type="xs:int" default="-1"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="useButton" type="xs:string" default="Use"/>
+ <xs:attribute name="owner" type="xs:string" use="optional" default="player"/>
+ <xs:attribute name="errorText" type="xs:string" use="optional"/>
+ <xs:attribute name="alwaysVisible" type="bool" use="optional" default="false"/>
+ <xs:attribute name="castingSrcEffectId" type="xs:string" use="optional"/>
+ <xs:attribute name="castingDstEffectId" type="xs:string" use="optional"/>
+ <xs:attribute name="castingGroundEffectId" type="xs:string" use="optional"/>
+ <xs:attribute name="castingAction" type="xs:string" use="optional"/>
+ <xs:attribute name="castingRideAction" type="xs:string" use="optional"/>
+ <xs:attribute name="castingSkyAction" type="xs:string" use="optional"/>
+ <xs:attribute name="castingWaterAction" type="xs:string" use="optional"/>
+ <xs:attribute name="castingAnimation" type="xs:string" use="optional"/>
+ <xs:attribute name="castingMissile-particle" type="xs:string" use="optional"/>
+ <xs:attribute name="castingMissile-z" type="xs:float" use="optional"/>
+ <xs:attribute name="castingMissile-lifetime" type="xs:int" use="optional"/>
+ <xs:attribute name="castingMissile-speed" type="xs:float" use="optional"/>
+ <xs:attribute name="castingMissile-diedistance" type="xs:float" use="optional"/>
+ <xs:attribute name="missile-particle" type="xs:string" use="optional"/>
+ <xs:attribute name="missile-z" type="xs:float" use="optional"/>
+ <xs:attribute name="missile-lifetime" type="xs:int" use="optional"/>
+ <xs:attribute name="missile-speed" type="xs:float" use="optional"/>
+ <xs:attribute name="missile-diedistance" type="xs:float" use="optional"/>
+ <xs:attribute name="useTextParameter" type="bool" default="false"/>
+ <xs:attribute name="icon" type="xs:string" use="optional"/>
+ <xs:attribute name="level" type="xs:int" default="0"/>
+ <xs:attribute name="shortName" type="xs:string" use="optional"/>
+ <xs:attribute name="description" type="xs:string" use="optional"/>
+ <xs:attribute name="soundHit" type="xs:string" use="optional"/>
+ <xs:attribute name="soundHitDelay" type="xs:int" use="optional"/>
+ <xs:attribute name="soundMiss" type="xs:string" use="optional"/>
+ <xs:attribute name="soundMissDelay" type="xs:int" use="optional"/>
+ <xs:attribute name="invokeCmd" type="xs:string" use="optional"/>
+ <xs:attribute name="levelUpEffectId" type="xs:int" use="optional"/>
+ <xs:attribute name="removeEffectId" type="xs:int" use="optional"/>
+ <xs:attribute name="hitEffectId" type="xs:int" use="optional"/>
+ <xs:attribute name="missEffectId" type="xs:int" use="optional"/>
+ <xs:attribute name="srcEffectId" type="xs:string" use="optional"/>
+ <xs:attribute name="dstEffectId" type="xs:string" use="optional"/>
+ <xs:attribute name="x" type="xs:int" use="optional"/>
+ <xs:attribute name="y" type="xs:int" use="optional"/>
+ <xs:attribute name="autoTab" type="bool" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="type" type="skillType" default="vertical"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!--
+ SkillUnitDb::load
+ -->
+ <xs:element name="skillunits" type="root_skillunits"/>
+ <xs:complexType name="root_skillunits">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="skillunit">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="particlefx" type="xs:string"/>
+ <xs:element name="sprite">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="variant" type="xs:int" default="0"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="name" type="xs:string" default="skill"/>
+ <xs:attribute name="targetSelection" type="bool" default="true"/>
+ <xs:attributeGroup ref="being_basic_attributes"/>
+ <xs:attribute name="deadSortOffsetY" type="xs:int" default="31"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!--
+ EquipmentWindow::fillBoxes
+ equipmentWindowFile: equipmentwindow.xml
+ -->
+ <xs:element name="equipment" type="root_equipment"/>
+ <xs:complexType name="root_equipment">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="page" type="equipment_page"/>
+ </xs:choice>
+ </xs:sequence>
+ <xs:attribute name="image" type="xs:string" default="equipmentbox.png"/>
+ </xs:complexType>
+
+ <!--
+ EquipmentWindow::loadPage
+ -->
+ <xs:complexType name="equipment_page">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="playerbox" type="equipment_playerbox"/>
+ <xs:element name="slot" type="equipment_slot"/>
+ </xs:choice>
+ <xs:attribute name="name" type="xs:string"/>
+ </xs:complexType>
+
+ <!--
+ EquipmentWindow::loadPlayerBox
+ -->
+ <xs:complexType name="equipment_playerbox">
+ <xs:attribute name="x" type="xs:int" use="required"/>
+ <xs:attribute name="y" type="xs:int" use="required"/>
+ <xs:attribute name="width" type="xs:int" use="required"/>
+ <xs:attribute name="height" type="xs:int" use="required"/>
+ </xs:complexType>
+ <!--
+ EquipmentWindow::loadSlot
+ -->
+ <xs:complexType name="equipment_slot">
+ <xs:attribute name="name" type="equipment_slot_name" use="required"/>
+ <xs:attribute name="x" type="xs:int" use="required"/>
+ <xs:attribute name="y" type="xs:int" use="required"/>
+ <xs:attribute name="image" type="xs:int" default="0"/>
+ </xs:complexType>
+ <xs:simpleType name="equipment_slot_name">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="topclothes"/>
+ <xs:enumeration value="top"/>
+ <xs:enumeration value="torso"/>
+ <xs:enumeration value="body"/>
+ <xs:enumeration value="slot0"/>
+ <xs:enumeration value="glove"/>
+ <xs:enumeration value="gloves"/>
+ <xs:enumeration value="slot1"/>
+ <xs:enumeration value="hat"/>
+ <xs:enumeration value="hats"/>
+ <xs:enumeration value="slot2"/>
+ <xs:enumeration value="bottomclothes"/>
+ <xs:enumeration value="bottom"/>
+ <xs:enumeration value="pants"/>
+ <xs:enumeration value="slot3"/>
+ <xs:enumeration value="shoes"/>
+ <xs:enumeration value="boot"/>
+ <xs:enumeration value="boots"/>
+ <xs:enumeration value="slot4"/>
+ <xs:enumeration value="misc1"/>
+ <xs:enumeration value="cape"/>
+ <xs:enumeration value="slot5"/>
+ <xs:enumeration value="wings"/>
+ <xs:enumeration value="slot6"/>
+ <xs:enumeration value="misc2"/>
+ <xs:enumeration value="scarf"/>
+ <xs:enumeration value="scarfs"/>
+ <xs:enumeration value="slot7"/>
+ <xs:enumeration value="weapon"/>
+ <xs:enumeration value="weapons"/>
+ <xs:enumeration value="slot8"/>
+ <xs:enumeration value="shield"/>
+ <xs:enumeration value="shields"/>
+ <xs:enumeration value="slot9"/>
+ <xs:enumeration value="arrow"/>
+ <xs:enumeration value="arrows"/>
+ <xs:enumeration value="ammo"/>
+ <xs:enumeration value="slot10"/>
+ <xs:enumeration value="amulet"/>
+ <xs:enumeration value="amulets"/>
+ <xs:enumeration value="slot11"/>
+ <xs:enumeration value="ring"/>
+ <xs:enumeration value="rings"/>
+ <xs:enumeration value="slot12"/>
+ <xs:enumeration value="slot13"/>
+ <xs:enumeration value="slot14"/>
+ <xs:enumeration value="slot15"/>
+ <xs:enumeration value="slot16"/>
+ <xs:enumeration value="slot17"/>
+ <xs:enumeration value="slot18"/>
+ <xs:enumeration value="slot19"/>
+ <xs:enumeration value="slot20"/>
+ <xs:enumeration value="slot21"/>
+ <xs:enumeration value="slot22"/>
+ <xs:enumeration value="slot23"/>
+ <xs:enumeration value="slot24"/>
+ <xs:enumeration value="slot25"/>
+ <xs:enumeration value="slot26"/>
+ <xs:enumeration value=""/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!--
+ Units::loadXMLFile
+ unitsFile: units.xml
+ unitsPatchFile: units_patch.xml
+ unitsPatchDir: units.d/*.xml
+ -->
+ <xs:element name="units" type="root_units"/>
+ <xs:complexType name="root_units">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="unit">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="level">
+ <xs:complexType>
+ <xs:attribute name="symbol" type="xs:string" use="required"/>
+ <xs:attribute name="count" type="xs:int" use="required"/>
+ <xs:attribute name="round" type="xs:int" use="optional"/>
+ <xs:attribute name="separator" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ <xs:attribute name="type" type="units_unit_type" use="required"/>
+ <xs:attribute name="conversion" type="xs:int" default="1"/>
+ <xs:attribute name="mix" type="yesno" default="no"/>
+ <xs:attribute name="base" type="xs:string" default="¤"/>
+ <xs:attribute name="round" type="xs:int" default="2"/>
+ <xs:attribute name="separator" type="xs:string" default=" "/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="currency">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="unit">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="level">
+ <xs:complexType>
+ <xs:attribute name="symbol" type="xs:string" use="required"/>
+ <xs:attribute name="count" type="xs:int" use="required"/>
+ <xs:attribute name="round" type="xs:int" use="optional"/>
+ <xs:attribute name="separator" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="conversion" type="xs:int" default="1"/>
+ <xs:attribute name="mix" type="yesno" default="no"/>
+ <xs:attribute name="base" type="xs:string" default="¤"/>
+ <xs:attribute name="round" type="xs:int" default="2"/>
+ <xs:attribute name="separator" type="xs:string" default=" "/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="units_unit_type">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="currency"/>
+ <xs:enumeration value="weight"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="yesno">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="true"/>
+ <xs:enumeration value="yes"/>
+ <xs:enumeration value="no"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!--
+ StatusEffect::loadXmlFile
+ statusEffectsFile: status-effects.xml
+ statusEffectsPatchFile: status-effects_patch.xml
+ statusEffectsPatchDir: status-effects.d/*.xml
+ -->
+ <xs:element name="status-effects" type="root_status-effects"/>
+ <xs:complexType name="root_status-effects">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="status-effect">
+ <xs:complexType>
+ <!-- legacy attribute only for old clients -->
+ <xs:attribute name="block-id" type="xs:int" use="optional"/>
+ <xs:attributeGroup ref="status_or_stun_attrs"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:attributeGroup name="status_or_stun_attrs">
+ <xs:attribute name="id" type="xs:int" use="optional"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="start-message" type="xs:string" use="optional"/>
+ <xs:attribute name="start-audio" type="xs:string" use="optional"/>
+ <xs:attribute name="start-particle" type="xs:string" use="optional"/>
+ <xs:attribute name="icon" type="xs:string" use="optional"/>
+ <xs:attribute name="action" type="xs:string" use="optional"/>
+ <xs:attribute name="persistent-particle-effect" type="yesno" use="optional"/>
+ <xs:attribute name="end-message" type="xs:string" use="optional"/>
+ <xs:attribute name="end-audio" type="xs:string" use="optional"/>
+ <xs:attribute name="end-particle" type="xs:string" use="optional"/>
+ <xs:attribute name="option" type="xs:int" use="optional"/>
+ <xs:attribute name="opt1" type="xs:int" use="optional"/>
+ <xs:attribute name="opt2" type="xs:int" use="optional"/>
+ <xs:attribute name="opt3" type="xs:int" use="optional"/>
+ </xs:attributeGroup>
+ <!--
+ maps and tilesets omitted, they have a DTD from tiled
+ -->
+ <!--
+ EmoteDB::loadXmlFile
+ emotesFile: emotes.xml
+ emotesPatchFile: emotes.xml
+ emotesPatchDir: emotes.d/*.xml
+ EmoteDB::loadSpecialXmlFile
+ manaplus_emotes.xml
+ -->
+ <xs:element name="emotes" type="root_emotes"/>
+ <xs:complexType name="root_emotes">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="emote">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="sprite">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="variant" type="xs:int" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="particlefx" type="xs:string"/>
+ </xs:choice>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <!-- altid is only in manaplus_emotes.xml -->
+ <xs:attribute name="altid" type="xs:int" use="optional"/>
+ <xs:attribute name="time" type="xs:int" default="500"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <!--
+ NPCDB::loadXmlFile
+ npcsFile: npcs.xml
+ npcsPatchFile: npcs.xml
+ npcsPatchDir: npcs.d/*.xml
+ -->
+ <xs:element name="npcs" type="root_npcs"/>
+ <xs:complexType name="root_npcs">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="npc">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="sprite">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="variant" type="xs:int" default="0"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="particlefx" type="xs:string"/>
+ <xs:element name="menu">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="command" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="targetSelection" type="bool" default="true"/>
+ <xs:attributeGroup ref="being_basic_attributes"/>
+ <xs:attribute name="deadSortOffsetY" type="xs:int" default="31"/>
+ <xs:attribute name="avatar" type="xs:unsignedShort" default="0"/>
+ <xs:attribute name="allowDelete" type="bool" default="true"/>
+ <xs:attribute name="currency" type="xs:string" default=""/>
+ <xs:attribute name="allowEquipment" type="bool" default="false"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:attributeGroup name="being_basic_attributes">
+ <xs:attribute name="targetCursor" type="cursor" default="medium"/>
+ <!--
+ default for hoverCursor varies:
+ npc: "talk"
+ monster: "attack"
+ pet: "talk"
+ -->
+ <xs:attribute name="hoverCursor" type="xs:string" use="optional"/>
+ <xs:attribute name="targetOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="targetOffsetY" type="xs:int" default="0"/>
+ <xs:attribute name="nameOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="nameOffsetY" type="xs:int" default="0"/>
+ <xs:attribute name="sortOffsetY" type="xs:int" default="0"/>
+ <xs:attribute name="hpBarOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="hpBarOffsetY" type="xs:int" default="0"/>
+ <xs:attribute name="quickActionEffect" type="xs:int" default="-1"/>
+ <xs:attribute name="walkType" type="xs:string" default="walk"/>
+ </xs:attributeGroup>
+ <xs:simpleType name="cursor">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="small"/>
+ <xs:enumeration value="medium"/>
+ <xs:enumeration value="large"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!--
+ ItemDB::loadXmlFile
+ itemsFile: items.xml
+ itemsPatchFile: items_patch.xml
+ itemsPatchDir: items.d/*.xml
+ -->
+ <!-- not all item fields added -->
+ <xs:element name="items" type="root_items"/>
+ <xs:complexType name="root_items">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="item">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="sprite" type="items_item_sprite"/>
+ <xs:element name="particlefx" type="xs:string"/>
+ <xs:element name="sound" type="items_item_sound"/>
+ <xs:element name="floor" type="items_item_floor"/>
+ <xs:element name="replace" type="items_item_replace"/>
+ <xs:element name="drawAfter" type="items_item_draworder"/>
+ <xs:element name="drawBefore" type="items_item_draworder"/>
+ <xs:element name="inventory" type="items_item_menu"/>
+ <xs:element name="storage" type="items_item_menu"/>
+ <xs:element name="cart" type="items_item_menu"/>
+ </xs:choice>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="type" type="items_item_type" default="other"/>
+ <xs:attribute name="weight" type="xs:int" default="0"/>
+ <xs:attribute name="view" type="xs:int" default="0"/>
+ <xs:attribute name="name" type="xs:string" default="unnamed"/>
+ <xs:attribute name="image" type="xs:string" use="optional"/>
+ <xs:attribute name="floor" type="xs:string" use="optional"/>
+ <xs:attribute name="description" type="xs:string" use="optional"/>
+ <xs:attribute name="attack-action" type="xs:string" use="optional"/>
+ <xs:attribute name="skyattack-action" type="xs:string" use="optional"/>
+ <xs:attribute name="waterattack-action" type="xs:string" use="optional"/>
+ <xs:attribute name="rideattack-action" type="xs:string" use="optional"/>
+ <xs:attribute name="drawBefore" type="xs:string" use="optional"/>
+ <xs:attribute name="drawAfter" type="xs:string" use="optional"/>
+ <xs:attribute name="maxFloorOffset" type="xs:int" default="0"/>
+ <xs:attribute name="useButton" type="xs:string" use="optional"/>
+ <xs:attribute name="useButton2" type="xs:string" use="optional"/>
+ <xs:attribute name="colors" type="xs:string" use="optional"/>
+ <xs:attribute name="iconColors" type="xs:string" use="optional"/>
+ <xs:attribute name="cardColor" type="xs:int" use="optional"/>
+ <!-- Can't use both tag and tag1; tag has precedence. -->
+ <xs:attribute name="tag" type="xs:string" use="optional"/>
+ <xs:attribute name="tag1" type="xs:string" use="optional"/>
+ <xs:attribute name="tag2" type="xs:string" use="optional"/>
+ <xs:attribute name="tag3" type="xs:string" use="optional"/>
+ <xs:attribute name="drawPriority" type="xs:int" default="0"/>
+ <xs:attribute name="attack-range" type="xs:int" default="0"/>
+ <xs:attribute name="missile-particle" type="xs:string" use="optional"/>
+ <xs:attribute name="missile-z" type="xs:float" use="optional"/>
+ <xs:attribute name="missile-lifetime" type="xs:int" use="optional"/>
+ <xs:attribute name="missile-speed" type="xs:float" use="optional"/>
+ <xs:attribute name="missile-diedistance" type="xs:float" use="optional"/>
+ <xs:attribute name="hit-effect-id" type="xs:string" use="optional"/>
+ <xs:attribute name="critical-hit-effect-id" type="xs:string" use="optional"/>
+ <xs:attribute name="miss-effect-id" type="xs:string" use="optional"/>
+ <xs:attribute name="sellProtected" type="bool" default="false"/>
+ <xs:attribute name="pickupCursor" type="xs:string" default="pickup"/>
+ <xs:attribute name="attack" type="xs:string" use="optional"/>
+ <xs:attribute name="criticalattack" type="xs:string" use="optional"/>
+ <xs:attribute name="criticalattackrate" type="xs:string" use="optional"/>
+ <xs:attribute name="atkspeed" type="xs:string" use="optional"/>
+ <xs:attribute name="mattack" type="xs:string" use="optional"/>
+ <xs:attribute name="block" type="xs:string" use="optional"/>
+ <xs:attribute name="defense" type="xs:string" use="optional"/>
+ <xs:attribute name="criticaldefense" type="xs:string" use="optional"/>
+ <xs:attribute name="mdefense" type="xs:string" use="optional"/>
+ <xs:attribute name="hp" type="xs:string" use="optional"/>
+ <xs:attribute name="mp" type="xs:string" use="optional"/>
+ <xs:attribute name="maxhp" type="xs:string" use="optional"/>
+ <xs:attribute name="maxmp" type="xs:string" use="optional"/>
+ <xs:attribute name="maxweight" type="xs:string" use="optional"/>
+ <xs:attribute name="hprecovery" type="xs:string" use="optional"/>
+ <xs:attribute name="sprecovery" type="xs:string" use="optional"/>
+ <xs:attribute name="money" type="xs:string" use="optional"/>
+ <xs:attribute name="exp" type="xs:string" use="optional"/>
+ <xs:attribute name="hit" type="xs:string" use="optional"/>
+ <xs:attribute name="level" type="xs:string" use="optional"/>
+ <xs:attribute name="speed" type="xs:string" use="optional"/>
+ <xs:attribute name="range" type="xs:string" use="optional"/>
+ <xs:attribute name="flee" type="xs:string" use="optional"/>
+ <xs:attribute name="str" type="xs:string" use="optional"/>
+ <xs:attribute name="agi" type="xs:string" use="optional"/>
+ <xs:attribute name="vit" type="xs:string" use="optional"/>
+ <xs:attribute name="int" type="xs:string" use="optional"/>
+ <xs:attribute name="dex" type="xs:string" use="optional"/>
+ <xs:attribute name="luk" type="xs:string" use="optional"/>
+ <xs:attribute name="effect" type="xs:string" use="optional"/>
+ <xs:attribute name="penalty" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="items_item_type">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="generic"/>
+ <xs:enumeration value="other"/>
+ <xs:enumeration value="usable"/>
+ <xs:enumeration value="equip-1hand"/>
+ <xs:enumeration value="equip-2hand"/>
+ <xs:enumeration value="equip-torso"/>
+ <xs:enumeration value="equip-arms"/>
+ <xs:enumeration value="equip-head"/>
+ <xs:enumeration value="equip-legs"/>
+ <xs:enumeration value="equip-shield"/>
+ <xs:enumeration value="equip-ring"/>
+ <xs:enumeration value="equip-charm"/>
+ <xs:enumeration value="equip-necklace"/>
+ <xs:enumeration value="equip-neck"/>
+ <xs:enumeration value="equip-feet"/>
+ <xs:enumeration value="equip-ammo"/>
+ <xs:enumeration value="card"/>
+ <xs:enumeration value="racesprite"/>
+ <xs:enumeration value="hairsprite"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="items_item_sprite">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="variant" type="xs:int" default="0"/>
+ <xs:attribute name="race" type="xs:int" default="0"/>
+ <xs:attribute name="gender" default="unisex">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="unisex"/>
+ <xs:enumeration value="male"/>
+ <xs:enumeration value="female"/>
+ <xs:enumeration value="other"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ <xs:complexType name="items_item_sound">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="event" use="required">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="hit"/>
+ <xs:enumeration value="strike"/>
+ <xs:enumeration value="miss"/>
+ <xs:enumeration value="use"/>
+ <xs:enumeration value="equip"/>
+ <xs:enumeration value="unequip"/>
+ <xs:enumeration value="drop"/>
+ <xs:enumeration value="pickup"/>
+ <xs:enumeration value="take"/>
+ <xs:enumeration value="put"/>
+ <xs:enumeration value="usecard"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="delay" type="xs:int" default="0"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ <xs:complexType name="items_item_floor">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="sprite">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="variant" type="xs:int" default="0"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="particlefx" type="xs:string"/>
+ </xs:choice>
+ </xs:complexType>
+ <xs:complexType name="items_item_replace">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="item">
+ <xs:complexType>
+ <xs:attribute name="from" type="xs:int" use="optional"/>
+ <xs:attribute name="to" type="xs:int" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ <xs:attribute name="sprite" type="items_item_x_sprite" use="optional"/>
+ <xs:attribute name="direction" type="items_item_x_direction" default="all"/>
+ </xs:complexType>
+ <xs:complexType name="items_item_draworder">
+ <xs:attribute name="name" type="items_item_x_sprite" use="required"/>
+ <xs:attribute name="priority" type="xs:int" use="optional"/>
+ <xs:attribute name="direction" type="items_item_x_direction" default="all"/>
+ </xs:complexType>
+ <xs:simpleType name="items_item_x_sprite">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="race"/>
+ <xs:enumeration value="type"/>
+ <xs:enumeration value="shoes"/>
+ <xs:enumeration value="boot"/>
+ <xs:enumeration value="boots"/>
+ <xs:enumeration value="bottomclothes"/>
+ <xs:enumeration value="bottom"/>
+ <xs:enumeration value="pants"/>
+ <xs:enumeration value="topclothes"/>
+ <xs:enumeration value="top"/>
+ <xs:enumeration value="torso"/>
+ <xs:enumeration value="body"/>
+ <xs:enumeration value="misc1"/>
+ <xs:enumeration value="misc2"/>
+ <xs:enumeration value="scarf"/>
+ <xs:enumeration value="scarfs"/>
+ <xs:enumeration value="hair"/>
+ <xs:enumeration value="hat"/>
+ <xs:enumeration value="hats"/>
+ <xs:enumeration value="wings"/>
+ <xs:enumeration value="glove"/>
+ <xs:enumeration value="gloves"/>
+ <xs:enumeration value="weapon"/>
+ <xs:enumeration value="weapons"/>
+ <xs:enumeration value="shield"/>
+ <xs:enumeration value="shields"/>
+ <xs:enumeration value="amulet"/>
+ <xs:enumeration value="amulets"/>
+ <xs:enumeration value="ring"/>
+ <xs:enumeration value="rings"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="items_item_x_direction">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="down"/>
+ <xs:enumeration value="downleft"/>
+ <xs:enumeration value="leftdown"/>
+ <xs:enumeration value="left"/>
+ <xs:enumeration value="upleft"/>
+ <xs:enumeration value="leftup"/>
+ <xs:enumeration value="up"/>
+ <xs:enumeration value="upright"/>
+ <xs:enumeration value="rightup"/>
+ <xs:enumeration value="right"/>
+ <xs:enumeration value="downright"/>
+ <xs:enumeration value="rightdown"/>
+ <xs:enumeration value="downall"/>
+ <xs:enumeration value="upall"/>
+ <xs:enumeration value="died"/>
+ <!-- handled by implicit else -->
+ <xs:enumeration value="all"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="items_item_menu">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="menu">
+ <xs:complexType>
+ <xs:attribute name="name1" type="xs:string" use="optional"/>
+ <xs:attribute name="name2" type="xs:string" use="optional"/>
+ <xs:attribute name="command1" type="xs:string" use="optional"/>
+ <xs:attribute name="command2" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:complexType>
+ <!--
+ SoundDB::loadXmlFile
+ soundsFile: sounds.xml
+ soundsPatchFile: sounds_patch.xml
+ soundsPatchDir: sounds.d/*.xml
+ -->
+ <xs:element name="sounds" type="root_sounds"/>
+ <xs:complexType name="root_sounds">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="sound">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="value" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:element name="languages" type="root_languages"/>
+ <xs:complexType name="root_languages">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="language">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="icon" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:element name="texts" type="root_texts"/>
+ <xs:complexType name="root_texts">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="text">
+ <xs:complexType>
+ <xs:attribute name="show" type="bool" default="false"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:element name="itemoptions" type="root_itemoptions"/>
+ <xs:complexType name="root_itemoptions">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="option">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="field" type="xs:string" use="optional"/>
+ <xs:attribute name="field0" type="xs:string" use="optional"/>
+ <xs:attribute name="field1" type="xs:string" use="optional"/>
+ <xs:attribute name="field2" type="xs:string" use="optional"/>
+ <xs:attribute name="field3" type="xs:string" use="optional"/>
+ <xs:attribute name="field4" type="xs:string" use="optional"/>
+ <xs:attribute name="field5" type="xs:string" use="optional"/>
+ <xs:attribute name="field6" type="xs:string" use="optional"/>
+ <xs:attribute name="field7" type="xs:string" use="optional"/>
+ <xs:attribute name="field8" type="xs:string" use="optional"/>
+ <xs:attribute name="field9" type="xs:string" use="optional"/>
+ <xs:attribute name="field10" type="xs:string" use="optional"/>
+ <xs:attribute name="field11" type="xs:string" use="optional"/>
+ <xs:attribute name="field12" type="xs:string" use="optional"/>
+ <xs:attribute name="field13" type="xs:string" use="optional"/>
+ <xs:attribute name="field14" type="xs:string" use="optional"/>
+ <xs:attribute name="field15" type="xs:string" use="optional"/>
+ <xs:attribute name="field16" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!--
+ ColorDB::loadHair
+ hairColorFile: hair.xml
+ hairColorPatchFile: hair.xml
+ hairColorPatchDir: hair.d/*.xml
+ ColorDB::loadColorLists
+ itemColorFile: itemcolors.xml
+ itemColorPatchFile: itemcolors_patch.xml
+ itemColorPatchDir: itemcolors.d/*.xml
+ -->
+ <xs:element name="colors" type="root_colors"/>
+ <xs:complexType name="root_colors">
+ <xs:sequence>
+ <xs:choice>
+ <!-- hair.xml -->
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="color" type="colors_color"/>
+ </xs:choice>
+ <!-- itemcolors.xml -->
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="list">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="color" type="colors_color"/>
+ </xs:choice>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="colors_color">
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="value" type="xs:string" use="required"/>
+ </xs:complexType>
+ <!--
+ MapDB::loadInfo
+ mapsFile: maps.xml
+ mapsPatchFile: maps_patch.xml
+ mapsPatchDir: maps.d/*.xml
+ -->
+ <xs:element name="maps" type="root_maps"/>
+ <xs:complexType name="root_maps">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="map" type="maps_map"/>
+ <xs:element name="atlas" type="maps_atlas"/>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <!-- MapDB::readMap -->
+ <xs:complexType name="maps_map">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="atlas">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ <!-- MapDB::readMap -->
+ <xs:complexType name="maps_atlas">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="file">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ <!--
+ MapDB::loadRemap
+ mapsRemapFile: maps/remap.xml
+ -->
+ <xs:element name="remap" type="root_remap"/>
+ <xs:complexType name="root_remap">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <!-- TODO <xs:element ref="include"/> -->
+ <xs:element name="map">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="value" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <!--
+ DeadDB::loadXmlFile
+ deadMessagesFile: deadmessages.xml
+ deadMessagesPatchFile: deadmessages_patch.xml
+ deadMessagesPatchDir: deadmessages.d/*.xml
+ -->
+ <xs:element name="messages" type="root_messages"/>
+ <xs:complexType name="root_messages">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="message" type="xs:string"/>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <!--
+ AvatarDB::loadXmlFile
+ avatarsFile: avatars.xml
+ avatarsPatchFile: avatars_patch.xml
+ avatarsPatchDir: avatars.d/*.xml
+ -->
+ <xs:element name="avatars" type="root_avatars"/>
+ <xs:complexType name="root_avatars">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="avatar">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="sprite">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="variant" type="xs:int" default="0"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="targetOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="targetOffsetY" type="xs:int" default="0"/>
+ <xs:attribute name="width" type="xs:int" default="0"/>
+ <xs:attribute name="height" type="xs:int" default="0"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <!--
+ MonsterDB::loadXmlFile
+ monstersFile: monsters.xml
+ monstersPatchFile: monsters_patch.xml
+ monstersPatchDir: monsters.d/*.xml
+ -->
+ <xs:element name="monsters" type="root_monsters"/>
+ <xs:complexType name="root_monsters">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="monster">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="sprite">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="variant" type="xs:int" default="0"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="sound">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="event" type="monster_sound_event" use="required"/>
+ <xs:attribute name="delay" type="xs:int" default="0"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="attack">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="effect-id" type="xs:int" use="optional"/>
+ <xs:attribute name="hit-effect-id" type="xs:int" use="optional"/>
+ <xs:attribute name="critical-hit-effect-id" type="xs:int" use="optional"/>
+ <xs:attribute name="miss-effect-id" type="xs:int" use="optional"/>
+ <xs:attribute name="action" type="xs:string" default="attack"/>
+ <xs:attribute name="skyaction" type="xs:string" default="skyattack"/>
+ <xs:attribute name="wateraction" type="xs:string" default="waterattack"/>
+ <xs:attribute name="rideaction" type="xs:string" default="waterattack"/>
+ <xs:attribute name="missile-particle" type="xs:string" use="optional"/>
+ <xs:attribute name="missile-z" type="xs:float" use="optional"/>
+ <xs:attribute name="missile-lifetime" type="xs:int" use="optional"/>
+ <xs:attribute name="missile-speed" type="xs:float" use="optional"/>
+ <xs:attribute name="missile-diedistance" type="xs:float" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="particlefx" type="xs:string"/>
+ </xs:choice>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attributeGroup ref="being_basic_attributes"/>
+<!-- disable attributes for outdated servers
+ <xs:attribute name="maxHP" type="xs:int" use="optional"/> -->
+ <xs:attribute name="deadSortOffsetY" type="xs:int" default="31"/>
+ <xs:attribute name="colors" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ <!-- really optional with default="1002", but that's not sane -->
+ <xs:attribute name="offset" type="xs:int" default="1002"/>
+ </xs:complexType>
+ <xs:simpleType name="monster_sound_event">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="hit"/>
+ <xs:enumeration value="miss"/>
+ <xs:enumeration value="hurt"/>
+ <xs:enumeration value="die"/>
+ <xs:enumeration value="move"/>
+ <xs:enumeration value="sit"/>
+ <xs:enumeration value="sittop"/>
+ <xs:enumeration value="spawn"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!--
+ NpcDialogDB::load
+ -->
+ <xs:element name="dialogs" type="root_npc_dialogs"/>
+ <xs:complexType name="root_npc_dialogs">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="dialog" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="menu" type="npc_dialogs_menu"/>
+ <xs:element name="inventory" type="npc_dialogs_inventory"/>
+ </xs:choice>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="hideText" type="bool"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element ref="include"/>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="npc_dialogs_menu">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="button">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" default=""/>
+ <xs:attribute name="value" type="xs:string" use="required"/>
+ <xs:attribute name="x" type="xs:int" default="0"/>
+ <xs:attribute name="y" type="xs:int" default="0"/>
+ <xs:attribute name="image" type="xs:string" default=""/>
+ <xs:attribute name="imageWidth" type="xs:int" default="16"/>
+ <xs:attribute name="imageHeight" type="xs:int" default="16"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="image">
+ <xs:complexType>
+ <xs:attribute name="image" type="xs:string" use="required"/>
+ <xs:attribute name="x" type="xs:int" default="0"/>
+ <xs:attribute name="y" type="xs:int" default="0"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="text">
+ <xs:complexType>
+ <xs:attribute name="text" type="xs:string" use="required"/>
+ <xs:attribute name="x" type="xs:int" default="0"/>
+ <xs:attribute name="y" type="xs:int" default="0"/>
+ <xs:attribute name="width" type="xs:int" default="20"/>
+ <xs:attribute name="height" type="xs:int" default="20"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="npc_dialogs_inventory">
+ <xs:attribute name="cell" type="xs:string" default=""/>
+ <xs:attribute name="columns" type="xs:int" default="10000"/>
+ </xs:complexType>
+
+ <!--
+ PETDB::loadXmlFile
+ petsFile: pets.xml
+ petsPatchFile: pets_patch.xml
+ petsPatchDir: pets.d/*.xml
+ -->
+ <xs:element name="pets" type="root_pets"/>
+ <xs:complexType name="root_pets">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="pet">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="sprite">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="variant" type="xs:int" default="0"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="particlefx" type="xs:string"/>
+ </xs:choice>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="targetSelection" type="bool" default="true"/>
+ <xs:attributeGroup ref="being_basic_attributes"/>
+ <xs:attribute name="deadSortOffsetY" type="xs:int" default="31"/>
+ <xs:attribute name="removeMessage" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:element name="badges" type="root_badges"/>
+ <xs:complexType name="root_badges">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="guild" type="badges_list" />
+ <xs:element name="party" type="badges_list" />
+ <xs:element name="name" type="badges_list" />
+ <xs:element name="clan" type="badges_list" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="badges_list">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="badge">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="image" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:element name="network" type="root_network"/>
+ <xs:complexType name="root_network">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="inpackets" type="network_inpackets" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="network_inpackets">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="fakepacket">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="len" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="removepacket">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:element name="stats" type="root_stats"/>
+ <xs:complexType name="root_stats">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="basic">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="stat">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:int" use="optional"/>
+ <xs:attribute name="attr" type="xs:string" use="required"/>
+ <xs:attribute name="tag" type="xs:string" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="extended">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="stat">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:int" use="optional"/>
+ <xs:attribute name="attr" type="xs:string" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="page">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="stat">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:int" use="optional"/>
+ <xs:attribute name="attr" type="xs:string" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:element name="horses" type="root_horses"/>
+ <xs:complexType name="horse_offset">
+ <xs:attribute name="direction" type="xs:string" use="required"/>
+ <xs:attribute name="horseOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="horseOffsetY" type="xs:int" default="0"/>
+ </xs:complexType>
+ <xs:complexType name="horse_sprite">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="variant" type="xs:int" default="0"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ <xs:complexType name="root_horses">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="horse" type="horse_node" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="horse_node">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="offset">
+ <xs:complexType>
+ <xs:attribute name="direction" type="xs:string" use="required"/>
+ <xs:attribute name="riderOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="riderOffsetY" type="xs:int" default="0"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ <xs:element name="up" minOccurs="0">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="sprite" type="horse_sprite" />
+ <xs:element name="offset" type="horse_offset" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="down" minOccurs="0">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="sprite" type="horse_sprite" />
+ <xs:element name="offset" type="horse_offset" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ </xs:complexType>
+
+ <xs:element name="itemfields" type="root_itemfields"/>
+ <xs:complexType name="root_itemfields">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:sequence>
+ <xs:element name="required" minOccurs="1" maxOccurs="1">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="field" type="itemfield_node" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="add" minOccurs="1" maxOccurs="1">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="field" type="itemfield_node" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="itemfield_node">
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="description" type="xs:string" use="required"/>
+ <xs:attribute name="signed" type="bool" default="true"/>
+ </xs:complexType>
+
+ <xs:element name="equipmentslots" type="root_equipmentslots"/>
+ <xs:complexType name="root_equipmentslots">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="slot">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="slot" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:element name="groups" type="root_groups"/>
+ <xs:complexType name="root_groups">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="group">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="commands" type="groups_commands" />
+ <xs:element name="permissions" type="groups_permissions" />
+ </xs:choice>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="name" type="xs:string" default=""/>
+ <xs:attribute name="longName" type="xs:string" default=""/>
+ <xs:attribute name="showBadge" type="bool" default="false"/>
+ <xs:attribute name="highlightName" type="bool" default="false"/>
+ <xs:attribute name="badge" type="xs:string" use="optional"/>
+ <xs:attribute name="inherit" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="groups_commands">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="command">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="use" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="groups_permissions">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="permission">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="enable" type="bool" default="true"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- probably not all tags correct here -->
+ <xs:element name="homunculuses" type="root_homunculuses"/>
+ <xs:complexType name="root_homunculuses">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="homunculus">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="sprite">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="variant" type="xs:int" default="0"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="particlefx" type="xs:string"/>
+ </xs:choice>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="targetSelection" type="bool" default="true"/>
+ <xs:attributeGroup ref="being_basic_attributes"/>
+ <xs:attribute name="deadSortOffsetY" type="xs:int" default="31"/>
+ <xs:attribute name="startFollowDistance" type="xs:int" default="3"/>
+ <xs:attribute name="followDistance" type="xs:int" default="0"/>
+ <xs:attribute name="warpDistance" type="xs:int" default="11"/>
+ <xs:attribute name="walkSpeed" type="xs:int" default="0"/>
+ <xs:attribute name="offsetX" type="xs:int" default="0"/>
+ <xs:attribute name="offsetY" type="xs:int" default="1"/>
+ <xs:attribute name="sitOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="sitOffsetY" type="xs:int" default="1"/>
+ <xs:attribute name="moveOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="moveOffsetY" type="xs:int" default="1"/>
+ <xs:attribute name="deadOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="deadOffsetY" type="xs:int" default="1"/>
+ <xs:attribute name="attackOffsetX" type="xs:int" use="optional"/>
+ <xs:attribute name="attackOffsetY" type="xs:int" use="optional"/>
+ <xs:attribute name="thinkTime" type="xs:int" default="500"/>
+ <xs:attribute name="directionType" type="xs:int" default="1"/>
+ <xs:attribute name="sitDirectionType" type="xs:int" default="1"/>
+ <xs:attribute name="deadDirectionType" type="xs:int" default="1"/>
+ <xs:attribute name="attackDirectionType" type="xs:int" default="4"/>
+ <xs:attribute name="removeMessage" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- probably not all tags correct here -->
+ <xs:element name="mercenaries" type="root_mercenaries"/>
+ <xs:complexType name="root_mercenaries">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="mercenary">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="sprite">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="variant" type="xs:int" default="0"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="particlefx" type="xs:string"/>
+ </xs:choice>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="targetSelection" type="bool" default="true"/>
+ <xs:attributeGroup ref="being_basic_attributes"/>
+ <xs:attribute name="deadSortOffsetY" type="xs:int" default="31"/>
+ <xs:attribute name="startFollowDistance" type="xs:int" default="3"/>
+ <xs:attribute name="followDistance" type="xs:int" default="0"/>
+ <xs:attribute name="warpDistance" type="xs:int" default="11"/>
+ <xs:attribute name="walkSpeed" type="xs:int" default="0"/>
+ <xs:attribute name="offsetX" type="xs:int" default="0"/>
+ <xs:attribute name="offsetY" type="xs:int" default="1"/>
+ <xs:attribute name="sitOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="sitOffsetY" type="xs:int" default="1"/>
+ <xs:attribute name="moveOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="moveOffsetY" type="xs:int" default="1"/>
+ <xs:attribute name="deadOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="deadOffsetY" type="xs:int" default="1"/>
+ <xs:attribute name="attackOffsetX" type="xs:int" use="optional"/>
+ <xs:attribute name="attackOffsetY" type="xs:int" use="optional"/>
+ <xs:attribute name="thinkTime" type="xs:int" default="500"/>
+ <xs:attribute name="directionType" type="xs:int" default="1"/>
+ <xs:attribute name="sitDirectionType" type="xs:int" default="1"/>
+ <xs:attribute name="deadDirectionType" type="xs:int" default="1"/>
+ <xs:attribute name="attackDirectionType" type="xs:int" default="4"/>
+ <xs:attribute name="removeMessage" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- probably not all tags correct here -->
+ <xs:element name="elementals" type="root_elementals"/>
+ <xs:complexType name="root_elementals">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="elemental">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="sprite">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="variant" type="xs:int" default="0"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="particlefx" type="xs:string"/>
+ </xs:choice>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="targetSelection" type="bool" default="true"/>
+ <xs:attributeGroup ref="being_basic_attributes"/>
+ <xs:attribute name="deadSortOffsetY" type="xs:int" default="31"/>
+ <xs:attribute name="startFollowDistance" type="xs:int" default="3"/>
+ <xs:attribute name="followDistance" type="xs:int" default="0"/>
+ <xs:attribute name="warpDistance" type="xs:int" default="11"/>
+ <xs:attribute name="walkSpeed" type="xs:int" default="0"/>
+ <xs:attribute name="offsetX" type="xs:int" default="0"/>
+ <xs:attribute name="offsetY" type="xs:int" default="1"/>
+ <xs:attribute name="sitOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="sitOffsetY" type="xs:int" default="1"/>
+ <xs:attribute name="moveOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="moveOffsetY" type="xs:int" default="1"/>
+ <xs:attribute name="deadOffsetX" type="xs:int" default="0"/>
+ <xs:attribute name="deadOffsetY" type="xs:int" default="1"/>
+ <xs:attribute name="attackOffsetX" type="xs:int" use="optional"/>
+ <xs:attribute name="attackOffsetY" type="xs:int" use="optional"/>
+ <xs:attribute name="thinkTime" type="xs:int" default="500"/>
+ <xs:attribute name="directionType" type="xs:int" default="1"/>
+ <xs:attribute name="sitDirectionType" type="xs:int" default="1"/>
+ <xs:attribute name="deadDirectionType" type="xs:int" default="1"/>
+ <xs:attribute name="attackDirectionType" type="xs:int" default="4"/>
+ <xs:attribute name="removeMessage" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- not all item fields added -->
+ <xs:element name="clans" type="root_clans"/>
+ <xs:complexType name="root_clans">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="clan">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="maxhp" type="xs:string" use="optional"/>
+ <xs:attribute name="maxmp" type="xs:string" use="optional"/>
+ <xs:attribute name="str" type="xs:string" use="optional"/>
+ <xs:attribute name="agi" type="xs:string" use="optional"/>
+ <xs:attribute name="vit" type="xs:string" use="optional"/>
+ <xs:attribute name="int" type="xs:string" use="optional"/>
+ <xs:attribute name="dex" type="xs:string" use="optional"/>
+ <xs:attribute name="luck" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!--
+ ModDB::loadXmlFile
+ modsFile: mods.xml
+ modsPatchFile: mods_patch.xml
+ modsPatchDir: mods.d/*.xml
+ -->
+ <xs:element name="mods" type="root_mods"/>
+ <xs:complexType name="root_mods">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="include"/>
+ <xs:element name="mod">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="description" type="xs:string" use="optional"/>
+ <xs:attribute name="help" type="xs:string" use="optional"/>
+ <xs:attribute name="localdir" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <!--
+ CharDB::load
+ charCreationFile: charcreation.xml
+ -->
+ <xs:element name="chars" type="root_chars"/>
+ <xs:complexType name="root_chars">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <!-- TODO <xs:element ref="include"/> -->
+ <xs:element name="haircolor">
+ <xs:complexType>
+ <xs:attribute name="min" type="xs:int" use="required"/>
+ <xs:attribute name="max" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="hairstyle">
+ <xs:complexType>
+ <xs:attribute name="min" type="xs:int" use="required"/>
+ <xs:attribute name="max" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="look">
+ <xs:complexType>
+ <xs:attribute name="min" type="xs:int" use="required"/>
+ <xs:attribute name="max" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="stat">
+ <xs:complexType>
+ <xs:attribute name="min" type="xs:int" use="required"/>
+ <xs:attribute name="max" type="xs:int" use="required"/>
+ <xs:attribute name="sum" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="race">
+ <xs:complexType>
+ <xs:attribute name="min" type="xs:int" use="required"/>
+ <xs:attribute name="max" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="item">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ <!--
+ SpriteDef::load
+ SpriteDef::loadSprite
+ -->
+ <xs:element name="sprite" type="root_sprite"/>
+ <xs:complexType name="root_sprite">
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <!-- SpriteDef::includeSprite -->
+ <xs:element name="include">
+ <xs:complexType>
+ <xs:attribute name="file" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <!-- SpriteDef::loadImageSet -->
+ <xs:element name="imageset">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="width" type="xs:int" use="required"/>
+ <xs:attribute name="height" type="xs:int" use="required"/>
+ <xs:attribute name="src" type="xs:string" use="required"/>
+ <xs:attribute name="offsetX" type="xs:int" default="0"/>
+ <xs:attribute name="offsetY" type="xs:int" default="0"/>
+ </xs:complexType>
+ </xs:element>
+ <!-- SpriteDef::loadAction -->
+ <xs:element name="action">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <!-- SpriteDef::loadAnimation -->
+ <xs:element name="animation" type="animation_3"/>
+ </xs:choice>
+ <!-- TODO mostly enum? -->
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="imageset" type="xs:string" use="required"/>
+ <xs:attribute name="hp" type="xs:int" default="100"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ <xs:attribute name="variants" type="xs:int" use="optional"/>
+ <xs:attribute name="variant_offset" type="xs:int" use="optional"/>
+ </xs:complexType>
+ <!--
+ WeaponsDB::load
+ weaponsFile: weapons.xml
+ weaponsPatchFile: weapons_patch.xml
+ weaponsPatchDir: weapons.d/*.xml
+ -->
+ <xs:element name="weapons" type="root_weapons"/>
+ <xs:complexType name="root_weapons">
+ <xs:choice maxOccurs="unbounded">
+ <xs:element name="swords" type="weapons_items"/>
+ <xs:element name="bows" type="weapons_items"/>
+ <xs:element name="shields" type="weapons_items"/>
+ </xs:choice>
+ </xs:complexType>
+ <xs:complexType name="weapons_items">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="item">
+ <xs:complexType>
+ <xs:attribute name="id" type="xs:integer" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:complexType>
+</xs:schema>
diff --git a/testxml/xsd/xlink.xsd b/testxml/xsd/xlink.xsd
new file mode 100644
index 0000000..2c53ec3
--- /dev/null
+++ b/testxml/xsd/xlink.xsd
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://www.w3.org/1999/xlink" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="xml.xsd"/>
+ <xs:attribute name="type">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="simple"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attributeGroup name="show">
+ <xs:attribute name="show" use="optional" form="qualified">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="embed"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="actuate">
+ <xs:attribute name="actuate" use="optional" form="qualified">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="onLoad"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="show1">
+ <xs:attribute name="show" use="optional" form="qualified">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="replace"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="actuate1">
+ <xs:attribute name="actuate" use="optional" form="qualified">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="onRequest"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="show2">
+ <xs:attribute name="show" use="optional" form="qualified">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="new"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="its-type">
+ <xs:attribute name="type" use="optional">
+ <xs:annotation>
+ <xs:documentation>Type of pointer to external rules files.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="simple">
+ <xs:annotation>
+ <xs:documentation>Simple link.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="its-href">
+ <xs:attribute name="href" type="xs:anyURI" use="optional">
+ <xs:annotation>
+ <xs:documentation>Pointer to external rules files.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+</xs:schema>
diff --git a/testxml/xsd/xml.xsd b/testxml/xsd/xml.xsd
new file mode 100644
index 0000000..aea7d0d
--- /dev/null
+++ b/testxml/xsd/xml.xsd
@@ -0,0 +1,287 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
+<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns ="http://www.w3.org/1999/xhtml"
+ xml:lang="en">
+
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+ <h1>About the XML namespace</h1>
+
+ <div class="bodytext">
+ <p>
+ This schema document describes the XML namespace, in a form
+ suitable for import by other schema documents.
+ </p>
+ <p>
+ See <a href="http://www.w3.org/XML/1998/namespace.html">
+ http://www.w3.org/XML/1998/namespace.html</a> and
+ <a href="http://www.w3.org/TR/REC-xml">
+ http://www.w3.org/TR/REC-xml</a> for information
+ about this namespace.
+ </p>
+ <p>
+ Note that local names in this namespace are intended to be
+ defined only by the World Wide Web Consortium or its subgroups.
+ The names currently defined in this namespace are listed below.
+ They should not be used with conflicting semantics by any Working
+ Group, specification, or document instance.
+ </p>
+ <p>
+ See further below in this document for more information about <a
+ href="#usage">how to refer to this schema document from your own
+ XSD schema documents</a> and about <a href="#nsversioning">the
+ namespace-versioning policy governing this schema document</a>.
+ </p>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:attribute name="lang">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>lang (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose value
+ is a language code for the natural language of the content of
+ any element; its value is inherited. This name is reserved
+ by virtue of its definition in the XML specification.</p>
+
+ </div>
+ <div>
+ <h4>Notes</h4>
+ <p>
+ Attempting to install the relevant ISO 2- and 3-letter
+ codes as the enumerated possible values is probably never
+ going to be a realistic possibility.
+ </p>
+ <p>
+ See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
+ http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
+ and the IANA language subtag registry at
+ <a href="http://www.iana.org/assignments/language-subtag-registry">
+ http://www.iana.org/assignments/language-subtag-registry</a>
+ for further information.
+ </p>
+ <p>
+ The union allows for the 'un-declaration' of xml:lang with
+ the empty string.
+ </p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:union memberTypes="xs:language">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value=""/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:union>
+ </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="space">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>space (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose
+ value is a keyword indicating what whitespace processing
+ discipline is intended for the content of the element; its
+ value is inherited. This name is reserved by virtue of its
+ definition in the XML specification.</p>
+
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:NCName">
+ <xs:enumeration value="default"/>
+ <xs:enumeration value="preserve"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="base" type="xs:anyURI"> <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>base (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose value
+ provides a URI to be used as the base for interpreting any
+ relative URIs in the scope of the element on which it
+ appears; its value is inherited. This name is reserved
+ by virtue of its definition in the XML Base specification.</p>
+
+ <p>
+ See <a
+ href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
+ for information about this attribute.
+ </p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="id" type="xs:ID">
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>id (as an attribute name)</h3>
+ <p>
+ denotes an attribute whose value
+ should be interpreted as if declared to be of type ID.
+ This name is reserved by virtue of its definition in the
+ xml:id specification.</p>
+
+ <p>
+ See <a
+ href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
+ for information about this attribute.
+ </p>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attributeGroup name="specialAttrs">
+ <xs:attribute ref="xml:base"/>
+ <xs:attribute ref="xml:lang"/>
+ <xs:attribute ref="xml:space"/>
+ <xs:attribute ref="xml:id"/>
+ </xs:attributeGroup>
+
+ <xs:annotation>
+ <xs:documentation>
+ <div>
+
+ <h3>Father (in any context at all)</h3>
+
+ <div class="bodytext">
+ <p>
+ denotes Jon Bosak, the chair of
+ the original XML Working Group. This name is reserved by
+ the following decision of the W3C XML Plenary and
+ XML Coordination groups:
+ </p>
+ <blockquote>
+ <p>
+ In appreciation for his vision, leadership and
+ dedication the W3C XML Plenary on this 10th day of
+ February, 2000, reserves for Jon Bosak in perpetuity
+ the XML name "xml:Father".
+ </p>
+ </blockquote>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+ <xs:documentation>
+ <div xml:id="usage" id="usage">
+ <h2><a name="usage">About this schema document</a></h2>
+
+ <div class="bodytext">
+ <p>
+ This schema defines attributes and an attribute group suitable
+ for use by schemas wishing to allow <code>xml:base</code>,
+ <code>xml:lang</code>, <code>xml:space</code> or
+ <code>xml:id</code> attributes on elements they define.
+ </p>
+ <p>
+ To enable this, such a schema must import this schema for
+ the XML namespace, e.g. as follows:
+ </p>
+ <pre>
+ &lt;schema . . .>
+ . . .
+ &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+ </pre>
+ <p>
+ or
+ </p>
+ <pre>
+ &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+ </pre>
+ <p>
+ Subsequently, qualified reference to any of the attributes or the
+ group defined below will have the desired effect, e.g.
+ </p>
+ <pre>
+ &lt;type . . .>
+ . . .
+ &lt;attributeGroup ref="xml:specialAttrs"/>
+ </pre>
+ <p>
+ will define a type which will schema-validate an instance element
+ with any of those attributes.
+ </p>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+ <xs:documentation>
+ <div id="nsversioning" xml:id="nsversioning">
+ <h2><a name="nsversioning">Versioning policy for this schema document</a></h2>
+ <div class="bodytext">
+ <p>
+ In keeping with the XML Schema WG's standard versioning
+ policy, this schema document will persist at
+ <a href="http://www.w3.org/2009/01/xml.xsd">
+ http://www.w3.org/2009/01/xml.xsd</a>.
+ </p>
+ <p>
+ At the date of issue it can also be found at
+ <a href="http://www.w3.org/2001/xml.xsd">
+ http://www.w3.org/2001/xml.xsd</a>.
+ </p>
+ <p>
+ The schema document at that URI may however change in the future,
+ in order to remain compatible with the latest version of XML
+ Schema itself, or with the XML namespace itself. In other words,
+ if the XML Schema or XML namespaces change, the version of this
+ document at <a href="http://www.w3.org/2001/xml.xsd">
+ http://www.w3.org/2001/xml.xsd
+ </a>
+ will change accordingly; the version at
+ <a href="http://www.w3.org/2009/01/xml.xsd">
+ http://www.w3.org/2009/01/xml.xsd
+ </a>
+ will not change.
+ </p>
+ <p>
+ Previous dated (and unchanging) versions of this schema
+ document are at:
+ </p>
+ <ul>
+ <li><a href="http://www.w3.org/2009/01/xml.xsd">
+ http://www.w3.org/2009/01/xml.xsd</a></li>
+ <li><a href="http://www.w3.org/2007/08/xml.xsd">
+ http://www.w3.org/2007/08/xml.xsd</a></li>
+ <li><a href="http://www.w3.org/2004/10/xml.xsd">
+ http://www.w3.org/2004/10/xml.xsd</a></li>
+ <li><a href="http://www.w3.org/2001/03/xml.xsd">
+ http://www.w3.org/2001/03/xml.xsd</a></li>
+ </ul>
+ </div>
+ </div>
+ </xs:documentation>
+ </xs:annotation>
+
+</xs:schema>
+
diff --git a/testxml/xsdcheck.sh b/testxml/xsdcheck.sh
new file mode 100755
index 0000000..8cd27dd
--- /dev/null
+++ b/testxml/xsdcheck.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+
+cd xsd
+DIR="$(pwd)/../../../clientdata"
+rm ../errors.txt
+
+function check {
+ xmllint --format --schema tmw.xsd "${DIR}"/"${1}" 2>&1 >/dev/null | \
+ grep -v ": Skipping import of schema located at " | \
+ grep -v ".xml validates" | \
+ grep -v ".manaplus validates" >>../errors.txt
+}
+
+xmllint --format --schema XMLSchema.xsd tmw.xsd 2>&1 >/dev/null | \
+ grep -v ": Skipping import of schema located at " | \
+ grep -v ".xsd validates" >>../errors.txt
+
+check avatars.xml
+check badges.xml
+check charcreation.xml
+check deadmessages.xml
+check effects.xml
+check elementals.xml
+check emotes.xml
+check equipmentslots.xml
+check equipmentwindow.xml
+check tmw2.manaplus
+check features.xml
+check groups.xml
+check homunculuses.xml
+check horses.xml
+check itemcolors.xml
+check itemfields.xml
+check items.xml
+check maps.xml
+check mercenaries.xml
+check mods.xml
+check monsters.xml
+check npcdialogs.xml
+check npcs.xml
+check paths.xml
+check pets.xml
+check quests.xml
+check skills.xml
+check skillunits.xml
+check sounds.xml
+check stats.xml
+check status-effects.xml
+check units.xml
+check weapons.xml
+
+find -H "${DIR}/graphics" -type f -name "*.xml" -exec ./checkfile.sh {} \; >>../errors.txt
diff --git a/update/TMW2org.zip b/update/TMW2org.zip
new file mode 100644
index 0000000..5bed7a0
--- /dev/null
+++ b/update/TMW2org.zip
Binary files differ
diff --git a/update/addmods.sh b/update/addmods.sh
new file mode 100755
index 0000000..109a81b
--- /dev/null
+++ b/update/addmods.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2010-2012 TMW2 Online
+# Author: Andrei Karas (4144)
+
+dir=`pwd`
+mds='../../clientdata/mods'
+
+previous=`cat commit.txt`
+rm files/xml_mods.txt
+rm files/mod-*
+
+cd ../../clientdata/mods
+
+FILES=`ls`
+
+for file in $FILES; do
+ echo "Uploading $file mod...."
+ cd $file
+ find . -type f | xargs zip -9 -r ../../../tools/update/files/mod-$file.zip
+ cd $dir/files
+ sum=`adler32 mod-$file.zip | awk '{print $2}'`
+ echo " <mod type=\"data\" group=\"$file\" file=\"mod-$file.zip\" hash=\"${sum}\" />" >> xml_mods.txt
+ cp mod-$file.zip ../upload/
+ cd ../$mds
+done
+
+cd $dir/files
+cp xml_header.txt resources.xml
+cat xml_footer.txt >>resources.xml
+cat xml_mods.txt >>resources.xml
+echo '</updates>' >>resources.xml
+cp resources2.txt ../upload/
+cp resources.xml ../upload/
+
diff --git a/update/adler32.c b/update/adler32.c
new file mode 100644
index 0000000..606c739
--- /dev/null
+++ b/update/adler32.c
@@ -0,0 +1,79 @@
+/*
+ * adler32.c (c) 2006 Bjorn Lindeijer
+ * License: GPL, v2 or later
+ *
+ * Calculates Adler-32 checksums for all files passed as argument.
+ *
+ * Usage: adler32 [file]...
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <zlib.h>
+
+/**
+ * Calculates the Adler-32 checksum for the given file.
+ */
+unsigned long fadler32(FILE *file)
+{
+ // Obtain file size
+ fseek(file, 0, SEEK_END);
+ long fileSize = ftell(file);
+ rewind(file);
+
+ // Calculate Adler-32 checksum
+ char *buffer = (char*) malloc(fileSize);
+ fread(buffer, 1, fileSize, file);
+ unsigned long adler = adler32(0L, Z_NULL, 0);
+ adler = adler32(adler, (Bytef*) buffer, fileSize);
+ free(buffer);
+
+ return adler;
+}
+
+/**
+ * Prints out usage and exists.
+ */
+void print_usage()
+{
+ printf("Usage: adler32 mode [file]...\n");
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ int i; /**< Loops through arguments. */
+
+ if (argc < 2)
+ {
+ print_usage();
+ }
+
+ int mode = atoi(argv[1]);
+
+ for (i = 2; i < argc; ++i)
+ {
+ FILE *file = fopen(argv[i], "r");
+
+ if (!file)
+ {
+ printf("Error while opening '%s' for reading!\n", argv[i]);
+ exit(1);
+ }
+
+ unsigned long adler = fadler32(file);
+ switch (mode)
+ {
+ case 0:
+ default:
+ printf("%s %lx\n", argv[i], adler);
+ break;
+ case 1:
+ printf("%lx", adler);
+ break;
+ }
+ fclose(file);
+ }
+
+ return 0;
+}
diff --git a/update/commit.txt b/update/commit.txt
new file mode 100644
index 0000000..dc221c3
--- /dev/null
+++ b/update/commit.txt
@@ -0,0 +1 @@
+a0877f34f3f73c84629bf7781e7da0b2feaa0174
diff --git a/update/commit_old.txt b/update/commit_old.txt
new file mode 100644
index 0000000..652fd1b
--- /dev/null
+++ b/update/commit_old.txt
@@ -0,0 +1 @@
+2b90b8051698ab0e962dab6e338e6894e4a7cd21
diff --git a/update/create_music.sh b/update/create_music.sh
new file mode 100755
index 0000000..70720d9
--- /dev/null
+++ b/update/create_music.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2011-2012 Evol Online
+# Author: Andrei Karas (4144)
+
+dir=`pwd`
+
+rm adler32
+gcc -lz adler32.c -o adler32
+
+mkdir files
+mkdir upload
+
+rm files/music.zip
+cd ../../music
+find -iregex ".+[.]\(ogg\|mp3\)" -exec touch --date=2015-01-01 {} \;
+find -iregex ".+[.]\(ogg\|mp3\)" -printf "%P\n" | zip -X -@ ../tools/update/files/music.zip
+git log --pretty=oneline -n 1 | awk '{print $1}' >../tools/update/musiccommit.txt
+
+cd $dir/files
+sum=`adler32 music.zip | awk '{print $2}'`
+
+echo " <update type=\"music\" required=\"no\" file=\"music.zip\" hash=\"${sum}\" description=\"TMW-2 music\" />" >> xml_footer.txt
+
+cp xml_header.txt resources.xml
+cat xml_footer.txt >>resources.xml
+echo '</updates>' >>resources.xml
+
+cp music.zip ../upload/
+cp resources.xml ../upload/
+cp ../news.txt ../upload/
diff --git a/update/createnew.sh b/update/createnew.sh
new file mode 100755
index 0000000..5ab356f
--- /dev/null
+++ b/update/createnew.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2011-2012 TMW2 Online
+# Author: Andrei Karas (4144)
+
+dir=`pwd`
+
+rm adler32
+gcc -lz adler32.c -o adler32
+
+mkdir files
+mkdir upload
+
+rm files/TMW2.zip
+cd ../../clientdata
+find -iregex ".+[.]\(xml\|png\|jpg\|tmx\|ogg\|txt\|po\|tsx\)" -exec touch --date=2015-01-01 {} \;
+find -iregex ".+[.]\(xml\|png\|jpg\|tmx\|ogg\|txt\|po\|tsx\)" -printf "%P\n" | zip -X -@ ../tools/update/files/TMW2.zip
+git log --pretty=oneline -n 1 | awk '{print $1}' >../tools/update/commit.txt
+
+cd $dir/files
+sum=`adler32 TMW2.zip | awk '{print $2}'`
+echo "TMW2.zip ${sum}" >resources2.txt
+
+echo '<?xml version="1.0"?>
+<updates>' >xml_header.txt
+touch xml_footer.txt
+touch xml_mods.txt
+
+echo " <update type=\"data\" file=\"TMW2.zip\" hash=\"${sum}\" />" >> xml_header.txt
+cp xml_header.txt resources.xml
+cat xml_footer.txt >>resources.xml
+cat xml_mods.txt >>resources.xml
+echo '</updates>' >>resources.xml
+
+cp TMW2.zip ../upload/
+cp resources2.txt ../upload/
+cp resources.xml ../upload/
+cp ../news.txt ../upload/
+
+
diff --git a/update/deploy.sh b/update/deploy.sh
new file mode 100755
index 0000000..1ae1be7
--- /dev/null
+++ b/update/deploy.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# Deploy script
+echo "Deploying Bugfix-TMW2.zip"
+scp -4 upload/Bugfix-TMW2.zip jesusalva@updates.tmw2.org:/home/jesusalva/http/updates/
+scp -4 upload/resources.xml jesusalva@updates.tmw2.org:/home/jesusalva/http/updates/
+
diff --git a/update/musiccommit.txt b/update/musiccommit.txt
new file mode 100644
index 0000000..58369c4
--- /dev/null
+++ b/update/musiccommit.txt
@@ -0,0 +1 @@
+39e9aec960bd515ba0c458e39117edcc2f0d3079
diff --git a/update/musiccommit_old.txt b/update/musiccommit_old.txt
new file mode 100644
index 0000000..ee52604
--- /dev/null
+++ b/update/musiccommit_old.txt
@@ -0,0 +1 @@
+e65188eb11f07b09c20cfbc6e97ddd2275893a48
diff --git a/update/news.txt b/update/news.txt
new file mode 100644
index 0000000..2559f5f
--- /dev/null
+++ b/update/news.txt
@@ -0,0 +1,40 @@
+##0 Actual Release: ##1 17.2 Interlude in Red
+##0 Welcome to ##BTMW-2: Moubootaur Legends##b!
+
+##0 By playing you agree and abide to the ##1##BTerms of Service##b##0, available at:
+##1 [@@https://tmw2.org/legal|https://tmw2.org/legal@@]
+##2 Last ToS update: 2021-09-15
+
+##0 A Night in Red got delayed, and this is an interlude! Laura was only slightly
+##0 tweaked thus far. This interlude has more hints and tips, and fix bugs on Aurora
+##0 as well as rewording several stuff. ##BRealm of Drops##b skill was added: Now
+##0 you'll get warranted drops as you kill monsters, reducing the effects of PRNG.
+##0 ##BKreist Daily Quest##b was entirely reworked, and defeating a boss now plays
+##0 out a fanfare (but only some boss!) Boss Raid and Dream Towers events were
+##0 tweaked. You need less intelligence to learn magic. ##BConnor##b is more
+##0 generous giving coal. More drops for mobs above level 100. Iced Bottle now
+##0 heals a tiny bit. These Quality of Life changes makes an ##BInterlude in Red##b.
+
+##0 Ready to become a Moubootaur Legend now?
+##0 TMW2 Team
+
+##7 We want to express our gratitude here to ##BWoody##b, ##BSharli##b and ##BPovo##b, for sponsoring this server.
+##7 We would also want to kindly thank ##Bjak1##b and the GermanTMW Team for
+##7 their sponsorship and help.
+##7 And by last, our gratitude to every developer and contributor who made this
+##7 possible!
+
+----
+
+##0 [@@https://transifex.com/arctic-games/moubootaur-legends|Translators@@] are always welcome.
+##1 Our Staff will never ask for your password. You are the sole responsible for
+##1 its safety! You can contact a GM with ##B@request##b or calling one at #world.
+##0 (If you want, it's possible to skip music download under Settings > Audio)
+
+##0 Enjoy gaming, and leave feedback!
+
+##9 -- Your TMW2 Team
+##9 October 2022
+
+##0 You can check out this page for older entries:
+##9 [@@https://tmw2.org/news|https://tmw2.org/news@@]
diff --git a/update/pseudo_update.sh b/update/pseudo_update.sh
new file mode 100755
index 0000000..38678bf
--- /dev/null
+++ b/update/pseudo_update.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2010-2012 TMW2 Online
+# Author: Andrei Karas (4144)
+
+dir=`pwd`
+
+previous=`cat commit.txt`
+rm upload/Bugfix-TMW2.zip
+
+cd ../../clientdata
+head=`git log --pretty=oneline -n 1 | awk '{print $1}'`
+u1=`echo ${previous} | cut -c 1-7`
+u2=`echo ${head} | cut -c 1-7`
+git diff --name-status ${previous} HEAD | awk '/^(A|M)\t/ {print $2}; /^(R...)\t/ {print $3}' | \
+grep -e "[.]\(xml\|png\|jpg\|tmx\|ogg\|txt\|po\|tsx\)" | sort | uniq | \
+xargs zip -X -9 -r ../tools/update/upload/Bugfix-TMW2.zip
+
+cd $dir/upload
+
+sum=`adler32 Bugfix-TMW2.zip | awk '{print $2}'`
+echo "Update ID: ${u1}..${u2}"
+echo "Checksum: ${sum}"
+
+echo "Bugfix-TMW2.zip ${sum}" >>resources2.txt
+cp ../files/xml_header.txt resources.xml
+echo " <update type=\"data\" file=\"Bugfix-TMW2.zip\" hash=\"${sum}\" />" >> resources.xml
+cat ../files/xml_footer.txt >>resources.xml
+cat ../files/xml_mods.txt >>resources.xml
+echo '</updates>' >>resources.xml
+
diff --git a/update/update.sh b/update/update.sh
new file mode 100755
index 0000000..5e02147
--- /dev/null
+++ b/update/update.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2010-2012 TMW2 Online
+# Author: Andrei Karas (4144)
+
+dir=`pwd`
+
+rm adler32
+gcc -lz adler32.c -o adler32
+
+mkdir files
+mkdir upload
+
+previous=`cat commit_old.txt`
+
+cd ../../clientdata
+head=`git log --pretty=oneline -n 1 | awk '{print $1}'`
+u1=`echo ${previous} | cut -c 1-7`
+u2=`echo ${head} | cut -c 1-7`
+git diff --name-status ${previous} HEAD | awk '/^(A|M)\t/ {print $2}; /^(R...)\t/ {print $3}' | \
+ grep -e "[.]\(xml\|png\|jpg\|tmx\|ogg\|txt\|po\|tsx\)" | sort | uniq | \
+ xargs zip -X -9 -r ../tools/update/files/TMW2-${u1}.zip
+
+cd $dir/files
+if [ -f TMW2-${u1}.zip ]; then
+ #mv ../commit.txt ../commit_old.txt
+ echo ${head} >../commit.txt
+ sum=`adler32 TMW2-${u1}.zip | awk '{print $2}'`
+ echo "TMW2-${u1}.zip ${sum}" >>resources2.txt
+ echo " <update type=\"data\" file=\"TMW2-${u1}.zip\" hash=\"${sum}\" />" >> xml_header.txt
+ cp xml_header.txt resources.xml
+ cat xml_footer.txt >>resources.xml
+ cat xml_mods.txt >>resources.xml
+ echo '</updates>' >>resources.xml
+
+ cp TMW2-${u1}.zip ../upload/
+ cp resources2.txt ../upload/
+ cp resources.xml ../upload/
+ cp ../news.txt ../upload
+
+ echo "Update ID: ${u1}..${u2}"
+fi
diff --git a/update/update_music.sh b/update/update_music.sh
new file mode 100755
index 0000000..a99f5c6
--- /dev/null
+++ b/update/update_music.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2010-2012 Evol Online
+# Author: Andrei Karas (4144)
+
+dir=`pwd`
+
+rm adler32
+gcc -lz adler32.c -o adler32
+
+mkdir files
+mkdir upload
+
+previous=`cat musiccommit.txt`
+
+cd ../../music
+head=`git log --pretty=oneline -n 1 | awk '{print $1}'`
+u1=`echo ${previous} | cut -c 1-7`
+u2=`echo ${head} | cut -c 1-7`
+git log --name-status ${previous}..${head} \
+| awk '/^(A|M)\t/ {print $2}'
+ grep -e "[.]\(ogg\|mp3\)" | sort | uniq | \
+ xargs zip -X -9 -r ../../tools/update/files/music-${u1}..${u2}.zip
+#exit
+
+cd $dir/files
+if [ -f music-${u1}..${u2}.zip ]; then
+ mv ../muciscommit.txt ../muciscommit_old.txt
+ echo ${head} >../muciscommit.txt
+ sum=`adler32 music-${u1}..${u2}.zip | awk '{print $2}'`
+
+ echo " <update type=\"music\" required=\"no\" file=\"music-${u1}..${u2}.zip\" hash=\"${sum}\" description=\"TMW2 extra music\" />" >> xml_footer.txt
+ cp xml_header.txt resources.xml
+ cat xml_footer.txt >>resources.xml
+ cat xml_mods.txt >>resources.xml
+ echo '</updates>' >>resources.xml
+
+ cp music-${u1}..${u2}.zip ../upload/
+ cp resources2.txt ../upload/
+ cp resources.xml ../upload/
+ cp ../news.txt ../upload
+fi
diff --git a/web/all_news.bin b/web/all_news.bin
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/web/all_news.bin
@@ -0,0 +1 @@
+
diff --git a/web/backupcopy b/web/backupcopy
new file mode 100755
index 0000000..81601b0
--- /dev/null
+++ b/web/backupcopy
@@ -0,0 +1,129 @@
+#! /usr/bin/env python2.7
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2018 TMW-2
+# Author: Jesusalva
+
+import os
+
+defaultLang = "en"
+rootPath = "../../web/"
+
+langs=[]
+files={}
+originals={}
+
+# Populates the basic arrays
+def populate():
+ # Populate langs
+ o=open("langs.txt", "r")
+ for i in o:
+ langs.append(i.replace('\n',''))
+ o.close()
+
+ # Populate files
+ for i in os.listdir(rootPath):
+ if i.endswith(".php.en"):
+ print "Valid file: "+str(i)
+ files[i]={}
+ for x in langs:
+ files[i][x]={}
+
+ print str(files)
+# Write po files
+def writePoFile(nf,lg):
+ a=open("po/"+lg+".po", "a")
+ f=open(rootPath+nf, "r")
+
+ for line in f:
+ a.write("#, no-c-format\n")
+ a.write('msgid "%s"\n' % line.replace('\n', ''))
+ try:
+ if files[nf][lg][line] != line:
+ a.write('msgstr "%s"\n' % files[nf][lg][line].replace('\n',''))
+ else:
+ a.write('msgstr "%s"\n' % "")
+ except KeyError:
+ a.write('msgstr "%s"\n' % "")
+
+ a.write('\n')
+
+ a.close()
+
+# Creates/Loads stuff
+def genreadPoFile():
+ for a in langs:
+ print("Reading translations for "+a)
+ for i in files:
+ readPoFile(i,a)
+
+def generatePoFiles():
+ for a in langs:
+ print("Updating po file for "+a)
+ xrv=open("po/"+a+".po", "w")
+ # Print header
+ xrv.write('\
+# Copyright (C) 2018 TMW2\n\
+#\n\
+\n\
+msgid ""\n\
+msgstr ""\n\
+"Project-Id-Version: TMW2\\n"\n\
+"MIME-Version: 1.0\\n"\n\
+"Content-Type: text/plain; charset=UTF-8\\n"\n\
+"Content-Transfer-Encoding: 8bit\\n"\n\
+\n\
+')
+ xrv.close()
+
+ for i in files:
+ writePoFile(i,a)
+
+def generateLocal():
+ for a in langs:
+ print("generating local file for "+a)
+ for i in files:
+ writeLocal(i,a)
+
+# Reads Po Files
+def readPoFile(nf, lg):
+ try:
+ a=open("po/"+lg+".po", "r")
+ except:
+ a=open("po/"+lg+".po", "w")
+ a.close()
+ a=open("po/"+lg+".po", "r")
+
+ ctx=""
+ for line in a:
+ if "msgid " in line:
+ ctx=line.replace('\n','')
+ if "msgstr " in line and ctx != "":
+ if line != 'msgstr ""\n':
+ files[nf][lg][ctx]=line.replace('\n', '')
+ else:
+ files[nf][lg][ctx]=ctx
+ ctx=""
+ a.close()
+
+def writeLocal(nf, lg):
+ a=open(rootPath+nf[:-2]+lg, 'w')
+ b=open(rootPath+nf, 'r')
+
+ for line in b:
+ try:
+ a.write(files[nf][lg][line])
+ except KeyError:
+ a.write(line)
+
+ b.close()
+ a.close()
+
+# Mainframe: populate arrays
+populate()
+
+# Mainframe: handle PO files
+genreadPoFile()
+generatePoFiles()
+generateLocal()
+
diff --git a/web/deploy.sh b/web/deploy.sh
new file mode 100755
index 0000000..65c3bd2
--- /dev/null
+++ b/web/deploy.sh
@@ -0,0 +1,4 @@
+./news_to_html.py
+./newsfeed.py
+#cp news.html ../../web/
+cp feed.xml ../../web/tmw2.org/
diff --git a/web/feed.xml b/web/feed.xml
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/web/feed.xml
@@ -0,0 +1 @@
+
diff --git a/web/fetch.py b/web/fetch.py
new file mode 100755
index 0000000..c5fbeea
--- /dev/null
+++ b/web/fetch.py
@@ -0,0 +1,62 @@
+#! /usr/bin/env python2.7
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2018 TMW-2
+# Author: Jesusalva
+
+from transifex.api import TransifexAPI
+#import sys
+
+#if len(sys.argv) == 2:
+# if sys.argv[1] == "AllFilesVerified":
+# pass
+# else:
+# print("Rejected: Incorrect magic word.")
+# exit(1)
+#else:
+# print("Rejected: As this script is VULNERABLE TO CODE INJECTION, you cannot run this command without verifying all files at Transifex first.")
+# exit(1)
+print("\033[1mThis script is VULNERABLE TO CODE INJECTION. Git diff before deploying!\033[0m")
+
+project='moubootaur-legends'
+
+print("Loading user credentials (login.txt/password.txt) from lang/ folder...")
+
+# Load credentials from login.txt and password.txt
+login=open('../lang/login.txt', 'r')
+for i in login:
+ username=i.replace('\n', '').replace('\r', '')
+
+login.close()
+
+passw=open('../lang/password.txt', 'r')
+for i in passw:
+ password=i.replace('\n', '').replace('\r', '')
+
+passw.close()
+
+t=TransifexAPI(username, password, 'https://www.transifex.com')
+
+if (not t.ping):
+ print("ERROR: Ping failed, this may be due incorrect username/password in login.txt and password.txt. Ensure there is NO newline at the end of file.")
+ exit(1)
+
+if (not t.project_exists(project)):
+ print("ERROR: Invalid project name")
+ exit(1)
+
+# Load languages
+langs=[]
+vcx=open("langs.txt", "r")
+for i in vcx:
+ if i != "en":
+ langs.append(i.replace('\n', ''))
+vcx.close()
+
+# Fetch all translations and record them at in/
+for i in langs:
+ print("Fetching %s..." %(i))
+ t.get_translation(project, 'website', i, 'po/'+str(i)+'.po')
+
+print("All translations were retrieved.")
+print("Please sanitize files removing \"\\n\", or updatelang.py won't parse properly.")
diff --git a/web/gameinfo.sh b/web/gameinfo.sh
new file mode 100755
index 0000000..757ee96
--- /dev/null
+++ b/web/gameinfo.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+echo "Deploying news file..."
+scp -4 -P 22 feed.xml jesusalva@213.202.247.189:/home/jesusalva/http/
+scp -4 -P 22 ../update/news.txt jesusalva@213.202.247.189:/home/jesusalva/http/updates/
+
+echo "Begin - Gameinfo updater. User input required."
+cd ../../gameinfo-api
+
+cd serverdata
+git pull --rebase -X theirs
+cd ..
+
+cd clientdata
+git pull --rebase -X theirs
+cd ..
+
+git commit -a
+git push
+
+echo "Gameinfo updated! Mission complete!"
+
diff --git a/web/index.bin b/web/index.bin
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/web/index.bin
@@ -0,0 +1 @@
+
diff --git a/web/langs.txt b/web/langs.txt
new file mode 100644
index 0000000..c1cd357
--- /dev/null
+++ b/web/langs.txt
@@ -0,0 +1,5 @@
+pt-BR
+fr
+de
+es
+ru
diff --git a/web/legacy b/web/legacy
new file mode 100755
index 0000000..4629a06
--- /dev/null
+++ b/web/legacy
@@ -0,0 +1,51 @@
+#! /usr/bin/env python2.7
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2018 TMW-2
+# Author: Jesusalva
+
+import os
+import re
+
+defaultLang = "en"
+
+rootPath = "../../web/"
+
+filt = re.compile(".+[.]php", re.IGNORECASE)
+
+allFiles=[]
+langFiles = dict()
+langs = [] # Current languages
+
+# Read languages
+o=open("langs.txt", "r")
+for i in o:
+ langs.append(i.replace('\n',''))
+o.close()
+
+# Read PHP files, and create langFiles[i]["master"]
+for i in os.listdir(rootPath):
+ if i.endswith(".php"):
+ langFiles[i]={}
+ o=open(rootPath+'/'+i, 'r')
+ for line in o:
+ langFiles[i]["master"]=line
+ o.close()
+
+# Sort each language for stuff
+for a in langFiles:
+ for b in langs:
+ langFiles[a][b]=[]
+
+ cnt=0
+ pot=open("po/"+b+".po", "r")
+ for line in pot:
+ if "msgstr" in line:
+ abaco=str(line.replace('msgstr "','')[:-1])
+ if abaco != "":
+ langFiles[a][b].append(abaco)
+ else:
+ langFiles[a][b].append(langFiles[a][master][cnt])
+ cnt+=1
+
+ print str(langFiles[a][b])
diff --git a/web/news.html b/web/news.html
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/web/news.html
@@ -0,0 +1 @@
+
diff --git a/web/news_to_html.py b/web/news_to_html.py
new file mode 100755
index 0000000..4015a62
--- /dev/null
+++ b/web/news_to_html.py
@@ -0,0 +1,91 @@
+#! /usr/bin/env python
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2018 The Mana World 2
+# Author: Jonatas N. (Jesusalva)
+
+import datetime
+date=str(datetime.date.today())
+
+#date="2019-08-05"
+
+# Update the INDEX
+i1=open("index.bin", "r")
+i2=open("1index.bin", "w")
+for i in i1:
+ i2.write(i)
+i2.close()
+i1.close()
+i1=open("index.bin", "w")
+i2=open("1index.bin", "r")
+i1.write(date+'\n')
+for i in i2:
+ i1.write(i)
+i2.close()
+i1.close()
+i1=open("index.bin", "r")
+
+# Prepare to sort from newest to oldest
+dt=open("all_news.bin", "r")
+dc=open("backup.bin", "w")
+for i in dt:
+ dc.write(i)
+dc.close()
+dt.close()
+dt=open("all_news.bin", "w")
+dc=open("backup.bin", "r")
+
+# Read news and prepare header
+ns=open("../update/news.txt", "r")
+dt.write("\n<h3><a name="+date+">"+date+"</a></h3>\n\n<font color=black>")
+
+# Function to markup it, and strip new lines
+def markup(r):
+ r=r.replace('##0', '</font><font color=black>')
+ r=r.replace('##1', '<font color=red>')
+ r=r.replace('##2', '<font color=green>')
+ r=r.replace('##3', '<font color=blue>')
+ r=r.replace('##4', '<font color=gold>')
+ r=r.replace('##5', '<font color=yellow>')
+ r=r.replace('##6', '<font color=pink>')
+ r=r.replace('##7', '<font color=purple>')
+ r=r.replace('##8', '<font color=gray>')
+ r=r.replace('##9', '<font color=brown>')
+ r=r.replace('##B', '<b>')
+ r=r.replace('##b', '</b>')
+ r=r.replace('[@@', '<a href="')
+ r=r.replace('|', '">')
+ r=r.replace('@@]', '</a>')
+ return r
+def nn(r):
+ return r.replace('\n', '')
+
+# Write the news at all_news.bin, and then reload the backup. Finally, close the file
+for i in ns:
+ dt.write(markup(i)+'<br/>\n')
+dt.write("</font><hr/>")
+for i in dc:
+ dt.write(i)
+
+ns.close()
+dt.close()
+dc.close()
+
+# Write the final HTML page
+wp=open("news.html", "w")
+dt=open("all_news.bin", "r")
+
+wp.write("<!DOCTYPE HTML><html><head><title>Server News</title></head><body>")
+wp.write("<table border=1 align=left><tr><td>Archive</td></tr>")
+for i in i1:
+ wp.write('<tr><td><a href=#'+nn(i)+'>'+nn(i)+'</a></td></tr>')
+wp.write('</table><br/>')
+
+for line in dt:
+ wp.write(nn(line))
+wp.write("</body></html>")
+
+wp.close()
+dt.close()
+i1.close()
+
diff --git a/web/newsfeed.py b/web/newsfeed.py
new file mode 100755
index 0000000..5840ca4
--- /dev/null
+++ b/web/newsfeed.py
@@ -0,0 +1,101 @@
+#! /usr/bin/env python
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2018 The Mana World 2
+# Author: Jonatas N. (Jesusalva)
+
+import datetime
+date=str(datetime.date.today())
+fulldate=str(datetime.datetime.utcnow().isoformat())
+
+#date="2020-01-31"
+#fulldate="2020-01-31T11:45:20.543750"
+
+# Open file (Dt) and export to "old". (Src) is current news
+dt=open("nf_main.xml", "r")
+old=[]
+for line in dt:
+ old.append(line)
+dt.close()
+dt=open("nf_main.xml", "w")
+src=open("../update/news.txt", "r")
+
+
+# Read news and prepare header
+ns=open("../update/news.txt", "r")
+
+
+# Function to markup it, and strip new lines
+def markup(r):
+ r=r.replace('##0', '')
+ r=r.replace('##1', '')
+ r=r.replace('##2', '')
+ r=r.replace('##3', '')
+ r=r.replace('##4', '')
+ r=r.replace('##5', '')
+ r=r.replace('##6', '')
+ r=r.replace('##7', '')
+ r=r.replace('##8', '')
+ r=r.replace('##9', '')
+ r=r.replace('##B', '<b>')
+ r=r.replace('##b', '</b>')
+ r=r.replace('[@@', '<a href="')
+ r=r.replace('|', '">')
+ r=r.replace('@@]', '</a>')
+ return r
+def nn(r):
+ return r.replace('\n', '')
+
+# Write the news, and close that file
+dt.write(' <entry>\n')
+for i in src:
+ if ('##0 Actual Release: ##1' in i):
+ dt.write('\
+ <title>'+nn(i.replace('##0 Actual Release: ##1',''))+'</title>\n\
+ <link href="https://tmw2.org/news#'+date+'"/>\n\
+ <updated>'+fulldate+'</updated>\n\
+ <id>tag:tmw2.org,'+date+'</id>\n\
+ <content type="html"><![CDATA[')
+ else:
+ dt.write('<p>'+markup(i)+'</p>\n')
+dt.write("]]></content>\n </entry>")
+
+dt.write('\n\n')
+
+for i in old:
+ dt.write(i)
+
+src.close()
+dt.close()
+
+# Open main file as (Dt) and read from (Ns)
+dt=open("feed.xml", "w")
+ns=open("nf_main.xml", "r")
+
+# headers
+dt.write('\
+<?xml version="1.0" encoding="utf-8"?>\n\
+<feed xmlns="http://www.w3.org/2005/Atom">\n\
+ <title>TMW2</title>\n\
+ <link href="https://tmw2.org/feed.xml" rel="self"/>\n\
+ <link href="https://tmw2.org"/>\n\
+')
+dt.write(' <updated>'+fulldate+'</updated>\n')
+dt.write('\
+ <id>https://tmw2.org</id>\n\
+ <author>\n\
+ <name>TMW2 Project</name>\n\
+ <email>admin@tmw2.org</email>\n\
+ </author>\n\
+')
+
+# Write data
+for i in ns:
+ dt.write(i)
+
+# close
+dt.write('\n</feed>')
+
+dt.close()
+ns.close()
+
diff --git a/web/nf_main.xml b/web/nf_main.xml
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/web/nf_main.xml
@@ -0,0 +1 @@
+
diff --git a/web/oldupdatelang.py b/web/oldupdatelang.py
new file mode 100755
index 0000000..e7a367a
--- /dev/null
+++ b/web/oldupdatelang.py
@@ -0,0 +1,164 @@
+#! /usr/bin/env python3
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2018 TMW-2
+# Author: Jesusalva
+
+import os
+
+defaultLang = "en"
+rootPath = "../../web/"
+
+langs=[]
+files={}
+originals={}
+tm=["","\n"]
+
+def invalidline(line, filx="none"):
+ return (line.startswith("include ") or
+ line.startswith("<?php") or
+ line.startswith("?>") or
+ "<div " in line or
+ "</div>" in line or
+ "iframe" in line or
+ "header" in filx or
+ (line.startswith(" <p>") and line.endswith("<p>\n")) or
+ (line.startswith(" </p>") and line.endswith("</p>\n")) or
+ (line.startswith(" <pre><code>") and line.endswith("<code>\n")) or
+ (line.startswith(" </code></pre>") and line.endswith("</pre>\n")) or
+ line.replace('\n','') == "")
+
+# Populates the basic arrays
+def populate():
+ # Populate langs
+ o=open("langs.txt", "r")
+ for i in o:
+ langs.append(i.replace('\n',''))
+ o.close()
+
+ # Populate files
+ for i in os.listdir(rootPath):
+ if i.endswith(".php.en"):
+ print("Valid file: "+str(i))
+ files[i]={}
+ for x in langs:
+ files[i][x]={}
+
+ #print(str(files))
+
+
+# Creates/Loads stuff
+def genreadPoFile():
+ for a in langs:
+ print("Reading translations for "+a)
+ for i in files:
+ readPoFile(i,a)
+
+def generatePoFiles():
+ global tm
+ context=langs
+ context.append('en')
+ for a in langs:
+ print("Updating po file for "+a)
+ xrv=open("po/"+a+".po", "w")
+ # Prints header
+ xrv.write('\
+# Copyright (C) 2018 TMW2\n\
+#\n\
+\n\
+msgid ""\n\
+msgstr ""\n\
+"Project-Id-Version: TMW2\\n"\n\
+"MIME-Version: 1.0\\n"\n\
+"Content-Type: text/plain; charset=UTF-8\\n"\n\
+"Content-Transfer-Encoding: 8bit\\n"\n\
+\n\
+')
+ xrv.close()
+
+ tm=[""]
+ for i in files:
+ writePoFile(i,a)
+ context.remove('en')
+
+def generateLocal():
+ for a in langs:
+ print("generating local file for "+a)
+ for i in files:
+ writeLocal(i,a)
+
+
+
+# Write po files. TODO: WARNING: Some translations are getting overriden!
+def writePoFile(nf,lg):
+ # Translation Memory, to prevent duplicates
+ global tm
+
+ a=open("po/"+lg+".po", "a")
+ f=open(rootPath+nf, "r")
+
+ for line in f:
+ if line in tm or invalidline(line, nf):
+ continue
+ else:
+ tm.append(line)
+
+ a.write("#, no-c-format\n")
+ a.write('msgid "%s"\n' % line.replace('\n', '').replace('"','\\"'))
+ try:
+ if files[nf][lg][line.replace('\n', '')] != line.replace('\n', '') and files[nf][lg][line.replace('\n', '')] != "":
+ a.write('msgstr "%s"\n' % files[nf][lg][line.replace('\n', '')].replace('\n','').replace('"','\\"'))
+ else:
+ a.write('msgstr "%s"\n' % "")
+ except KeyError:
+ a.write('msgstr "%s"\n' % "")
+
+ a.write('\n')
+
+ a.close()
+
+
+# Reads Po Files
+def readPoFile(nf, lg):
+ try:
+ a=open("po/"+lg+".po", "r")
+ except:
+ a=open("po/"+lg+".po", "w")
+ a.close()
+ a=open("po/"+lg+".po", "r")
+
+ ctx=""
+ for line in a:
+ if "msgid " in line:
+ ctx=line.replace('"\n','').replace('\\"','"').replace('msgid "', "")
+ if "msgstr " in line and ctx != "":
+ if line != 'msgstr ""\n':
+ files[nf][lg][ctx]=line.replace('"\n', '').replace('\\"','"').replace('msgstr "', "")
+ else:
+ files[nf][lg][ctx]=ctx
+ ctx=""
+
+ a.close()
+
+def writeLocal(nf, lg):
+ a=open(rootPath+nf[:-2]+lg, 'w')
+ b=open(rootPath+nf, 'r')
+
+ for line in b:
+ try:
+ a.write(files[nf][lg][line.replace('\n', '')])
+ a.write('\n')
+ except KeyError:
+ a.write(line)
+
+ b.close()
+ a.close()
+
+# Mainframe: populate arrays
+populate()
+
+# Mainframe: handle PO files
+genreadPoFile()
+generatePoFiles()
+generateLocal()
+
diff --git a/web/original.po b/web/original.po
new file mode 100644
index 0000000..f27e280
--- /dev/null
+++ b/web/original.po
@@ -0,0 +1,170 @@
+# Copyright (C) 2018 TMW2
+#
+# Translators:
+# Jesusaves <cpntb1@ymail.com>, 2018
+msgid ""
+msgstr ""
+"Project-Id-Version: TMW2\n"
+"Last-Translator: Jesusaves <cpntb1@ymail.com>, 2018\n"
+"Language-Team: Portuguese (Brazil) (https://www.transifex.com/akaras/teams/959/pt_BR/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pt_BR\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#, no-c-format
+msgid "TMW-2: Monster World | Index Page"
+msgstr "TMW-2: Mundo dos Monstros | Página Inicial"
+
+#, no-c-format
+msgid " <h2>TMW-2: Monster Wars</h2>"
+msgstr "<h2>TMW-2: Guerra dos Monstros</h2>"
+
+#, no-c-format
+msgid ""
+" <p>The Monster World (2) is an open source MMORPG project developed by "
+"the community and for the community. We believe that players should be "
+"rewarded for merit, and not for game time or luck. TMW2 runs in the ManaPlus"
+" client, also opensource, and our game have its code database licensed under"
+" GPL 3 and resources (GFX,SFX,etc) typically under CC-BY-SA, although GPL "
+"content is also present.</p>"
+msgstr ""
+"<p>O Mundo dos Monstros (2) é um MMORPG de código aberto, desenvolvido pela "
+"comunidade e para comunidade. Acreditamos que os jogadores devem ser "
+"recompensados por mérito, e não por tempo de jogo ou sorte. TMW2 roda no "
+"cliente ManaPlus, também de código aberto, e nosso jogo tem seu código na "
+"licença GPL 3 e recursos (GFX, SFX, etc) tipicamente sobre CC-BY-SA, embora "
+"conteúdo GPL também esteja presente.</p>"
+
+#, no-c-format
+msgid ""
+" <p>In this game, the player is given the opportunity to permanently "
+"change the world and its story, affecting all players forever, mainly by "
+"events and quests. The players are free to decide how they want the world to"
+" develop, and their actions change the storyline, besides always being able "
+"to join the Development Team.</p>"
+msgstr ""
+"<p>Nesse jogo, o jogador recebe a oportunidade de permanentemente modificar "
+"o mundo e sua história, afetando todos os jogadores para sempre, "
+"principalmente por eventos e quests. Os jogadores são livres para decidir "
+"como querem que o mundo se desenvolva, e suas ações mudam a história, além "
+"de sempre serem capazes de se unir a Equipe de Desenvolvimento.</p>"
+
+#, no-c-format
+msgid "TMW-2: Monster World | Download Page"
+msgstr "TMW-2: Mundo dos Monstros | Página de Download"
+
+#, no-c-format
+msgid " <h2>Downloading the Game</h2>"
+msgstr " <h2>Baixando o Jogo</h2>"
+
+#, no-c-format
+msgid " <h3>Downloading the Client</h3>"
+msgstr " <h3>Baixando o Cliente</h3>"
+
+#, no-c-format
+msgid " <p>"
+msgstr " <p>"
+
+#, no-c-format
+msgid ""
+" The first step is to download <a href=https://manaplus.org "
+"rel=\"nofollow\">ManaPlus</a>,"
+msgstr ""
+" O primeiro passo é baixar o <a href=https://manaplus.org "
+"rel=\"nofollow\">ManaPlus</a>,"
+
+#, no-c-format
+msgid ""
+" our game client. If you download from a repository, ensure the "
+"version is"
+msgstr ""
+"nosso cliente de jogo. Se você baixar de um repositório, certifique-se que a"
+" versão é"
+
+#, no-c-format
+msgid " 1.7.x or greater. Older versions may present bugs."
+msgstr "1.7.x ou superior. Versões mais antigas podem apresentar erros."
+
+#, no-c-format
+msgid " </p>"
+msgstr " </p>"
+
+#, no-c-format
+msgid " <h3>Adding the Server</h3>"
+msgstr "<h3>Adicionando o Servidor</h3>"
+
+#, no-c-format
+msgid ""
+" Once the game opens, a server list will show. Select “Add”. Insert a"
+" server"
+msgstr ""
+"Quando o jogo abrir, uma lista de servidores será exibida. Clique em "
+"\"Adicionar\". Insira um"
+
+#, no-c-format
+msgid " name and description. The important data is as follow:"
+msgstr "nome de servidor e descrição. Os dados importantes são esses:"
+
+#, no-c-format
+msgid " <pre><code>"
+msgstr " <pre><code>"
+
+#, no-c-format
+msgid " <b>Address:</b> server.tmw2.org"
+msgstr " <b>Endereço:</b> server.tmw2.org"
+
+#, no-c-format
+msgid " <b>Port:</b> 6901"
+msgstr "<b>Porta:</b> 6901"
+
+#, no-c-format
+msgid " <b>Type:</b> Evol2"
+msgstr "<b>Tipo:</b> Evol2"
+
+#, no-c-format
+msgid " </code></pre>"
+msgstr "</code></pre>"
+
+#, no-c-format
+msgid ""
+" You'll need to register before playing the game. Please use a valid "
+"email"
+msgstr ""
+"Você precisará se registrar antes de jogar. Por favor utilize um endereço de"
+" email válido"
+
+#, no-c-format
+msgid ""
+" address as if we need to email your password back to you, we will do"
+" using"
+msgstr "já que se precisarmos lhe enviar a sua senha de volta, o faremos"
+
+#, no-c-format
+msgid " that email."
+msgstr "utilizando esse email."
+
+#, no-c-format
+msgid " <h3>Troubleshooting</h3>"
+msgstr "<h3>Solução de Problemas</h3>"
+
+#, no-c-format
+msgid ""
+" Game official release is 2018-03-02. If we are past this date and "
+"you're"
+msgstr ""
+"O lançamento oficial do jogo é 2018-03-02. Se essa data já passou e você"
+
+#, no-c-format
+msgid ""
+" getting a “servers unavailable” message, or is otherwise unable to "
+"connect,"
+msgstr ""
+"está recebendo uma mensagem de \"servidores indisponíveis\", ou não consegue"
+" se conectar,"
+
+#, no-c-format
+msgid " please contact us so we may fix this issue."
+msgstr ""
+"por favor entre em contato conosco para que possamos corrigir esse problema."
diff --git a/web/template.po b/web/template.po
new file mode 100644
index 0000000..16ce842
--- /dev/null
+++ b/web/template.po
@@ -0,0 +1,84 @@
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: 1.0\n"
+"Report-Msgid-Bugs-To: dev@tmw2.org\n"
+"POT-Creation-Date: 2020-02-28 05:43-0300\n"
+"PO-Revision-Date: 2020-02-28 05:43-0300\n"
+"Last-Translator: TMW2 Team <dev@tmw2.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "a string for testing"
+msgstr "uma linha para testar"
+
+msgid "$s Online Players"
+msgstr "$s Jogadores Online"
+
+msgid "Home"
+msgstr "Início"
+
+msgid "Community"
+msgstr "Comunidade"
+
+msgid "News"
+msgstr "Notícias"
+
+msgid "Legal Notice"
+msgstr "Nota Legal"
+
+msgid "Wiki & Info"
+msgstr "Wiki & Info"
+
+msgid "Get involved"
+msgstr "Se Involva"
+
+msgid "Making Art"
+msgstr "Pixelart"
+
+msgid "MapMaking"
+msgstr "Fazendo Mapas"
+
+msgid "Programming"
+msgstr "Programação"
+
+msgid "Game Content"
+msgstr "Conteúdo de Jogo"
+
+msgid "Music and SFX"
+msgstr "Música e SFX"
+
+msgid "Item Viewer"
+msgstr "Visualizador de Itens"
+
+msgid "Page not found"
+msgstr "Página não encontrada"
+
+msgid "Get in touch with the community!"
+msgstr "Entre em contato com a comunidade!"
+
+msgid ""
+"We have a great community that expands over many platforms with people from all over the world. If you have a question or you need help with something don't hesitate to ask us.\n"
+"Come visit us, and bring (or make) friends!"
+msgstr ""
+"We have a great community that expands over many platforms with people from all over the world. If you have a question or you need help with something don't hesitate to ask us.\n"
+"Come visit us, and bring (or make) friends!"
+
+msgid "Social Platforms"
+msgstr "Plataformas Sociais"
+
+msgid "Development"
+msgstr "Desenvolvimento"
+
+msgid "Support us"
+msgstr "Apoie-nos"
+
+msgid "You want to contribute in a way not listed above?"
+msgstr "Você quer contribuir de uma maneira não listada acima?"
+
+msgid "No problem!"
+msgstr "Sem problemas!"
+
+msgid "Reach out to us and tell us about it."
+msgstr "Entre em contato conosco e nos fale sobre."
diff --git a/web/updatelang.py b/web/updatelang.py
new file mode 100755
index 0000000..2eba12c
--- /dev/null
+++ b/web/updatelang.py
@@ -0,0 +1,190 @@
+#! /usr/bin/env python3
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2018~2020 TMW-2
+# Author: Jesusalva
+# Note: This will "force" completion to 100%. Use review system!
+
+import copy, datetime
+import polib, yaml
+
+defaultLang = "en"
+rootPath = "../../site/i18n/"
+
+langs=[]
+files={}
+originals={}
+tm={defaultLang: ["","\n"]}
+
+# Search for array[?]==search in an array of dicts
+# Returns the dictionary, or returns "ERROR"
+def dl_search(array, search):
+ for key in array.keys():
+ if array[key] == search:
+ return key
+ raise Exception("Key not found")
+
+# For headers
+def currentdate():
+ dt=datetime.datetime.now()
+
+ day=dt.timetuple()[2]
+ month=dt.timetuple()[1]
+ year=dt.timetuple()[0]
+
+ hour=dt.timetuple()[3]
+ minute=dt.timetuple()[4]
+ #second=5
+ #weekday=6
+ #yearday=7
+
+ return "%04d-%02d-%02d %02d:%02d-0300" % (year, month, day, hour, minute)
+
+
+def init():
+ global defaultLang, rootPath, langs, files, originals, tm
+ # Populate langs
+ o=open("langs.txt", "r")
+ for i in o:
+ langs.append(i.replace('\n',''))
+ o.close()
+
+ # Create the original file
+ f=open(rootPath+defaultLang+".yml", "r")
+ originals=yaml.load(f, Loader=yaml.FullLoader) # Dictionary
+ f.close()
+
+ # We don't care with metadata
+ del originals["language_meta"]
+
+ # Create each language's template
+ for i in langs:
+ files[str(i)]=copy.copy(originals)
+ tm[str(i)]=["","\n"]
+
+ """
+ # Do the reading for each language
+ for i in langs:
+ f=open(rootpath+i+".yml", "r")
+ files[str(i)]=yaml.load(f, Loader=yaml.FullLoader) # Dictionary
+ f.close()
+ """
+
+# [OK] Returns name from language code
+def lgname(lg):
+ if lg == "en":
+ return "English"
+ elif lg == "pt-BR":
+ return "Português (Brasil)"
+ elif lg == "de":
+ return "Deutsch"
+ elif lg == "fr":
+ return "François"
+ elif lg == "es":
+ return "Español"
+ elif lg == "ru":
+ return "Русский"
+ else:
+ return "Unknown %s" % lg
+
+# [OK] Entry
+def poentry(org, ttl, comments):
+ return polib.POEntry(msgid=org, msgstr=ttl, comment=comments)
+
+# Creates/Loads stuff
+def generatePoFiles():
+ global tm
+ context=copy.copy(langs)
+ context.append('en')
+ for lg in context:
+ print("Updating po file for "+lg)
+ po=polib.POFile()
+ po.metadata = {
+ 'Project-Id-Version': '1.0',
+ 'Report-Msgid-Bugs-To': 'dev@tmw2.org',
+ 'POT-Creation-Date': currentdate(),
+ 'PO-Revision-Date': currentdate(),
+ 'Last-Translator': 'TMW2 Team <dev@tmw2.org>',
+ 'MIME-Version': '1.0',
+ 'Content-Type': 'text/plain; charset=utf-8',
+ 'Content-Transfer-Encoding': '8bit',
+ }
+
+ for key, speech in originals.items():
+ # Translation Memory (no duplicates)
+ if speech in tm[lg]:
+ continue
+ tm[lg].append(speech)
+
+ # Add to po file
+ if lg == "en":
+ po.append(poentry(speech, "", key))
+ else:
+ po.append(poentry(speech, files[lg][key], key))
+
+ po.save("po/%s.po" % lg)
+ context.remove('en')
+
+
+
+
+
+# [OK] Reads Po Files
+def readPoFile(lg):
+ try:
+ po=polib.pofile("po/"+lg+".po")
+ except:
+ a=open("po/"+lg+".po", "w")
+ a.close()
+ print("%s.po does not exist, not reading" % lg)
+ return
+
+ print("%s Progress: %d%%" % (lgname(lg), po.percent_translated()))
+
+ for entry in po:
+ #print("%s = %s" % (entry.msgid, entry.msgstr))
+
+ try:
+ if entry.msgstr != "":
+ dest=dl_search(files[lg], entry.msgid)
+ #print("[+] %s" % dest)
+ files[lg][dest]=entry.msgstr
+ else:
+ dest=dl_search(files[lg], entry.msgid)
+ #print("%s.%s IS EMPTY" % (lg, dest))
+ try:
+ files[lg][dest]=originals[dest]
+ except:
+ print("%s - failed to obtain originals" % entry.msgid)
+ except:
+ print("%s - string was removed" % entry.msgstr)
+
+# [OK] Save the new YML file
+def writeLocal(lg):
+ f=open(rootPath+lg+".yml", 'w')
+ f.write("# THIS FILE WAS GENERATED AUTOMATICALLY\n#EDITING IT WILL HAVE NO EFFECT\n\n")
+ files[lg]["language_meta"]={"code": lg, "name": lgname(lg)}
+ yaml.dump(files[lg], f)
+ f.close()
+
+
+
+
+
+
+
+
+
+
+
+# Mainframe: setup
+init()
+
+# Save translations to their YML files
+for lg in langs:
+ readPoFile(lg)
+ writeLocal(lg)
+
+# Mainframe: handle PO files
+generatePoFiles()
+
diff --git a/wiki/elegen.py b/wiki/elegen.py
new file mode 100755
index 0000000..e8385f0
--- /dev/null
+++ b/wiki/elegen.py
@@ -0,0 +1,426 @@
+#! /usr/bin/env python3
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2010-2011 Evol Online
+# Copyright (C) 2018 TMW-2
+# Author: Andrei Karas (4144)
+# Author: Jesusalva
+
+import datetime
+
+wikib=open("EleMonsters.html", "w")
+wikib.write('<html><head><meta charset=utf8 /><title>EleGen File</title></head><body>')
+totalid=0
+bifs=False
+
+def printSeparator():
+ print("--------------------------------------------------------------------------------")
+
+def showHeader():
+ print("TMW2 Ele Generator")
+ print("This stuff analyzes and sorts mobs elementals, races and walkmasks.")
+ ##print "Evol client data validator."
+ print("Run at: " + datetime.datetime.now().isoformat())
+ ##print "https://gitlab.com/evol/evol-tools/blob/master/testxml/testxml.py"
+ printSeparator()
+
+def showFooter():
+ #pass
+ #printSeparator()
+ print("Done.")
+
+
+
+Mobs0=[]
+Mobs1=[]
+Mobs2=[]
+Mobs3=[]
+Mobs4=[]
+Mobs5=[]
+Mobs6=[]
+Mobs7=[]
+Mobs8=[]
+Mobs9=[]
+MobsA=[]
+
+SysDrops=[]
+
+def fwalk(wmask):
+ if wmask == 'WATER':
+ return '<font color=#00f>%s</font>' % (wmask)
+ elif wmask == 'AIR':
+ return '<font color=#093>%s</font>' % (wmask)
+ elif wmask == 'WALL':
+ return '<font color=#f00>%s</font>' % (wmask)
+ elif wmask == 'NORMAL' or wmask == 'DEFAULT':
+ return '<font color=#111>%s</font>' % (wmask)
+ else:
+ print("Invalid walk mask: "+wmask)
+ exit(1)
+
+def WhatRace(rac):
+ rc=rac.race
+ if rc == 0:
+ return "Formless"
+ elif rc == 1:
+ return "Undead"
+ elif rc == 2:
+ return "Brute"
+ elif rc == 3:
+ return "Plant"
+ elif rc == 4:
+ return "Insect"
+ elif rc == 5:
+ return "Mineral"
+ elif rc == 6:
+ return "Evil"
+ elif rc == 7:
+ return "SemiHuman"
+ elif rc == 8:
+ return "Angel"
+ elif rc == 9:
+ return "Dragon"
+ elif rc == 10:
+ return "Player"
+ elif rc == 11:
+ return "Boss"
+ elif rc == 12:
+ return "NonBoss"
+ elif rc == 14:
+ return "NonSemiHuman"
+ elif rc == 15:
+ return "NonPlayer"
+ elif rc == 16:
+ return "SemiPlayer"
+ elif rc == 17:
+ return "NonSemiPlayer"
+ else:
+ print("ERROR, INVALID RACE ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+
+def WhatElem(rac):
+ rc=rac.elem
+ tl="ERROR"
+ cl="#F00"
+ if rc == 0:
+ tl,cl="Neutral","#000"
+ elif rc == 1:
+ tl,cl="Water","#00F"
+ elif rc == 2:
+ tl,cl="Earth","#7A0"
+ elif rc == 3:
+ tl,cl="Fire","#F00"
+ elif rc == 4:
+ tl,cl="Wind","#093"
+ elif rc == 5:
+ tl,cl="Nature","#040"
+ elif rc == 6:
+ tl,cl="Holy","#afa"
+ elif rc == 7:
+ tl,cl="Dark","#908"
+ elif rc == 8:
+ tl,cl="Ghost","#404"
+ elif rc == 9:
+ tl,cl="Undead","#440"
+ else:
+ print("ERROR, INVALID ELEM ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+ return "<font color=%s>%s</font>" % (cl, tl)
+
+class Mob:
+ def __init__(self):
+ # Basic
+ self.id="0"
+ #self.aegis="UnknownMonster" # SpriteName is not used anywhere, we are using its ID
+ self.name="Unknown Monster Name"
+ self.view="1"
+
+ # Defensive
+ self.mobpt="0" # Mob Points “Level”
+ self.hp="0"
+ self.xp="0"
+ self.jp="0"
+ self.st=""
+
+ # Offensive
+ self.atk="[0, 0]"
+ self.range="0"
+ self.move="0"
+ self.delay="0"
+ self.drops=[]
+
+ # New
+ self.race=-1
+ self.elem=-1
+ self.elel=-1
+ self.walk="NORMAL"
+
+def MobAlloc(ab):
+ try:
+ maab=int(ab.elem)
+ except:
+ maab=9901
+
+ if maab == 0:
+ Mobs0.append(ab)
+ elif maab == 1:
+ Mobs1.append(ab)
+ elif maab == 2:
+ Mobs2.append(ab)
+ elif maab == 3:
+ Mobs3.append(ab)
+ elif maab == 4:
+ Mobs4.append(ab)
+ elif maab == 5:
+ Mobs5.append(ab)
+ elif maab == 6:
+ Mobs6.append(ab)
+ elif maab == 7:
+ Mobs7.append(ab)
+ elif maab == 8:
+ Mobs8.append(ab)
+ elif maab == 9:
+ Mobs9.append(ab)
+ else:
+ MobsA.append(ab)
+
+def testMobs():
+ global totalid
+ print("Generating Elem-Mob Wiki...")
+ src=open("../../serverdata/db/re/mob_db.conf", "r")
+ wikib.write("<h1 id=#x>EleGen Monster Database</h1>\n")
+ start=False
+ dropper=False
+ x=Mob() # Only for pyflakes2
+
+ for a in src:
+ if a == "{\n":
+ if start and not bifs and ((int(x.hp) <= 50) or (x.race == 3)):
+ start=False
+
+ if start:
+ MobAlloc(x)
+ else:
+ start=True
+ x=Mob()
+
+ if " Id:" in a:
+ x.id=stp(a)
+ totalid+=1
+ if x.id == "ID":
+ continue
+
+ if " Name:" in a:
+ x.name=stp(a)
+ elif " Hp:" in a:
+ x.hp=stp(a)
+ elif " Lv:" in a:
+ x.mobpt=stp(a)
+ elif " Exp:" in a:
+ x.xp=stp(a)
+ elif " JExp:" in a:
+ x.jp=stp(a)
+ elif " Attack:" in a:
+ x.atk=stp(a)
+ elif " AttackRange:" in a:
+ x.range=stp(a)
+ elif " MoveSpeed:" in a:
+ x.move=stp(a)
+ elif " ViewRange:" in a:
+ x.view=stp(a)
+ elif " AttackDelay:" in a:
+ x.delay=stp(a)
+ elif " Looter: true" in a:
+ x.st+="<font color=#790>Lot</font>,"
+ elif " Assist: true" in a:
+ x.st+="<font color=#0a0>Ass</font>,"
+ elif " Aggressive: true" in a:
+ x.st+="<font color=#f00>Agr</font>,"
+ elif " WalkMask:" in a:
+ x.walk=stp(a)
+ elif " Element:" in a:
+ tmp=stp(a)
+ tmp2=tmp.split(',')
+ try:
+ x.elem=int(tmp2[0])
+ x.elel=int(tmp2[1])
+ except:
+ print("Invalid Element for mob %s: %s" % (x.id, tmp))
+ exit(1)
+ elif " Race:" in a:
+ try:
+ x.race=int(stp(a))
+ except:
+ print("Invalid Race for mob %s: %s" % (x.id, a))
+ exit(1)
+ elif 'Drops: ' in a:
+ dropper=True
+ elif dropper and '}' in a:
+ dropper=False
+ elif dropper:
+ x.drops.append(stp(a).split(": "))
+ # Write last entry
+ MobAlloc(x)
+
+ writeMob()
+
+ wikib.write('\n\n|Mode|Desc|\n|----|----|\n')
+ wikib.write('|Lot|Looter|\n')
+ wikib.write('|Ass|Assist|\n')
+ wikib.write('|Agr|Aggressive|\n')
+
+ src.close()
+
+def stp(x):
+ return x.replace('\n', '').replace('|', '').replace('(int, defaults to ', '').replace(')', '').replace('basic experience', '').replace('"','').replace(" ","").replace("\t","").replace('(string', '').replace('Name: ','').replace('Element: ','').replace('Race: ','').replace('AttackDelay: ', '').replace('WalkMask: ','').replace('MoveSpeed: ', '').replace('AttackRange: ', '').replace('ViewRange: ','').replace('Attack: ','').replace('ViewRange: ','').replace('Hp: ','').replace('Id: ','').replace('Lv: ','').replace('view range','').replace('attack range','').replace('move speed','').replace('health','').replace('(int','').replace('attack delay','atk.').replace('(','').replace(')','').replace('WALK_','')
+
+
+def MonsterWrite(tbl):
+ # TODO: Check _mobs files to determine the usual monster density (a misc info to aid adding proper drop specs)
+ wikib.write("<table border=1>\n")
+ wikib.write("<tr><th>ID</th><th>Name</th><th>Mob Info</th><th>Elegen</th><th>Misc Info</th><th>Rewards</th><th>Drops</th></tr>\n")
+ for i in sorted(tbl, key=lambda tbl: int(tbl.mobpt)):
+ if i.id == 'ID':
+ continue
+ wikib.write('<tr><td><a name="' + i.id + '"></a>' +
+ i.id +"</td><td>"+
+ i.name +"</td><td>"+
+ mb_core(i) +"</td><td>"+
+ mb_eleg(i) +"</td><td>"+
+ mbdt('misc', mb_rdmisc(i)) +"</td><td>"+
+ mbdt('Exp\'s', mb_rdrw(i)) +"</td><td>"+
+ mbdt('drops', mb_rddrop(i)) +"</td></tr>\n"
+ )
+ wikib.write("</table>\n")
+ wikib.write("\n<a href=#x>(↑) Return to top</a><br/><br/>\n\n")
+
+def writeMob():
+ wikib.write("Total monsters: %d<br/>" % totalid)
+ wikib.write("<ul>\
+<li><a href=#0>Neutral</a></li>\n\
+<li><a href=#1>Water</a></li>\n\
+<li><a href=#2>Earth</a></li>\n\
+<li><a href=#3>Fire</a></li>\n\
+<li><a href=#4>Wind</a></li>\n\
+<li><a href=#5>Nature (Poison)</a></li>\n\
+<li><a href=#6>Holy</a></li>\n\
+<li><a href=#7>Dark (Shadow)</a></li>\n\
+<li><a href=#8>Ghost</a></li>\n\
+<li><a href=#9>Undead</a></li>\n\
+<li><a href=#Nan>-</a></li>\n\
+<li><a href=Elements><font color=#f00>Elemental Reference</font></a></li>\n\
+</ul> ")
+
+ wikib.write("<h1 id=0>Neutral</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs0))
+ MonsterWrite(Mobs0)
+ wikib.write("<h1 id=1>Water</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs1))
+ MonsterWrite(Mobs1)
+ wikib.write("<h1 id=2>Earth</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs2))
+ MonsterWrite(Mobs2)
+ wikib.write("<h1 id=3>Fire</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs3))
+ MonsterWrite(Mobs3)
+ wikib.write("<h1 id=4>Wind</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs4))
+ MonsterWrite(Mobs4)
+ wikib.write("<h1 id=5>Nature</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs5))
+ MonsterWrite(Mobs5)
+ wikib.write("<h1 id=6>Holy</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs6))
+ MonsterWrite(Mobs6)
+ wikib.write("<h1 id=7>Dark</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs7))
+ MonsterWrite(Mobs7)
+ wikib.write("<h1 id=8>Ghost</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs8))
+ MonsterWrite(Mobs8)
+ wikib.write("<h1 id=9>Undead</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs9))
+ MonsterWrite(Mobs9)
+
+ wikib.write("<h1 id=NaN>Error</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(MobsA))
+ MonsterWrite(MobsA)
+
+
+def mbdt(summary, content):
+ return "<pre>%s</pre>" % (content)
+ return "<details>\
+<summary>"+summary+"</summary>\
+<pre>"+content+"</pre></details>"
+
+def mb_core(mb):
+ buff=""
+ buff+="Lvl: %s<br/>\n" % (mb.mobpt)
+ buff+="HP: <b><font color=#0a0>%s</font></b><br/>\n" % (mb.hp)
+ buff+="ATK: <b><font color=#a00>%s</font></b><br/>\n" % (mb.atk)
+ if mb.st != "":
+ buff+="Modes: <i>%s</i>" % (mb.st)
+ return buff
+
+def mb_eleg(mb):
+ buff=""
+ buff+="Race: %s<br/>\n" % (WhatRace(mb))
+ buff+="Walk: %s<br/>\n" % (fwalk(mb.walk))
+ buff+="Element: %s<br/>\n" % (WhatElem(mb))
+ return buff
+
+def mb_rdmisc(mb):
+ buff=""
+ if "agr" in mb.st.lower():
+ buff+="View Range: %s\n" % (mb.view)
+ buff+="Attack Range: %s\n" % (mb.range)
+ buff+="AtkDelay: %s ms\n" % (mb.delay)
+ buff+="Move speed: %s ms\n" % (mb.move)
+ buff+="Element Level: %d\n" % (mb.elel)
+ return buff
+
+def mb_rdrw(mb):
+ buff=""
+ try:
+ buff+="MobPoints: %d\n" % (int(mb.mobpt)*11/10)
+ except:
+ pass
+ buff+="%s\n" % (mb.xp)
+ buff+="%s\n" % (mb.jp)
+ return buff
+
+def mb_rddrop(mb):
+ buff=""
+ for ax in mb.drops:
+ # Ignore disabled drops
+ if ax[0].startswith("//"):
+ continue
+
+ # Write drop
+ try:
+ buff+=ax[0]+': ' + str(int(ax[1])/100.0) + ' %\n'
+ except IndexError:
+ print("Fatal: invalid %s mob with %s drops" % (mb.name, str(ax)))
+ exit(1)
+ except:
+ print("[Warning] %s incorrect drop: %s" % (mb.name, str(ax)))
+ buff+=ax[0]+': ' + ax[1] + ' ppm\n'
+
+ # Save to SysDrops
+ SysDrops.append([ax[0], ax[1], mb.name])
+
+ return buff
+
+
+
+showHeader()
+
+testMobs()
+print("Total monsters: %d" % totalid)
+
+wikib.write('</body></html>')
+wikib.close()
+#print(str(SysDrops))
+
+showFooter()
+exit(0)
diff --git a/wiki/lanalysis.py b/wiki/lanalysis.py
new file mode 100755
index 0000000..bc2ffe2
--- /dev/null
+++ b/wiki/lanalysis.py
@@ -0,0 +1,394 @@
+#! /usr/bin/env python3
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2010-2011 Evol Online
+# Copyright (C) 2018 TMW-2
+# Author: Andrei Karas (4144)
+# Author: Jesusalva
+
+import datetime
+import sys
+
+wikib=open("EleMonsters.html", "w")
+wikib.write('<html><head><meta charset=utf8 /><title>EleGen File</title></head><body>')
+
+def printSeparator():
+ print("--------------------------------------------------------------------------------")
+
+def showHeader():
+ print("TMW2 Ele Generator")
+ print("This stuff analyzes and sorts mobs elementals, races and walkmasks.")
+ ##print "Evol client data validator."
+ print("Run at: " + datetime.datetime.now().isoformat())
+ print("Output is: EleMonsters.html")
+ ##print "https://gitlab.com/evol/evol-tools/blob/master/testxml/testxml.py"
+ printSeparator()
+
+def showFooter():
+ #pass
+ #printSeparator()
+ print("Done.")
+
+
+
+Mobs1=[]
+Mobs2=[]
+Mobs3=[]
+Mobs4=[]
+Mobs5=[]
+Mobs6=[]
+MobsA=[]
+
+SysDrops=[]
+
+def fwalk(wmask):
+ if wmask == 'WATER':
+ return '<font color=#00f>%s</font>' % (wmask)
+ elif wmask == 'AIR':
+ return '<font color=#093>%s</font>' % (wmask)
+ elif wmask == 'WALL':
+ return '<font color=#f00>%s</font>' % (wmask)
+ elif wmask == 'NORMAL' or wmask == 'DEFAULT':
+ return '<font color=#111>%s</font>' % (wmask)
+ else:
+ print("Invalid walk mask: "+wmask)
+ exit(1)
+
+def WhatRace(rac):
+ rc=rac.race
+ if rc == 0:
+ return "Formless"
+ elif rc == 1:
+ return "Undead"
+ elif rc == 2:
+ return "Brute"
+ elif rc == 3:
+ return "Plant"
+ elif rc == 4:
+ return "Insect"
+ elif rc == 5:
+ return "Fish"
+ elif rc == 6:
+ return "-"
+ elif rc == 7:
+ return "SemiHuman"
+ elif rc == 8:
+ return "Angel"
+ elif rc == 9:
+ return "Dragon"
+ elif rc == 10:
+ return "Player"
+ elif rc == 11:
+ return "Boss"
+ elif rc == 12:
+ return "NonBoss"
+ elif rc == 14:
+ return "NonSemiHuman"
+ elif rc == 15:
+ return "NonPlayer"
+ elif rc == 16:
+ return "SemiPlayer"
+ elif rc == 17:
+ return "NonSemiPlayer"
+ else:
+ print("ERROR, INVALID RACE ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+
+def WhatElem(rac):
+ rc=rac.elem
+ tl="ERROR"
+ cl="#F00"
+ if rc == 0:
+ tl,cl="Neutral","#000"
+ elif rc == 1:
+ tl,cl="Water","#00F"
+ elif rc == 2:
+ tl,cl="Earth","#7A0"
+ elif rc == 3:
+ tl,cl="Fire","#F00"
+ elif rc == 4:
+ tl,cl="Wind","#093"
+ elif rc == 5:
+ tl,cl="Nature","#040"
+ elif rc == 6:
+ tl,cl="Holy","#afa"
+ elif rc == 7:
+ tl,cl="Dark","#908"
+ elif rc == 8:
+ tl,cl="Ghost","#404"
+ elif rc == 9:
+ tl,cl="Undead","#440"
+ else:
+ print("ERROR, INVALID ELEM ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+ return "<font color=%s>%s</font>" % (cl, tl)
+
+class Mob:
+ def __init__(self):
+ # Basic
+ self.id="0"
+ #self.aegis="UnknownMonster" # SpriteName is not used anywhere, we are using its ID
+ self.name="Unknown Monster Name"
+ self.view="1"
+ self.boss=False
+
+ # Defensive
+ self.mobpt="0" # Mob Points “Level”
+ self.hp="0"
+ self.xp="Exp: 0"
+ self.jp="JExp: 0"
+ self.st=""
+
+ # Offensive
+ self.atk="[0, 0]"
+ self.range="0"
+ self.move="0"
+ self.delay="0"
+ self.drops=[]
+
+ # New
+ self.race=-1
+ self.elem=-1
+ self.elel=-1
+ self.walk="NORMAL"
+
+def MobAlloc(ab):
+ try:
+ maab=int(ab.mobpt)
+ except:
+ maab=9901
+
+ if maab <= 20:
+ Mobs1.append(ab)
+ elif maab <= 40:
+ Mobs2.append(ab)
+ elif maab <= 60:
+ Mobs3.append(ab)
+ elif maab <= 80:
+ Mobs4.append(ab)
+ elif maab <= 100:
+ Mobs5.append(ab)
+ elif maab <= 150:
+ Mobs6.append(ab)
+ else:
+ MobsA.append(ab)
+
+def testMobs():
+ print("Generating Elem-Mob Wiki...")
+ if len(sys.argv) >= 2:
+ src=open(sys.argv[1]+"/db/re/mob_db.conf", "r")
+ else:
+ src=open("../../server-data/db/re/mob_db.conf", "r")
+
+ wikib.write("<h1 id=#x>EleGen Monster Database</h1>\n")
+ start=False
+ dropper=False
+ x=Mob() # Only for pyflakes2
+
+ for a in src:
+ if a == "{\n":
+ if start:
+ MobAlloc(x)
+ else:
+ start=True
+ x=Mob()
+
+ if " Id:" in a:
+ x.id=stp(a)
+ if x.id == "ID":
+ continue
+
+ if " Name:" in a:
+ x.name=stp(a)
+ elif " Hp:" in a:
+ x.hp=stp(a)
+ elif " Lv:" in a:
+ x.mobpt=stp(a)
+ elif " Exp:" in a:
+ x.xp=stp(a)
+ elif " JExp:" in a:
+ x.jp=stp(a)
+ elif " Attack:" in a:
+ x.atk=stp(a)
+ elif " AttackRange:" in a:
+ x.range=stp(a)
+ elif " MoveSpeed:" in a:
+ x.move=stp(a)
+ elif " ViewRange:" in a:
+ x.view=stp(a)
+ elif " AttackDelay:" in a:
+ x.delay=stp(a)
+ elif " Boss: true" in a:
+ x.boss=True
+ elif " Looter: true" in a:
+ x.st+="<font color=#790>Lot</font>,"
+ elif " Assist: true" in a:
+ x.st+="<font color=#0a0>Ass</font>,"
+ elif " Aggressive: true" in a:
+ x.st+="<font color=#f00>Agr</font>,"
+ elif " WalkMask:" in a:
+ x.walk=stp(a)
+ elif " Element:" in a:
+ tmp=stp(a)
+ tmp2=tmp.split(',')
+ try:
+ x.elem=int(tmp2[0])
+ x.elel=int(tmp2[1])
+ except:
+ print("Invalid Element for mob %s: %s" % (x.id, tmp))
+ exit(1)
+ elif " Race:" in a:
+ try:
+ x.race=int(stp(a))
+ except:
+ print("Invalid Race for mob %s: %s" % (x.id, a))
+ exit(1)
+ elif 'Drops: ' in a:
+ dropper=True
+ elif dropper and '}' in a:
+ dropper=False
+ elif dropper:
+ x.drops.append(stp(a).split(": "))
+ # Write last entry
+ MobAlloc(x)
+
+ writeMob()
+
+ wikib.write('\n\n|Mode|Desc|\n|----|----|\n')
+ wikib.write('|Lot|Looter|\n')
+ wikib.write('|Ass|Assist|\n')
+ wikib.write('|Agr|Aggressive|\n')
+
+ src.close()
+
+def stp(x):
+ return x.replace('\n', '').replace('|', '').replace('(int, defaults to ', '').replace(')', '').replace('basic experience', '').replace('"','').replace(" ","").replace("\t","").replace('(string', '').replace('Name: ','').replace('Element: ','').replace('Race: ','').replace('AttackDelay: ', '').replace('WalkMask: ','').replace('MoveSpeed: ', '').replace('AttackRange: ', '').replace('ViewRange: ','').replace('Attack: ','').replace('ViewRange: ','').replace('Hp: ','').replace('Id: ','').replace('Lv: ','').replace('view range','').replace('attack range','').replace('move speed','').replace('health','').replace('(int','').replace('attack delay','atk.').replace('(','').replace(')','').replace('WALK_','')
+
+
+def MonsterWrite(tbl):
+ # TODO: Check _mobs files to determine the usual monster density (a misc info to aid adding proper drop specs)
+ wikib.write("<table border=1>\n")
+ wikib.write("<tr><th>ID</th><th>Name</th><th>Mob Info</th><th>Elegen</th><th>Misc Info</th><th>Rewards</th><th>Drops</th></tr>\n")
+ for i in sorted(tbl, key=lambda tbl: int(tbl.mobpt)):
+ if i.id == 'ID':
+ continue
+ if i.boss:
+ i.name="<b>"+i.name+"</b>"
+ wikib.write('<tr><td><a name="' + i.id + '"></a>' +
+ i.id +"</td><td>"+
+ i.name +"</td><td>"+
+ mb_core(i) +"</td><td>"+
+ mb_eleg(i) +"</td><td>"+
+ mbdt('misc', mb_rdmisc(i)) +"</td><td>"+
+ mbdt('Exp\'s', mb_rdrw(i)) +"</td><td>"+
+ mbdt('drops', mb_rddrop(i)) +"</td></tr>\n"
+ )
+ wikib.write("</table>\n")
+ wikib.write("\n<a href=#x>(↑) Return to top</a><br/><br/>\n\n")
+
+def writeMob():
+ wikib.write("<ul>\
+<li><a href=#0>Starter</a></li>\n\
+<li><a href=#1>Apprentice</a></li>\n\
+<li><a href=#2>Intermediary</a></li>\n\
+<li><a href=#3>Advanced</a></li>\n\
+<li><a href=#4>Expert</a></li>\n\
+<li><a href=#5>Master</a></li>\n\
+<li><a href=#Nan>OoS</a></li>\n\
+</ul> ")
+
+ wikib.write("<h1 id=0>Lv 0-20</h1>\n\n")
+ MonsterWrite(Mobs1)
+ wikib.write("<h1 id=1>Lv 21-40</h1>\n\n")
+ MonsterWrite(Mobs2)
+ wikib.write("<h1 id=2>Lv 41-60</h1>\n\n")
+ MonsterWrite(Mobs3)
+ wikib.write("<h1 id=3>Lv 61-80</h1>\n\n")
+ MonsterWrite(Mobs4)
+ wikib.write("<h1 id=4>Lv 81-100</h1>\n\n")
+ MonsterWrite(Mobs5)
+ wikib.write("<h1 id=5>Lv 101-150</h1>\n\n")
+ MonsterWrite(Mobs6)
+
+ wikib.write("<h1 id=NaN>Lv 101+</h1>\n\n")
+ MonsterWrite(MobsA)
+
+
+def mbdt(summary, content):
+ return "<b>"+summary+"</b><br/><pre>"+content+"</pre>"
+ return "<details>\
+<summary>"+summary+"</summary>\
+<pre>"+content+"</pre></details>"
+
+def mb_core(mb):
+ buff=""
+ buff+="Lvl: %s<br/>\n" % (mb.mobpt)
+ buff+="HP: <b><font color=#0a0>%s</font></b><br/>\n" % (mb.hp)
+ buff+="ATK: <b><font color=#a00>%s</font></b><br/>\n" % (mb.atk)
+ if mb.st != "":
+ buff+="Modes: <i>%s</i>" % (mb.st)
+ return buff
+
+def mb_eleg(mb):
+ buff=""
+ buff+="Race: %s<br/>\n" % (WhatRace(mb))
+ buff+="Walk: %s<br/>\n" % (fwalk(mb.walk))
+ buff+="Element: %s<br/>\n" % (WhatElem(mb))
+ return buff
+
+def mb_rdmisc(mb):
+ buff=""
+ if "agr" in mb.st.lower():
+ buff+="View Range: %s\n" % (mb.view)
+ buff+="Attack Range: %s\n" % (mb.range)
+ buff+="AtkDelay: %s ms\n" % (mb.delay)
+ buff+="Move speed: %s ms\n" % (mb.move)
+ buff+="Element Level: %d\n" % (mb.elel)
+ return buff
+
+def mb_rdrw(mb):
+ buff=""
+ buff+="<font color=#060><u>%s</b></u></font>\n" % (mb.xp.replace(' ', ' <b>'))
+ buff+="<font color=#006><u>%s</b></u></font>\n" % (mb.jp.replace(' ', ' <b>'))
+ try:
+ buff+="MobPoints: %d\n" % (int(mb.mobpt)*11/10)
+ except:
+ pass
+ return buff
+
+def mb_rddrop(mb):
+ buff=""
+ for ax in mb.drops:
+ # Ignore disabled drops
+ if ax[0].startswith("//"):
+ continue
+
+ # Write drop
+ try:
+ buff+=ax[0]+': ' + str(int(ax[1])/100.0) + ' %\n'
+ except IndexError:
+ print("Fatal: invalid %s mob with %s drops" % (mb.name, str(ax)))
+ exit(1)
+ except:
+ print("[Warning] %s incorrect drop: %s" % (mb.name, str(ax)))
+ buff+=ax[0]+': ' + ax[1] + ' ppm\n'
+
+ # Save to SysDrops
+ SysDrops.append([ax[0], ax[1], mb.name])
+
+ return buff
+
+
+
+showHeader()
+
+testMobs()
+
+wikib.write('<br/><hr/>')
+wikib.write("Run at: " + datetime.datetime.now().isoformat())
+wikib.write('</body></html>')
+wikib.close()
+#print(str(SysDrops))
+
+showFooter()
+exit(0)
diff --git a/wiki/redesign.py b/wiki/redesign.py
new file mode 100755
index 0000000..8e94c52
--- /dev/null
+++ b/wiki/redesign.py
@@ -0,0 +1,1050 @@
+#! /usr/bin/env python3
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2010-2011 Evol Online
+# Copyright (C) 2018 TMW-2
+# Author: Andrei Karas (4144)
+# Author: Jesusalva
+#
+# The use of the data
+
+import datetime
+import sys
+
+stgen=True
+aeros=False
+bifs=False
+skipCI=False
+
+wikib=open("EleMonsters.html", "w")
+wikib.write('<html><head><meta charset=utf8 /><title>EleGen File</title></head><body>')
+
+exp=[ 9,16,25,36,77,112,153,200,253,320,
+ 385,490,585,700,830,970,1120,1260,1420,1620,
+ 1860,1990,2240,2504,2950,3426,3934,4474,6889,7995,
+ 9174,10425,11748,13967,15775,17678,19677,21773,28543,34212,
+ 38065,42102,46323,53026,58419,64041,69892,75973,92468,115254,
+ 128692,142784,157528,178184,196300,215198,234879,255341,310188,365914,
+ 402508,442769,487051,535756,589342,648281,713112,784421,862867,949158,
+ 1044076,1148484,1263331,1389671,1528642,1681509,1849671,2034639,2238111,
+ 2461928,
+ 2708132,2978946,3276840,3604530,3964987,4361495,4797656,5277432,5805184,6095442,
+ 6400217,6720231,7056251,7409070,7779531,8168509,8576941,9005793,9456080,9928893,
+ 10425342,10946613,11493946,12068655,12672087,13305690,13970980,14669530,15403013,16173172,
+ 16981833,17830934,18722479,19658611,20641552,21673632,22757319,23895184,25089950,25591758,
+ 26103599,26625670,27158181,27701356,28255388,28820507,29396920,29984867,30584571,31196260,
+ 31820184,32456596,33105727,33767845,34443202,35132065,35834705,36551410,37282446,38028099,
+ 38788671,39564454,40355745,41162860,41986116,42825838,43682362,44556009,45447138,45901616,
+ 46360641,46824255,47292503,47765432,48243093,48725528,49212784,49704921,50201982,50704003,
+ 51211047,51723162,52240405,52762810,53290446,53823354,54361598,54905214,55454271,56008820,
+ 56568910,57134610,57705965,58283032,58865873,59454536,60049086,60649586,61256083,61868649,
+ 62487336,63112216,63743344,64380779,65024597,65674843,66331602,66994930,67664885,68341538,
+ 69024959,69715207,70412360,71116485,71827658,72545940,73271398,74004114,74744153,75491595,
+ 76246518,77008984,77779077,78556879,79342456,80135878,80937236,81746616,82564087,83389730,
+ 84223638,85065880,85916545,86775711,87643468,88519912,89405121,90299171,91202163,92114182,
+ 93035322,93965687,94905347,95854406,96812948,97781076,98758892,99746478,100743951,101751398,
+ 102768911,103796609,104834582,105882930,106941769,108011193,109091315,110182230,111284052,112396904,
+ 113520874,114656091,115802657,116960692,118130296,119311601,120504716,121709770,122926875,124156150,
+ 125397711,126651686,127918206,129197392,130489375,131794267,133112212,134443338,135787774,137145652,
+ 138517109,139902284,141301316,142714335,144141489,145582902,147038738,148509126,149994219,150894194,
+ 151799571,152710366,153626627,154548387,155475676,156408540,157346998,158291079,159240837,160196288,
+ 161157464,162124419,163097165,164075757,165060214,166050576,167046878,168049166,169057470,170071823,
+ 171092262,172118826,173151541,174190460,175235604,176287026,177344758,178408837,179479294,180556179,
+ 181639518,182729367,183825754,184928711,186038293,187154528,188277464,189407130,190543577,191686849,
+ 192836981,193994007,195157974,196328927,197506910,198691949,199884102,201083405,202289908,203503646,
+ 204724676,205953024,207188754,208431886,209682484,210940582,212206225,213479467,214760350,216048919,
+ 217345224,218649298,219961193,221280960,222608645,223944302,225287974,226639708,227999552,229367554,
+ 230743762,232128226,233520999,234922133,236331675,237749668,239176170,240611236,242054915,243507252,
+ 244968307,246438117,247916753,249404252,250900687,252406101,253920540,255444067,256976742,258518609,
+ 260069729,261630158,263199938,264779135,266367816,267966025,269573820,271191260,272818408,274455326,
+ 276102061,277758679,279425234,281101787,282788399,284485141,286192050,287909214,289636677,291374496,
+ 293122752,294881486,296650782,298430689,300221275,302022609,303834742,305657759,307491713,309336672,
+ 311192696,313059863,314938223,316827859,318728836,320641216,322565061,324500459,326447460,328406143,
+ 330376584,332358847,334353004,336359128,338377288,340407551,342449994,344504696,346571722,348651151,
+ 350743062,352847532,354964629,357094423,359236990,361392422,363560778,365742141,367936599,370144221,
+ 372365091,374599279,376846874,379107955,381382611,383670915,385972949,388288794,390618537,392962257,
+ 395320037,397691969,400078129,402478601,404893475,407322841,409766782,412225383,414698739,417186939,
+ 419690069,422208209,424741456,427289911,429853651,432432776,435027370,437637543,440263366,442024421,
+ 443792521,445567690,447349963,449139360,450935926,452739673,454550638,456368839,458194326,460027115,
+ 461867221,463714693,465569558,467431848,469301579,471178794,473063508,474955766,476855601,478763022,
+ 480678083,482600803,484531205,486469328,488415217,490368880,492330355,494299675,496276878,498261990,
+ 500255043,502256068,504265102,506282171,508307305,510340540,512381907,514431439,516489176,518555144,
+ 520629376,522711891,524802748,526901963,529009581,531125618,533250127,535383137,537524678,539674777,
+ 541833486,544000830,546176836,548361554,550555012,552757234,554968274,557188159,559416915,561654583,
+ 563901210,566156820,568421455,570695142,572977924,575269847,577570928,579881219,582200741,584529542,
+ 586867672,589215145,591572009,593938303,596314063,598699319,601094122,603498497,605912494,608336152,
+ 610769496,613212575,615665434,618128102,620600617,623083026,625575357,628077658,630589971,633112332,
+ 635644789,638187373,640740134,643303101,645876314,648459827,651053675,653657891,656272528,658897623,
+ 661533224,664179368,666836088,669503437,672181451,674870184,677569669,680279959,683001081,685733085,
+ 688476023,691229930,693994859,696770843,699557927,702356165,705165600,707986270,710818214,713661496,
+ 716516145,719382218,722259755,725148792,728049398,730961596,733885443,736820993,739768279,742727359,
+ 745698276,748681079,751675801,754682510,757701238,760732051,763774987,766830096,769897426,772977027,
+ 776068941,779173220,782289911,785419075,788560754,791714999,794881861,798061393,801253640,804458666,
+ 807676501,810907214,814150847,817407448,820677081,823959788,827255638,830564665,833886935,837222492
+ ]
+
+def printSeparator():
+ print("--------------------------------------------------------------------------------")
+
+def showHeader():
+ global stgen, aeros, bifs, skipCI
+ print("TMW2 Ele Generator")
+ print("Run at: " + datetime.datetime.now().isoformat())
+ print("Usage: ./redesign.py [default|aeros|none|update|all] [<path_to_serverdata>]")
+ if len(sys.argv) >= 2:
+ if sys.argv[1] == "default":
+ stgen=True
+ aeros=False
+ bifs=False
+ skipCI=False
+ elif sys.argv[1] == "aeros":
+ stgen=False
+ aeros=True
+ bifs=False
+ skipCI=False
+ elif sys.argv[1] == "none":
+ stgen=False
+ aeros=False
+ bifs=False
+ skipCI=False
+ elif sys.argv[1] == "update":
+ stgen=True
+ aeros=False
+ bifs=False
+ skipCI=True
+ elif sys.argv[1] == "all":
+ stgen=True
+ aeros=True
+ bifs=True
+ skipCI=False
+ else:
+ exit(1)
+ print("This stuff analyzes and sorts monsters and then create base stats for Moubootaur Legends.")
+ print("Drops aren't calculated or taken in account, TWEAK AS NEEDED")
+ printSeparator()
+ print("Output is: EleMonsters.html")
+
+def showFooter():
+ #pass
+ #printSeparator()
+ print("Done.")
+
+
+
+Mobs1=[]
+Mobs2=[]
+Mobs3=[]
+Mobs4=[]
+Mobs5=[]
+Mobs6=[]
+MobsA=[]
+
+# [N, W, E, F, W, -, H, H, G, -]
+Ele=[0, 0, 0, 0, 0, 0, 0, 0, 0]
+
+# This is for Aeros
+Plants=[]
+Level50=[]
+Level100=[]
+Aggressive=[]
+Assistant=[]
+Looter=[]
+Boss=[]
+
+SysDrops=[]
+
+def fwalk(wmask):
+ if wmask == 'WATER':
+ return '<font color=#00f>%s</font>' % (wmask)
+ elif wmask == 'AIR':
+ return '<font color=#093>%s</font>' % (wmask)
+ elif wmask == 'WALL':
+ return '<font color=#f00>%s</font>' % (wmask)
+ elif wmask == 'NORMAL' or wmask == 'DEFAULT':
+ return '<font color=#111>%s</font>' % (wmask)
+ else:
+ print("Invalid walk mask: "+wmask)
+ exit(1)
+
+def WhatRace(rac):
+ rc=rac.race
+ if rc == 0:
+ return "Formless"
+ elif rc == 1:
+ return "Undead"
+ elif rc == 2:
+ return "Brute"
+ elif rc == 3:
+ return "Plant"
+ elif rc == 4:
+ return "Insect"
+ elif rc == 5:
+ return "Mineral"
+ elif rc == 6:
+ return "-"
+ elif rc == 7:
+ return "SemiHuman"
+ elif rc == 8:
+ return "Angel"
+ elif rc == 9:
+ return "Dragon"
+ elif rc == 10:
+ return "Player"
+ elif rc == 11:
+ return "Boss"
+ elif rc == 12:
+ return "NonBoss"
+ elif rc == 14:
+ return "NonSemiHuman"
+ elif rc == 15:
+ return "NonPlayer"
+ elif rc == 16:
+ return "SemiPlayer"
+ elif rc == 17:
+ return "NonSemiPlayer"
+ else:
+ print("ERROR, INVALID RACE ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+
+def WhatElem(rac):
+ rc=rac.elem
+ tl="ERROR"
+ cl="#F00"
+ if rc == 0:
+ tl,cl="Neutral","#000"
+ elif rc == 1:
+ tl,cl="Water","#00F"
+ elif rc == 2:
+ tl,cl="Earth","#7A0"
+ elif rc == 3:
+ tl,cl="Fire","#F00"
+ elif rc == 4:
+ tl,cl="Wind","#093"
+ elif rc == 5:
+ tl,cl="Nature","#040"
+ elif rc == 6:
+ tl,cl="Holy","#afa"
+ elif rc == 7:
+ tl,cl="Dark","#908"
+ elif rc == 8:
+ tl,cl="Ghost","#404"
+ elif rc == 9:
+ tl,cl="Undead","#440"
+ else:
+ print("ERROR, INVALID ELEM ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+ Ele[rc]+=1
+ return "<font color=%s>%s</font>" % (cl, tl)
+
+class Mob:
+ def __init__(self):
+ # Basic
+ self.id="0"
+ #self.aegis="UnknownMonster" # SpriteName is not used anywhere, we are using its ID
+ self.name="Unknown Monster Name"
+ self.view="1"
+ self.boss=False
+ self.plant=False
+
+ # Defensive
+ self.mobpt="0" # Mob Points “Level”
+ self.hp="0"
+ self.xp="Exp: 0"
+ self.jp="JExp: 0"
+ self.st=""
+ self.dfn=0
+ self.mdf=0
+
+ # Offensive
+ self.atk="[0, 0]"
+ self.range="1"
+ self.move="0"
+ self.delay="0"
+ self.drops=[]
+
+ # New
+ self.race=-1
+ self.elem=-1
+ self.elel=-1
+ self.walk="NORMAL"
+
+ # Stats
+ self.str='0'
+ self.agi='0'
+ self.vit='0'
+ self.int='0'
+ self.dex='0'
+ self.luk='0'
+
+def MobAlloc(ab):
+ try:
+ maab=int(ab.mobpt)
+ except:
+ maab=9901
+
+ if maab <= 20 or skipCI:
+ Mobs1.append(ab)
+ elif maab <= 40:
+ Mobs2.append(ab)
+ elif maab <= 60:
+ Mobs3.append(ab)
+ elif maab <= 80:
+ Mobs4.append(ab)
+ elif maab <= 100:
+ Mobs5.append(ab)
+ elif maab <= 150:
+ Mobs6.append(ab)
+ else:
+ MobsA.append(ab)
+
+ # Aeros allocation
+ """Plants=[]
+ Level50=[]
+ Level100=[]
+ Aggressive=[]
+ Assistant=[]
+ Looter=[]
+ Boss=[]"""
+ normie=True
+ if ab.plant:
+ Plants.append(ab.id)
+ normie=False
+ if ab.boss:
+ Boss.append(ab.id)
+ normie=False
+ if normie:
+ if "Agr" in ab.st:
+ Aggressive.append(ab.id)
+ normie=False
+ if "Lot" in ab.st:
+ Looter.append(ab.id)
+ normie=False
+ if "Ass" in ab.st:
+ Assistant.append(ab.id)
+ normie=False
+ if normie:
+ if maab <= 55:
+ Level50.append(ab.id)
+ else:
+ Level100.append(ab.id)
+
+def testMobs():
+ print("Generating Elem-Mob Wiki...")
+ if len(sys.argv) >= 3:
+ src=open(sys.argv[2]+"/db/re/mob_db.conf", "r", encoding="utf-8")
+ else:
+ src=open("../../serverdata/db/re/mob_db.conf", "r", encoding="utf-8")
+
+ wikib.write("<h1 id=#x>EleGen Monster Database</h1>\n")
+ start=False
+ dropper=False
+ skip=0
+ x=Mob() # Only for pyflakes2
+
+ for a2 in src:
+ a=a2.replace(' ', '\t');
+ if a == "{\n":
+ if skip:
+ skip=0
+ if start:
+ MobAlloc(x)
+ else:
+ start=True
+ x=Mob()
+
+ if skip:
+ start=False
+ x=Mob()
+ continue
+ if " Id:" in a:
+ x.id=stp(a)
+ if x.id == "ID":
+ continue
+
+ if " Name:" in a:
+ x.name=stp(a)
+ elif " Hp:" in a:
+ x.hp=stp(a)
+ elif " Lv:" in a:
+ x.mobpt=stp(a)
+ elif " Exp:" in a:
+ x.xp=stp(a)
+ elif " JExp:" in a:
+ x.jp=stp(a)
+ elif " Attack:" in a:
+ x.atk=stp(a)
+ elif " AttackRange:" in a:
+ x.range=stp(a)
+ elif " MoveSpeed:" in a:
+ x.move=stp(a)
+ elif " ViewRange:" in a:
+ x.view=stp(a)
+ elif " AttackDelay:" in a:
+ x.delay=stp(a)
+
+ elif " Def:" in a:
+ x.dfn=stp(a)
+ elif " Mdef:" in a:
+ x.mdf=stp(a)
+
+ elif " Str:" in a:
+ x.str=stp(a)
+ elif " Agi:" in a:
+ x.agi=stp(a)
+ elif " Vit:" in a:
+ x.vit=stp(a)
+ elif " Int:" in a:
+ x.int=stp(a)
+ elif " Dex:" in a:
+ x.dex=stp(a)
+ elif " Luk:" in a:
+ x.luk=stp(a)
+
+
+ elif " Boss: true" in a:
+ x.boss=True
+ elif " Plant: true" in a:
+ x.plant=True
+ elif " Looter: true" in a:
+ x.st+="<font color=#790>Lot</font>,"
+ elif " Assist: true" in a:
+ x.st+="<font color=#0a0>Ass</font>,"
+ elif " Aggressive: true" in a:
+ x.st+="<font color=#f00>Agr</font>,"
+ elif " WalkMask:" in a:
+ x.walk=stp(a)
+ elif " Element:" in a:
+ tmp=stp(a)
+ tmp2=tmp.split(',')
+ try:
+ x.elem=int(tmp2[0])
+ x.elel=int(tmp2[1])
+ except:
+ print("Invalid Element for mob %s: %s" % (x.id, tmp))
+ exit(1)
+ elif " Race:" in a:
+ try:
+ x.race=int(stp(a))
+ except:
+ print("Invalid Race for mob %s: %s" % (x.id, a))
+ exit(1)
+ elif 'Drops: ' in a:
+ dropper=True
+ elif dropper and '}' in a:
+ dropper=False
+ elif dropper:
+ x.drops.append(stp(a).split(": "))
+ elif "Plant: true" in a:
+ skip=1
+ # Write last entry
+ MobAlloc(x)
+
+ writeMob()
+
+ wikib.write('\n\n|Mode|Desc|\n|----|----|\n')
+ wikib.write('|Lot|Looter|\n')
+ wikib.write('|Ass|Assist|\n')
+ wikib.write('|Agr|Aggressive|\n')
+
+ src.close()
+
+def stp(x):
+ return x.replace('\n', '').replace('|', '').replace('(int, defaults to ', '').replace(')', '').replace('basic experience', '').replace('"','').replace(" ","").replace("\t","").replace('(string', '').replace('Name: ','').replace('Element: ','').replace('Race: ','').replace('AttackDelay: ', '').replace('WalkMask: ','').replace('MoveSpeed: ', '').replace('AttackRange: ', '').replace('ViewRange: ','').replace('Attack: ','').replace('ViewRange: ','').replace('Hp: ','').replace('Id: ','').replace('Lv: ','').replace('view range','').replace('attack range','').replace('move speed','').replace('health','').replace('(int','').replace('attack delay','atk.').replace('(','').replace(')','').replace('WALK_','').replace('Def: ','').replace('Mdef: ','')
+
+
+def MonsterWrite(tbl):
+ global skipCI
+ # TODO: Check _mobs files to determine the usual monster density (a misc info to aid adding proper drop specs)
+ wikib.write("<table border=1>\n")
+ if stgen:
+ wikib.write("<tr><th>ID</th><th>Name</th><th>Mob Info</th><th>Stgen</th><th>Elegen</th><th>Misc Info</th><th>Rewards</th><th>Stats</th><th>Drops</th></tr>\n")
+ else:
+ wikib.write("<tr><th>ID</th><th>Name</th><th>Mob Info</th><th>Elegen</th><th>Misc Info</th><th>Rewards</th><th>Stats</th><th>Drops</th></tr>\n")
+
+ if not skipCI:
+ tbl=sorted(tbl, key=lambda tbl: int(tbl.mobpt))
+
+ for i in tbl:
+ if i.id == 'ID':
+ continue
+
+ # Special skips for REDESIGN
+ #if (int(i.id) < 1187):
+ # continue
+ if not bifs and ((int(i.hp) <= 50) or (i.race == 3)):
+ continue
+
+ if i.boss:
+ i.name="<b>"+i.name+"</b>"
+ if stgen:
+ wikib.write('<tr><td><a name="' + i.id + '"></a>' +
+ i.id +"</td><td>"+
+ i.name +"</td><td>"+
+ mb_core(i) +"</td><td>"+
+ mbdt('advise',mb_stgen(i)) +"</td><td>"+
+ mb_eleg(i) +"</td><td>"+
+ mbdt('misc', mb_rdmisc(i)) +"</td><td>"+
+ mbdt('Exp\'s', mb_rdrw(i)) +"</td><td>"+
+ mbdt('stats', mb_rdstat(i)) +"</td><td>"+
+ mbdt('drops', mb_rddrop(i)) +"</td></tr>\n"
+ )
+ else:
+ wikib.write('<tr><td><a name="' + i.id + '"></a>' +
+ i.id +"</td><td>"+
+ i.name +"</td><td>"+
+ mb_core(i) +"</td><td>"+
+ mb_eleg(i) +"</td><td>"+
+ mbdt('misc', mb_rdmisc(i)) +"</td><td>"+
+ mbdt('Exp\'s', mb_rdrw(i)) +"</td><td>"+
+ mbdt('stats', mb_rdstat(i)) +"</td><td>"+
+ mbdt('drops', mb_rddrop(i)) +"</td></tr>\n"
+ )
+ wikib.write("</table>\n")
+ wikib.write("<b>Total: %02d Monsters</b><br/>\n" % len(tbl))
+ wikib.write("\n<a href=#x>(↑) Return to top</a><br/><br/>\n\n")
+
+def writeMob():
+ wikib.write("<ul>\
+<li><a href=#0>Starter</a></li>\n\
+<li><a href=#1>Apprentice</a></li>\n\
+<li><a href=#2>Intermediary</a></li>\n\
+<li><a href=#3>Advanced</a></li>\n\
+<li><a href=#4>Expert</a></li>\n\
+<li><a href=#5>Master</a></li>\n\
+<li><a href=#Nan>OoS</a></li>\n\
+</ul> ")
+
+ wikib.write("<h1 id=0>Lv 0-20</h1>\n\n")
+ MonsterWrite(Mobs1)
+ wikib.write("<h1 id=1>Lv 21-40</h1>\n\n")
+ MonsterWrite(Mobs2)
+ wikib.write("<h1 id=2>Lv 41-60</h1>\n\n")
+ MonsterWrite(Mobs3)
+ wikib.write("<h1 id=3>Lv 61-80</h1>\n\n")
+ MonsterWrite(Mobs4)
+ wikib.write("<h1 id=4>Lv 81-100</h1>\n\n")
+ MonsterWrite(Mobs5)
+ wikib.write("<h1 id=5>Lv 101-150</h1>\n\n")
+ MonsterWrite(Mobs6)
+
+ wikib.write("<h1 id=NaN>Lv 101+</h1>\n\n")
+ MonsterWrite(MobsA)
+
+
+def mbdt(summary, content):
+ return "<b>"+summary+"</b><br/><pre>"+content+"</pre>"
+ return "<details>\
+<summary>"+summary+"</summary>\
+<pre>"+content+"</pre></details>"
+
+def mb_core(mb):
+ buff=""
+ buff+="Lvl: %s<br/>\n" % (mb.mobpt)
+ buff+="HP: <b><font color=#0a0>%s</font></b><br/>\n" % (mb.hp)
+ buff+="ATK: <b><font color=#a00>%s</font></b><br/>\n" % (mb.atk)
+ buff+="DEF: <b><font color=#775>%s/%s</font></b><br/>\n" % (mb.dfn, mb.mdf)
+ if mb.st != "":
+ buff+="Modes: <i>%s</i>" % (mb.st)
+ return buff
+
+def mb_eleg(mb):
+ buff=""
+ buff+="Race: %s<br/>\n" % (WhatRace(mb))
+ buff+="Walk: %s<br/>\n" % (fwalk(mb.walk))
+ buff+="Element: %s<br/>\n" % (WhatElem(mb))
+ return buff
+
+############################################################
+def mb_stgen(mb):
+ lv=int(mb.mobpt)
+ st=int(mb.str.replace(' ', '').replace('Str:',''))
+ #ag=int(mb.agi.replace(' ', '').replace('Agi:',''))
+ vi=int(mb.vit.replace(' ', '').replace('Vit:',''))
+ #it=int(mb.int.replace(' ', '').replace('Int:',''))
+ #dx=int(mb.dex.replace(' ', '').replace('Dex:',''))
+ #lk=int(mb.luk.replace(' ', '').replace('Luk:',''))
+
+ # Attack Range vs Attack Delay
+ ar=int(mb.range)
+ ad=int(mb.delay)
+ mv=int(mb.move)
+ magr=False
+ mass=False
+ if ('Agr' in mb.st):
+ magr=True
+ if ('Ass' in mb.st):
+ mass=True
+
+ if (not ar):
+ ar=1
+
+ # Fórmula do dano:
+ # Tal que level 10 cause 150 dano
+ # Tal que level 50 cause 1000 dano
+ # Tal que a fórmula seja exponencial
+ """
+ lhp=lv**1.5+lv*(vi/100)+lv*15
+ hhp=lv**1.8+lv*(vi/100)+lv*15
+ # Casos especiais
+ if mb.boss:
+ lhp*=1.6
+ hhp*=4.2
+ if "slime" in mb.name.lower():
+ lhp*=0.4
+ hhp*=0.4
+ if ar > 1:
+ lhp-=ar**2
+ hhp-=ar**2
+
+ # Fórmula da HPTable: 400+(x*50)
+ # MASTERED
+ lat=(lv*40+lv**1.2+lv*(st/100))/ar
+ hat=(lv*40+lv**1.5+lv*(st/100))/ar
+ # Casos especiais
+ if mb.boss:
+ lat*=1.2
+ hat*=1.4
+ if "slime" in mb.name.lower():
+ lat*=0.3
+ hat*=0.3
+ # Attack is DPS. So we must adjust to be a damage per second
+ lat*=(ad/1872)
+ hat*=(ad/1872)
+ # Formula is not reliable
+ lat/=2
+ hat/=2
+ # Consider Aggressive and Assist mobs
+ if magr:
+ lat*=0.8
+ hat*=0.8
+ if mass:
+ lat*=0.9
+ hat*=0.9
+
+ # Experience
+ lxp=(lv**1.2+lat+(lhp/8)) / max(1,(12-(lv/10)))
+ hxp=(lv**1.6+hat+(hhp/8)) / max(1,(15-(lv/10)))
+ # Casos especiais
+ if mb.boss:
+ lxp*=4.2
+ hxp*=4.7
+ """
+
+ # Over100 Special Formula
+ OVER100=0
+ if lv > 100:
+ OVER100=lv-100
+ lv=100+OVER100*0.25
+
+ # Fórmula da HPTable: 400+(x*50)
+ # MASTERED
+ lat=(lv*40+lv**1.2+lv*(st/100))
+ hat=(lv*40+lv**1.5+lv*(st/100))
+ if (ar > 1):
+ lat*=max(0.5, 1.0-((ar-1)/10.0))
+ hat*=max(0.5, 1.0-((ar-1)/10.0))
+
+ # Casos especiais
+ if mb.boss:
+ lat*=1.2
+ hat*=1.4
+ if "slime" in mb.name.lower():
+ lat*=0.3
+ hat*=0.3
+ # Attack is DPS. So we must adjust to be a damage per second
+ lat*=(ad/1872)
+ hat*=(ad/1872)
+ # Formula is not reliable
+ lat*=0.55
+ hat*=0.55
+ lat*=max(0.5, 1.0-(lv/10.0))
+ hat*=max(0.5, 1.0-(lv/10.0))
+ # Consider Aggressive and Assist mobs
+ if magr:
+ lat*=0.78
+ hat*=0.78
+ if mass:
+ lat*=0.89
+ hat*=0.89
+
+
+ # Over100 Special Formula
+ if OVER100:
+ lv=100+OVER100*2
+
+ # HP: Each 10 levels give you 100 extra weapon damage
+ # I expect from 6 to 14 hits to kill
+ # You deliver in average two hits per second
+ # Mobs deliver one hit each four seconds (and that's 1hand advantage)
+ lhp=lv*20*6+lv*(vi/100)
+ hhp=lv*20*14+lv*(vi/100)
+ if mb.boss:
+ lhp*=1.4
+ hhp*=1.8
+ if "slime" in mb.name.lower():
+ lhp*=0.6
+ hhp*=0.6
+ if ar > 1 and not OVER100:
+ lhp*=0.9
+ hhp*=0.9
+
+ # Experience is way too non-linear and I prefer to do it with reference formula
+ # like I was doing before
+ # But let's use a formula based on mobs-to-lvl-up where you must kill 1580 lv 60 mobs
+ # to raise from lv 60 (you can kill lv 50 mobs easier - but then I expect 3700 kills)
+ # mobs to kill to raise level
+
+ # This is the current mob-lvl-exp-table generated by this software
+ # As you see, it is somewhat similar to TMW Org.
+ # Remember aggressive, fast, and assistant mobs give even more exp
+
+ # Level 1: total 1 mobs individual 3 xp (1.450)
+ # Level 2: total 3 mobs individual 5 xp (1.450)
+ # Level 3: total 8 mobs individual 5 xp (1.450)
+ # Level 4: total 16 mobs individual 4 xp (1.450)
+ # Level 5: total 26 mobs individual 7 xp (1.450)
+ # Level 6: total 39 mobs individual 8 xp (1.450)
+ # Level 7: total 56 mobs individual 9 xp (1.450)
+ # Level 8: total 77 mobs individual 9 xp (1.450)
+ # Level 9: total 101 mobs individual 10 xp (1.450)
+ # Level 10: total 129 mobs individual 11 xp (1.450)
+ # Level 20: total 667 mobs individual 21 xp (1.450)
+ # Level 30: total 1767 mobs individual 57 xp (1.450)
+ # Level 31: total 1912 mobs individual 63 xp (1.450)
+ # Level 32: total 2064 mobs individual 68 xp (1.450)
+ # Level 33: total 2223 mobs individual 73 xp (1.450)
+ # Level 34: total 2390 mobs individual 84 xp (1.450)
+ # Level 35: total 2563 mobs individual 91 xp (1.450)
+ # Level 36: total 2747 mobs individual 96 xp (1.455)
+ # Level 37: total 2942 mobs individual 101 xp (1.460)
+ # Level 38: total 3148 mobs individual 105 xp (1.465)
+ # Level 39: total 3366 mobs individual 130 xp (1.470)
+ # Level 40: total 3597 mobs individual 148 xp (1.475)
+ # Level 41: total 3841 mobs individual 156 xp (1.480)
+ # Level 42: total 4098 mobs individual 163 xp (1.485)
+ # Level 43: total 4369 mobs individual 170 xp (1.490)
+ # Level 44: total 4656 mobs individual 185 xp (1.495)
+ # Level 45: total 4958 mobs individual 193 xp (1.500)
+ # Level 46: total 5276 mobs individual 201 xp (1.505)
+ # Level 47: total 5611 mobs individual 208 xp (1.510)
+ # Level 48: total 5963 mobs individual 215 xp (1.515)
+ # Level 49: total 6334 mobs individual 249 xp (1.520)
+ # Level 50: total 6724 mobs individual 295 xp (1.525)
+ # Level 51: total 7133 mobs individual 314 xp (1.530)
+ # Level 52: total 7564 mobs individual 331 xp (1.535)
+ # Level 53: total 8016 mobs individual 348 xp (1.540)
+ # Level 54: total 8491 mobs individual 375 xp (1.545)
+ # Level 55: total 8990 mobs individual 393 xp (1.550)
+ # Level 56: total 9512 mobs individual 411 xp (1.555)
+ # Level 57: total 10061 mobs individual 428 xp (1.560)
+ # Level 58: total 10636 mobs individual 443 xp (1.565)
+ # Level 59: total 11239 mobs individual 514 xp (1.570)
+ # Level 60: total 11871 mobs individual 579 xp (1.575)
+ # Level 61: total 12533 mobs individual 608 xp (1.580)
+ # Level 62: total 13226 mobs individual 638 xp (1.585)
+ # Level 63: total 13952 mobs individual 670 xp (1.590)
+ # Level 64: total 14712 mobs individual 704 xp (1.595)
+ # Level 65: total 15508 mobs individual 740 xp (1.600)
+ # Level 66: total 16340 mobs individual 778 xp (1.605)
+ # Level 67: total 17211 mobs individual 818 xp (1.610)
+ # Level 68: total 18122 mobs individual 861 xp (1.615)
+ # Level 69: total 19075 mobs individual 905 xp (1.620)
+ # Level 70: total 20071 mobs individual 952 xp (1.625)
+ # Level 80: total 32389 mobs individual 1783 xp (1.650)
+ # Level 90: total 47806 mobs individual 3635 xp (1.650)
+ # Level 100: total 66308 mobs individual 4976 xp (1.650)
+
+ # Over100 Special Formula
+ if OVER100:
+ lv=100+OVER100
+
+ try:
+ mxp=exp[lv]
+ hxp=exp[lv]
+ lxp=exp[lv-1]
+ minxp=exp[lv-1]
+ except:
+ mxp=exp[len(exp)-1]
+ hxp=exp[len(exp)-1]
+ lxp=exp[len(exp)-2]
+ minxp=exp[len(exp)-2]
+ print("Warning: Invalid exp for mob \033[1m%s\033[0m" % (mb.name.replace("<b>", "").replace("</b>", "")))
+
+ if not mb.boss:
+ if lv > 35:
+ fx=1.45+((lv-35)/200.0)
+ else:
+ fx=1.45
+ if fx > 1.65:
+ fx=1.65
+
+ # Aggressive, assistant and fast mobs yield more exp
+ # Slow monsters yield less exp, because they're easier to kill on any playstyle
+ if magr:
+ fx-=0.09
+ else:
+ fx-=0.04
+ if mass:
+ fx-=0.08
+ if mv < 200:
+ fx-=0.02
+ if mv > 500:
+ fx+=0.02
+
+ # 1 ^ anything = 1, so we need a better rule
+ if lv != 1:
+ lxp=minxp/(lv**fx)
+ else:
+ lxp=3 # 3~5 is fine
+ # Boss are BOSS. It gives roughly 0.1% ~ 10.0% from lvlup xp.
+ else:
+ lxp=mxp/1000
+ hxp=mxp/10
+ del minxp, mxp
+
+ # Over100 Special Formula (kinda)
+ olv=0
+ if lv > 80:
+ olv=lv+0
+ lv=80+(lv-80)*0.25
+
+ # Defense follows the same player formula
+ dfn=((lv**1.255)*2.5)
+ dfn=dfn*350.0/810.0
+ mdf=max(0, lv-5)+(lv/10.0)
+ if not mb.boss:
+ mdf/=2
+ if ar > 3:
+ dfn/=2
+
+ # Nerf mobs def in 50% (to be more realistic to what we have)
+ dfn/=2
+
+ # Over100 Special Formula
+ if OVER100:
+ lv=100+OVER100
+ else:
+ lv=olv+0
+ del olv
+
+ # Force HP to be higher
+ # It'll only start applying from level 40 onwards
+ # It gives a bonus of 0.5% HP per mob level
+ # This means a level 100 mob got 60 times that stronger: 30%
+ if lv > 40:
+ lhp=lhp*(1.0+((lv-40)/210.0))
+ lhp=int(lhp)
+
+ # Norm
+ lhp=int(lhp)
+ hhp=int(hhp)
+ lat=int(lat)
+ hat=int(hat)
+ lxp=int(lxp)
+ hxp=int(hxp)
+ dfn=int(dfn)
+ mdf=int(mdf)
+
+ buff="<pre><font size=-2px>"
+ buff+="HP Range: %s ~ %s<br/>\n" % (lhp, hhp)
+ buff+="ATK Range: %s ~ %s<br/>\n" % (lat, hat)
+ buff+="Maximum XP: %s ~ %s<br/>\n" % (lxp, hxp)
+ buff+="DEF: %s / %s<br/>\n" % (dfn, mdf)
+ buff+="<b>Drop, Move, Elegen, aspd<br/>\n"
+ buff+="</b></font></pre>"
+ return buff
+
+def mb_rdstat(mb):
+ buff="<pre><font size=-4px>"
+ buff+="%s\n" % (mb.str)
+ buff+="%s\n" % (mb.agi)
+ buff+="%s\n" % (mb.vit)
+ buff+="%s\n" % (mb.int)
+ buff+="%s\n" % (mb.dex)
+ buff+="%s\n" % (mb.luk)
+ buff+="</font></pre>"
+ return buff
+
+def mb_rdmisc(mb):
+ buff=""
+ if "agr" in mb.st.lower():
+ buff+="View Range: %s\n" % (mb.view)
+ buff+="Attack Range: %s\n" % (mb.range)
+ buff+="AtkDelay: %s ms\n" % (mb.delay)
+ buff+="Move speed: %s ms\n" % (mb.move)
+ buff+="Element Level: %d\n" % (mb.elel)
+ return buff
+
+def mb_rdrw(mb):
+ buff=""
+ buff+="<font color=#060><u>%s</b></u></font>\n" % (mb.xp.replace(' ', ' <b>'))
+ buff+="<font color=#006><u>%s</b></u></font>\n" % (mb.jp.replace(' ', ' <b>'))
+ try:
+ buff+="MobPoints: %d\n" % (int(mb.mobpt)*11/10)
+ except:
+ pass
+ return buff
+
+def mb_rddrop(mb):
+ buff=""
+ tdr=0
+ for ax in mb.drops:
+ # Ignore disabled drops
+ if ax[0].startswith("//"):
+ continue
+
+ # Write drop
+ try:
+ buff+=ax[0]+': ' + str(int(ax[1])/100.0) + ' %\n'
+ tdr+=int(ax[1])
+ except IndexError:
+ print("Fatal: invalid %s mob with %s drops" % (mb.name, str(ax)))
+ exit(1)
+ except:
+ print("[Warning] %s incorrect drop: %s" % (mb.name, str(ax)))
+ buff+=ax[0]+': ' + ax[1] + ' ppm\n'
+
+ # Save to SysDrops
+ SysDrops.append([ax[0], ax[1], mb.name])
+
+ if tdr < 500:
+ print("[INFO] %s has %d/500 drops. Disqualified for Realm of Drops." % (mb.name, tdr))
+ return buff
+
+
+
+showHeader()
+
+testMobs()
+
+wikib.write('<br/><hr/>')
+wikib.write("Run at: " + datetime.datetime.now().isoformat())
+wikib.write('<hr/>')
+
+wikib.write("""
+<b>Player Stats (melee warrior)</b>
+<table border=1>
+<tr><th>Level</th><th>Stats 1</th><th>Stats 2</th><th>Average</th></tr>
+
+<tr><th>00</th>
+<td>str: 2 agi: 5 vit: 7 int:1 dex: 6 luk: 3</td>
+<td>str: 2 agi: 5 vit: 7 int:1 dex: 6 luk: 3</td>
+<th>str: 2 agi: 5 vit: 7 int:1 dex: 6 luk: 3</th></tr>
+
+<tr><th>10</th>
+<td>str:15 agi:10 vit: 5 int:1 dex:10 luk: 4</td>
+<td>str:11 agi:11 vit: 6 int:1 dex:11 luk: 7</td>
+<th>str:13 agi:11 vit: 5 int:1 dex:10 luk: 6</th></tr>
+
+<tr><th>20</th>
+<td>str:19 agi:10 vit:10 int:1 dex:20 luk:10</td>
+<td>str:14 agi:19 vit: 8 int:1 dex:17 luk:11</td>
+<th>str:17 agi:14 vit: 9 int:1 dex:18 luk:11</th></tr>
+
+<tr><th>30</th>
+<td>str:25 agi:30 vit:10 int:1 dex:20 luk:10</td>
+<td>str:22 agi:28 vit:12 int:1 dex:21 luk:15</td>
+<th>str:24 agi:29 vit:11 int:1 dex:20 luk:13</th></tr>
+
+<tr><th>40</th>
+<td>str:38 agi:30 vit:20 int:1 dex:25 luk:17</td>
+<td>str:28 agi:35 vit:14 int:1 dex:32 luk:21</td>
+<th>str:33 agi:33 vit:17 int:1 dex:28 luk:19</th></tr>
+
+<tr><th>50</th>
+<td>str:50 agi:40 vit:30 int:1 dex:25 luk:18</td>
+<td>str:41 agi:41 vit:15 int:1 dex:41 luk:26</td>
+<th>str:46 agi:41 vit:22 int:1 dex:33 luk:27</th></tr>
+
+<tr><th>60</th>
+<td>str:54 agi:50 vit:40 int:1 dex:35 luk:20</td>
+<td>str:52 agi:52 vit:16 int:1 dex:45 luk:32</td>
+<th>str:53 agi:51 vit:28 int:1 dex:40 luk:26</th></tr>
+
+<tr><th>70</th>
+<td>str:60 agi:60 vit:43 int:1 dex:50 luk:20</td>
+<td>str:61 agi:61 vit:22 int:1 dex:51 luk:38</td>
+<th>str:60 agi:61 vit:33 int:1 dex:50 luk:29</th></tr>
+
+<tr><th>80</th>
+<td>str:80 agi:60 vit:50 int:1 dex:50 luk:25</td>
+<td>str:65 agi:71 vit:32 int:1 dex:60 luk:41</td>
+<th>str:72 agi:66 vit:41 int:1 dex:55 luk:33</th></tr>
+
+<tr><th>90</th>
+<td>str:80 agi:70 vit:60 int:1 dex:60 luk:31</td>
+<td>str:71 agi:71 vit:41 int:1 dex:71 luk:51</td>
+<th>str:76 agi:70 vit:50 int:1 dex:66 luk:41</th></tr>
+
+<tr><th>100</th>
+<td>str:90 agi:80 vit:69 int:1 dex:60 luk:31</td>
+<td>str:71 agi:81 vit:42 int:1 dex:81 luk:61</td>
+<th>str:81 agi:80 vit:56 int:1 dex:70 luk:46</th></tr>
+
+<tr><th>110</th>
+<td>str:90 agi:90 vit:69 int:1 dex:70 luk:35</td>
+<td>str:82 agi:82 vit:50 int:1 dex:82 luk:65</td>
+<th>str:86 agi:86 vit:60 int:1 dex:76 luk:50</th></tr>
+
+<tr><th>120</th>
+<td>str:99 agi:90 vit:69 int:1 dex:70 luk:45</td>
+<td>str:86 agi:86 vit:51 int:1 dex:86 luk:68</td>
+<th>str:93 agi:88 vit:60 int:1 dex:78 luk:56</th></tr>
+
+<tr><th>130</th>
+<td>str:99 agi:91 vit:75 int:1 dex:70 luk:55</td>
+<td>str:91 agi:90 vit:51 int:1 dex:90 luk:68</td>
+<th>str:95 agi:91 vit:63 int:1 dex:80 luk:61</th></tr>
+
+</table>
+""")
+wikib.write('<hr/>')
+wikib.write("""
+<h3>Elemental Count</h3>
+<table border=1>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+</table>
+""" % (
+"Neutral", Ele[0],
+"Water", Ele[1],
+"Earth", Ele[2],
+"Fire", Ele[3],
+"Wind", Ele[4],
+"Holy", Ele[6],
+"Dark", Ele[7],
+"Ghost", Ele[8],
+"Error", Ele[5]))
+
+wikib.write('</body></html>')
+wikib.close()
+#print(str(SysDrops))
+
+# Aeros allocation
+if aeros:
+ print("// These arrays are filled automatically by redesign.py");
+ print("setarray .ML_Plants, "+
+ str(Plants).replace('[','').replace(']','').replace("'","")+";")
+ print("setarray .ML_Boss, "+
+ str(Boss).replace('[','').replace(']','').replace("'","")+";")
+ print("setarray .ML_Aggr, "+
+ str(Aggressive).replace('[','').replace(']','').replace("'","")+";")
+ print("setarray .ML_Asst, "+
+ str(Assistant).replace('[','').replace(']','').replace("'","")+";")
+ print("setarray .ML_Loot, "+
+ str(Looter).replace('[','').replace(']','').replace("'","")+";")
+ print("setarray .ML_Lv50, "+
+ str(Level50).replace('[','').replace(']','').replace("'","").replace('ID, ','')+
+ ";")
+ print("setarray .ML_Lv99, "+
+ str(Level100).replace('[','').replace(']','').replace("'","")+";")
+
+showFooter()
+exit(0)
diff --git a/wiki/sedesign.py b/wiki/sedesign.py
new file mode 100755
index 0000000..26d956b
--- /dev/null
+++ b/wiki/sedesign.py
@@ -0,0 +1,515 @@
+#! /usr/bin/env python3
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2010-2011 Evol Online
+# Copyright (C) 2018 TMW-2
+# Author: Andrei Karas (4144)
+# Author: Jesusalva
+
+import datetime
+import sys
+
+stgen=True
+
+wikia=open("EleItems.html", "w")
+
+# the TYPEs we use to determine where to pack things
+IT_ARMOR={ 'MISC': [], # FOR FAILURE
+ 'EQP_ACC_L': [], # ACCESSORY LEFT
+ 'EQP_ACC_R': [], # ACCESSORT RIGHT
+ 'EQP_HEAD_MID': [], # CHEST
+ 'EQP_SHOES': [], # FEET
+ 'EQP_GARMENT': [], # GLOVES
+ 'EQP_HEAD_LOW':[], # PANTS
+ '1024': [], # NECKLACES (should be EQP_COSTUME_HEAD_TOP instead of number)
+ '2048': [], # RINGS (should be EQP_COSTUME_HEAD_MID instead of number)
+ 'EQP_MOUNT':[], # MOUNTS (ie. EQP_SHADOW_SHOES)
+ 'EQP_HEAD_TOP':[], # HATS/HELMETS
+ 'EQP_HAND_L': []} # SHIELDS
+IT_WEAPON={ 'RANGED': [], # RANGED WEAPONS
+ 'MAGICAL':[], # MAGICAL WEAPONS
+ 'HAND_2':[], # TWO HAND (LR)
+ 'HAND_1':[]} # WEAPONS (R)
+
+def printSeparator():
+ print("--------------------------------------------------------------------------------")
+
+def showHeader():
+ print("TMW2 EleItems Generator")
+ print("Run at: " + datetime.datetime.now().isoformat())
+ print("Usage: ./sedesign.py [info|level|none] [<path_to_serverdata>]")
+ print("SeDesign determines the reference defense for every item.")
+ print("Tweak as needed. Bonuses/Rarity/etc not taken in account.")
+ print("Running this without Redesign (or vice versa) will break balance")
+ printSeparator()
+ print("Output is: EleItems.html")
+
+def showFooter():
+ #pass
+ #printSeparator()
+ print("Done.")
+
+
+
+
+
+
+
+class It:
+ def __init__(self):
+ # Basic
+ self.id="0"
+ self.aegis="UnknownItem"
+ self.name="Unknown Item Name"
+ self.price="0" # Sell price, of course
+ self.weight="0"
+ self.type="IT_ETC" # default type
+ self.loc=""
+ self.fc=0.0
+
+ # Offensive/Defensive
+ self.atk="0"
+ self.matk="0"
+ self.range="0"
+ self.defs="0"
+
+ # Restrictions (EquipLv)
+ self.lvl="0"
+ self.drop=True
+ self.trade=True
+ self.sell=True
+ self.store=True
+
+ # Special settings
+ self.rare=False # DropAnnounce
+ self.script=""
+
+ # Visual
+ self.sl="0" # Slots
+ self.ac=False # Allow Cards
+
+def ItAlloc(it):
+ if (it.sl == "0" and it.ac) or (it.sl in ["1","2","3","4"] and not it.ac):
+ print("WARNING, item id "+it.id+" invalid dye/card setting!")
+ if (len(it.sl) > 1 and it.id != "ID(int"):
+ print("WARNING, item id "+it.id+" bad slots length: %d (%s)" % (len(it.sl), it.sl))
+
+ a=it.type
+ if "IT_ARMOR" in a:
+ if 'EQP_ACC_L' in it.loc:
+ IT_ARMOR['EQP_ACC_L'].append(it)
+ it.fc=0.00
+ elif 'EQP_ACC_R' in it.loc:
+ IT_ARMOR['EQP_ACC_R'].append(it)
+ it.fc=0.00
+ elif 'EQP_HEAD_MID' in it.loc:
+ IT_ARMOR['EQP_HEAD_MID'].append(it)
+ if ("bSpeedAddRate, -" in it.script or "bSpeedAddRate,-" in it.script):
+ it.fc=0.40
+ else:
+ it.fc=0.30
+ elif 'EQP_SHOES' in it.loc:
+ IT_ARMOR['EQP_SHOES'].append(it)
+ it.fc=0.08
+ elif 'EQP_GARMENT' in it.loc:
+ IT_ARMOR['EQP_GARMENT'].append(it)
+ it.fc=0.07
+ elif 'EQP_HEAD_LOW' in it.loc:
+ IT_ARMOR['EQP_HEAD_LOW'].append(it)
+ it.fc=0.10
+ elif 'EQP_HEAD_TOP' in it.loc:
+ IT_ARMOR['EQP_HEAD_TOP'].append(it)
+ if ("bSpeedAddRate, -" in it.script or "bSpeedAddRate,-" in it.script):
+ it.fc=0.15
+ else:
+ it.fc=0.10
+ elif 'EQP_HAND_L' in it.loc:
+ IT_ARMOR['EQP_HAND_L'].append(it)
+ it.fc=0.25
+ elif '1024' in it.loc:
+ IT_ARMOR['1024'].append(it)
+ it.fc=0.00
+ elif '2048' in it.loc:
+ IT_ARMOR['2048'].append(it)
+ it.fc=0.00
+ elif 'EQP_SHADOW_SHOES' in it.loc:
+ IT_ARMOR['EQP_MOUNT'].append(it)
+ it.fc=0.00
+ elif 'EQP_SHADOW_ACC_R' in it.loc:
+ IT_ARMOR['EQP_ACC_R'].append(it) # Not really
+ it.fc=0.00
+ else:
+ raise Exception("Invalid Loc for ID %s: %s" % (it.id, it.loc))
+ elif "IT_WEAPON" in a:
+ if int(it.matk) > 0:
+ IT_WEAPON["MAGICAL"].append(it)
+ elif int(it.range) > 2:
+ IT_WEAPON["RANGED"].append(it)
+ elif "HAND_L" in it.loc or "EQP_ARMS" in it.loc:
+ IT_WEAPON["HAND_2"].append(it)
+ elif "HAND_R" in it.loc:
+ IT_WEAPON["HAND_1"].append(it)
+ else:
+ raise Exception("Invalid location for weapon: %s" % it.loc)
+
+def newItemDB():
+ print("\nGenerating Item Wiki...")
+ if len(sys.argv) >= 3:
+ src=open(sys.argv[2]+"/db/re/item_db.conf", "r", encoding="utf-8")
+ else:
+ src=open("../../server-data/db/re/item_db.conf", "r", encoding="utf-8")
+
+ lg=False
+ x=It()
+ for a2 in src:
+ a=a2.replace(' ', '\t');
+ if a == "{\n":
+ ItAlloc(x)
+ x=It()
+
+ # sti() block
+ if " Id:" in a:
+ x.id=sti(a)
+ elif " AegisName:" in a:
+ x.aegis=sti(a)
+ elif " Name:" in a:
+ x.name=stin(a)
+ elif " Sell:" in a:
+ x.price=sti(a)
+ elif " Weight:" in a:
+ x.weight=sti(a)
+ elif " Type:" in a:
+ x.type=sti(a)
+ elif " Loc:" in a:
+ x.loc=sti(a)
+ elif " Atk:" in a:
+ x.atk=sti(a)
+ elif " Matk:" in a:
+ x.matk=sti(a)
+ elif " Range:" in a:
+ x.range=sti(a)
+ elif " Def:" in a:
+ x.defs=sti(a)
+ elif " EquipLv:" in a:
+ x.lvl=sti(a)
+ elif " Slots:" in a:
+ x.sl=sti(a)
+ elif " AllowCards:" in a:
+ x.ac=True
+ # Write booleans
+ elif "DropAnnounce: true" in a:
+ x.rare=True
+ elif "nodrop: true" in a:
+ x.drop=False
+ elif "notrade: true" in a:
+ x.trade=False
+ elif "noselltonpc: true" in a:
+ x.sell=False
+ elif "nostorage: true" in a:
+ x.store=False
+ elif "Script" in a:
+ lg=True
+ x.script+="<pre>"
+ elif lg and "\">" in a:
+ lg=False
+ x.script+="</pre>"
+ elif lg:
+ if not "announce" in a and not "debugmes" in a and not "logmes" in a:
+ x.script+=str(a.replace('\t', '').replace('getiteminfo(getequipid(', 'iteminfo((').replace('Flee2','Block'))
+
+ # Write last entry
+ ItAlloc(x)
+ writeItems()
+
+ src.close()
+
+def sti(x):
+ return x.replace('\n', '').replace('|', '').replace(')', '').replace('Id: ', '').replace('"','').replace(" ","").replace("\t","").replace('AegisName: ', '').replace('Name: ','').replace('Sell: ', '').replace('Weight: ', '').replace('Type: ', '').replace('Loc: ', '').replace('Atk: ', '').replace('Matk: ', '').replace('Range: ', '').replace('Def: ', '').replace('EquipLv: ', '').replace('Slots: ','').replace(" ", "").replace('@min=','').replace('@max=','').replace('@delay=','').replace(';','')
+
+def stin(x):
+ return x.replace('\n', '').replace('|', '').replace(')', '').replace('Id: ', '').replace('"','').replace(" ","").replace("\t","").replace('Name: ','').replace(';','')
+
+
+def writeItems():
+ wikia.write("<h1>Armors</h1>\n\
+<ul>\
+<li><a href=#armors>Armors</a></li>\n\
+<li><a href=#left-accessory>Left Accessory</a></li>\n\
+<li><a href=#right-accessory>Right Accessory</a></li>\n\
+<li><a href=#headgear>Headgear</a></li>\n\
+<li><a href=#chest>Chest</a></li>\n\
+<li><a href=#pants>Pants</a></li>\n\
+<li><a href=#shoes>Shoes</a></li>\n\
+<li><a href=#necklaces>Necklaces</a></li>\n\
+<li><a href=#rings>Rings</a></li>\n\
+<li><a href=#gloves>Gloves</a></li>\n\
+<li><a href=#shields>Shields</a></li>\n\
+</ul>\n\n")
+ wikia.write("<u>Restrictions Reference</u><br/>\n")
+ wikia.write("<ul>\n\
+<li> * - Rare item with drop announce.</li>\n\
+<li> (dp) - This item cannot be dropped.</li>\n\
+<li> (tr) - This item cannot de traded.</li>\n\
+<li> (sl) - This item cannot be sold.</li>\n\
+<li> (gg) - This item cannot go to storage.</li>\n\
+</ul><br/>\n")
+
+ ####################################################################
+ wikia.write("<h2>Armors</h2>\n")
+
+ ArmorWrite("Left Accessory",'EQP_ACC_L')
+ ArmorWrite("Right Accessory",'EQP_ACC_R')
+ ArmorWrite("Headgear",'EQP_HEAD_TOP')
+ ArmorWrite("Chest",'EQP_HEAD_MID')
+ ArmorWrite("Pants",'EQP_HEAD_LOW')
+ ArmorWrite("Shoes",'EQP_SHOES')
+ ArmorWrite("Necklaces",'1024')
+ ArmorWrite("Rings",'2048')
+ ArmorWrite("Gloves",'EQP_GARMENT')
+ ArmorWrite("Shields",'EQP_HAND_L')
+ #for i in sorted(IT_ARMOR['EQP_HEAD_TOP'], key=lambda xcv: int(xcv.lvl)):
+ # # Name | Level | Location | Specification
+ # print("`%s`|%d| :grey_question: | " % (i.name, int(i.lvl)))
+
+ ####################################################################
+ wikia.write("<hr/><h2>Weapons</h2>\n")
+
+ # 1 Hand Items
+ wikia.write("<h3>1H Weapons</h3>\n")
+ ItemWrite(IT_WEAPON['HAND_1'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, LVL=True)
+
+ # 2 Hand Items
+ wikia.write("<h3>2H Weapons</h3>\n")
+ ItemWrite(IT_WEAPON['HAND_2'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, LVL=True, RANGE=True)
+
+ # Ranged Items
+ wikia.write("<h3>Ranged Weapons</h3>\n")
+ ItemWrite(IT_WEAPON['RANGED'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, LVL=True, RANGE=True)
+
+ # Magic Items
+ wikia.write("<h3>Magical Weapons</h3>\n")
+ ItemWrite(IT_WEAPON['MAGICAL'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, MATK=True, LVL=True, RANGE=True)
+
+# Write AegisName with restrictions
+def hl(it):
+ buff=""
+ if it.rare:
+ buff+="*"
+ buff+=it.aegis
+ buff+=" "
+ if not it.drop:
+ buff+="<a href='#restrictions-reference'>(dp)</a>"
+ if not it.trade:
+ buff+="<a href='#restrictions-reference'>(tr)</a>"
+ if not it.sell:
+ buff+="<a href='#restrictions-reference'>(sl)</a>"
+ if not it.store:
+ buff+="<a href='#restrictions-reference'>(gg)</a>"
+ return buff
+
+# wikia.write("Id|Aegis|Name|Weight|Atk|Matk|\n")
+# wikia.write("Id|Aegis|Name|Price|Weight|\n")
+
+def ItemWrite(tbl, ID=False, AEGIS=False, NAME=False, PRICE=False, WEIGHT=False, DEF=False, LVL=False, ATK=False, MATK=False, RANGE=False, HEALING=False, SCRIPT=False, DROPPER=False):
+ global stgen
+ wikia.write("<table border=1>\n")
+ wikia.write("<tr>")
+ if ID:
+ wikia.write("<th>ID</th>")
+ if AEGIS:
+ wikia.write("<th>Aegis</th>")
+ if NAME:
+ wikia.write("<th>Name</th>")
+ if PRICE:
+ wikia.write("<th>Price</th>")
+ if WEIGHT:
+ wikia.write("<th>Weight</th>")
+ if DEF:
+ wikia.write("<th>Def</th>")
+ if stgen:
+ wikia.write("<th>Adj.Def</th>")
+ if LVL:
+ wikia.write("<th>Lvl</th>")
+ if ATK:
+ wikia.write("<th>Atk</th>")
+ if MATK:
+ wikia.write("<th>Matk</th>")
+ if ATK and MATK:
+ wikia.write("<th>+</th>")
+ if ATK or MATK:
+ if stgen:
+ wikia.write("<th>Adj. Atk.</th>")
+ if RANGE:
+ wikia.write("<th>Range</th>")
+ if HEALING:
+ wikia.write("<th>Min</th>")
+ wikia.write("<th>Max</th>")
+ wikia.write("<th>Delay</th>")
+ if SCRIPT:
+ wikia.write("<th>Script</th>")
+ if DROPPER:
+ wikia.write("<th>Mobs</th>")
+
+ wikia.write("</tr>\n")
+
+ try:
+ if (sys.argv[1] in ["level", "info"]):
+ sort=sorted(tbl, key=lambda xcv: int(xcv.lvl))
+ elif (sys.argv[1] == "none"):
+ sort=tbl
+ else:
+ print("Syntax: ./sedesign.py [info|level|none]")
+ sort=tbl
+
+ if (sys.argv[1] == "info"):
+ stgen=False
+ except:
+ sort=tbl
+
+ for i in sort:
+ wikia.write('<tr>')
+
+ if ID:
+ wikia.write("<td><a name=\"%s\"></a>%s</td>" % (i.id,i.id))
+ if AEGIS:
+ wikia.write("<td>%s</td>" % hl(i))
+ if NAME:
+ wikia.write("<td>%s</td>" % i.name)
+ if PRICE:
+ wikia.write("<td>%s GP</td>" % i.price)
+ if WEIGHT:
+ wikia.write("<td>%s g</td>" % i.weight)
+ if DEF:
+ wikia.write("<td>Def: %s</td>" % i.defs)
+ if stgen:
+ lv=int(i.lvl)
+
+ bb=(lv**1.255)*2.5*i.fc
+ hc=bb*350.0/810.0 # Hercules value: Capped at 350
+
+ # Magic penalty
+ if ("bMatk," in i.script):
+ hc=hc/2.0
+
+ # Precise rounding
+ if (hc % 1 >= 0.5):
+ hc+=1
+ wikia.write("<td>Adj. Df: %d</td>" % int(hc))
+ if LVL:
+ wikia.write("<td>Lv: %s</td>" % i.lvl)
+ if ATK:
+ wikia.write("<td>Atk: %s</td>" % i.atk)
+ if MATK:
+ wikia.write("<td>%s</td>" % i.matk)
+ if ATK and MATK:
+ try:
+ tmpatmat=i.atk.replace("Atk: ", "")
+ #print("`%s`+`%s`=?" % (tmpatmat, i.matk))
+ wikia.write("<td><i>%s</i></td>" % str(int(i.matk)+int(tmpatmat)))
+ except:
+ wikia.write("<td><i>?</i></td>")
+ if ATK or MATK:
+ if stgen:
+ lv=int(i.lvl)
+ at=int(i.atk)
+ fc=7.5
+ ## Two hand swords are stronger
+ if i in IT_WEAPON['HAND_2']:
+ fc+=0.62
+ ## Calculate guns
+ if int(i.id) == 6010 or int(i.id) == 6050:
+ lv+=20
+ if int(i.id) == 6020:
+ lv=0
+ if int(i.id) == 6040:
+ fc/=2.5
+
+ ## Progression
+ if int(i.matk) <= 0 or int(at) > 0:
+ fc-=1.0
+ fc+=((100-lv)/100.0)
+
+ ## Even slower progression (note bows doesn't gets the 0.8 2hand bonus)
+ if not i in IT_WEAPON['RANGED']:
+ fc-=(lv/15.0)/10.0
+
+ ## After level 50 weapons progression is slowed down
+ if lv > 45:
+ fc-=(lv-45)/90.0
+
+ ## This is because mob HP scaling is buggy
+ if lv > 45 and not i in IT_WEAPON['RANGED']:
+ fc-=(lv-45)/100.0
+
+ ## Magic Weapons adjustment
+ if int(i.matk) > 0:
+ fc+=0.0
+
+ ## Physical and Magic Weapons adjustment
+ if int(i.matk) > 0 and int(at) > 0:
+ fc=7.0+((100-lv)/100.0)
+
+ ## HAT is for craft or rare items.
+ lat=lv*fc
+ hat=lv*(fc+max(0.01, 1.0-(lv/100.0)))
+
+ # Edge Cases
+ ## Don't recalculate noob weapon
+ if lv <= 20:
+ lat=hat=at
+
+ wikia.write("<th>%d ~ %d <i>(%.2f)</i></th>" % (lat, hat, fc))
+ if RANGE:
+ wikia.write("<td>%s</td>" % i.range)
+ if HEALING:
+ wikia.write("<td>%s</td>" % i.minheal)
+ wikia.write("<td>%s</td>" % i.maxheal)
+ wikia.write("<td>%s s</td>" % i.delheal)
+ if SCRIPT:
+ wikia.write("<td>%s</td>" % i.script)
+ if DROPPER:
+ wikia.write("<td>-</td>")
+
+ wikia.write("</tr>")
+
+ wikia.write("</table><br/>\n")
+ wikia.write("\n<br/><a href=#items>(↑) Return to top</a><Br/><br/>\n\n")
+
+def ArmorWrite(name,scope):
+ wikia.write("<h3>"+name+"</h3>\n")
+ ItemWrite(IT_ARMOR[scope], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, DEF=True, LVL=True, SCRIPT=True)
+
+
+
+
+
+
+
+
+
+
+
+showHeader()
+
+wikia.write("<html><head>")
+wikia.write('<title>SeDesign</title><meta charset=utf8 />\n')
+wikia.write("<body>")
+newItemDB()
+
+# Ending
+wikia.write("<hr/>")
+wikia.write("Run at: " + datetime.datetime.now().isoformat())
+wikia.write("</body></html>")
+wikia.close()
+
+# Print for reference the landmarks
+i=0
+while i < 100:
+ i+=10
+ bb=(i**1.255)*2.5
+ hc=bb*350.0/810.0 # Hercules value: Capped at 350
+ df=(100.0 - hc / (hc + 400.0) * 90.0) / 100.0 * 100
+ print("%d Level %d Defense (%.2f%% DMG)" % (i, hc, df) );
+
+showFooter()
+exit(0)
diff --git a/wiki/tmp-arm b/wiki/tmp-arm
new file mode 100644
index 0000000..53e733e
--- /dev/null
+++ b/wiki/tmp-arm
@@ -0,0 +1,247 @@
+"791": 0,
+"1151": 4,
+"1152": 8,
+"1153": 5,
+"1155": 0,
+"1156": 5,
+"1159": 2,
+"1161": 2,
+"1162": 2,
+"1163": 2,
+"1164": 8,
+"1165": 3,
+"1169": 5,
+"1170": 5,
+"1171": 5,
+"1178": 10,
+"3200": 2,
+"3201": 2,
+"3202": 0,
+"3203": 0,
+"3204": 1,
+"3205": 4,
+"3206": 0,
+"3207": 0,
+"3210": 1,
+"2900": 1,
+"2901": 0,
+"2902": 1,
+"2903": 3,
+"2904": 3,
+"2905": 3,
+"2906": 9,
+"2907": 9,
+"2908": 3,
+"2909": 5,
+"2910": 3,
+"2911": 3,
+"2913": 3,
+"2914": 4,
+"2915": 5,
+"2916": 4,
+"2917": 2,
+"2918": 2,
+"2919": 6,
+"2920": 6,
+"2921": 4,
+"2923": 1,
+"2924": 0,
+"2926": 0,
+"2927": 9,
+"2928": 3,
+"2929": 9,
+"2930": 1,
+"2932": 6,
+"2934": 0,
+"2935": 0,
+"2936": 1,
+"2937": 4,
+"2938": 0,
+"2939": 0,
+"2940": 4,
+"2941": 7,
+"2942": 8,
+"2943": 6,
+"2944": 4,
+"2945": 3,
+"2946": 1,
+"2983": 1,
+"2985": 2,
+"2986": 2,
+"2987": 3,
+"2989": 0,
+"2990": 0,
+"2991": 2,
+"2992": 1,
+"2993": 2,
+"2994": 2,
+"2995": 3,
+"2996": 15,
+"2997": 3,
+"2998": 4,
+"2999": 5,
+"3000": 2,
+"3001": 5,
+"3002": 3,
+"3003": 5,
+"3004": 8,
+"3005": 8,
+"3006": 1,
+"3007": 6,
+"3008": 7,
+"3010": 3,
+"3011": 2,
+"3012": 5,
+"3013": 3,
+"3014": 1,
+"3015": 2,
+"3017": 2,
+"3018": 6,
+"3019": 4,
+"3020": 7,
+"3021": 0,
+"3022": 1,
+"3023": 3,
+"3024": 3,
+"3028": 3,
+"3029": 3,
+"3030": 10,
+"1300": 0,
+"1301": 4,
+"1302": 0,
+"1303": 2,
+"1304": 3,
+"1305": 5,
+"1306": 7,
+"1307": 7,
+"1308": 5,
+"1309": 0,
+"1310": 4,
+"1311": 8,
+"1312": 6,
+"1313": 0,
+"1314": 2,
+"1315": 1,
+"1316": 2,
+"1317": 5,
+"1318": 4,
+"1319": 1,
+"1320": 3,
+"1321": 0,
+"1323": 8,
+"1324": 3,
+"1325": 0,
+"1326": 0,
+"1327": 0,
+"1328": 6,
+"1329": 7,
+"1330": 0,
+"1331": 10,
+"1332": 1,
+"1333": 8,
+"1334": 3,
+"1335": 0,
+"1336": 6,
+"1337": 7,
+"1338": 2,
+"1339": 5,
+"2200": 0,
+"2201": 3,
+"2202": 8,
+"2203": 7,
+"2204": 5,
+"2205": 1,
+"2206": 1,
+"2207": 3,
+"2208": 5,
+"2209": 1,
+"2210": 8,
+"2211": 6,
+"2212": 2,
+"2213": 4,
+"2214": 4,
+"2215": 7,
+"2216": 10,
+"2217": 0,
+"2218": 2,
+"2219": 2,
+"2220": 3,
+"1800": 1,
+"1801": 8,
+"1802": 5,
+"1803": 0,
+"1804": 3,
+"1805": 2,
+"1806": 2,
+"1807": 3,
+"1808": 4,
+"1809": 5,
+"1810": 6,
+"1811": 9,
+"1812": 7,
+"1813": 8,
+"1814": 7,
+"1815": 10,
+"1816": 0,
+"1817": 1,
+"1818": 2,
+"1819": 3,
+"1000": 4,
+"1001": 5,
+"1002": 4,
+"1003": 4,
+"1004": 4,
+"1005": 6,
+"1007": 4,
+"1008": 6,
+"1009": 8,
+"1010": 7,
+"1012": 2,
+"1013": 2,
+"1014": 4,
+"1015": 5,
+"1016": 5,
+"1017": 5,
+"1019": 5,
+"2500": 0,
+"2501": 4,
+"2510": 0,
+"2511": 0,
+"2512": 2,
+"2000": 3,
+"2001": 5,
+"2002": 7,
+"2003": 9,
+"2004": 0,
+"2005": 1,
+"2006": 2,
+"2007": 2,
+"2008": 4,
+"2009": 5,
+"2010": 6,
+"2011": 9,
+"2012": 9,
+"2013": 7,
+"2014": 7,
+"2015": 8,
+"2016": 0,
+"2700": 0,
+"2701": 1,
+"2702": 3,
+"2703": 7,
+"2704": 5,
+"2705": 10,
+"2706": 6,
+"2707": 0,
+"2708": 6,
+"2709": 4,
+"2710": 2,
+"2711": 8,
+"2712": 9,
+"2713": 1,
+"2714": 2,
+"2715": 2,
+"2716": 3,
+"2717": 3,
+"2718": 4,
+"2719": 4,
diff --git a/wiki/tmp-dec b/wiki/tmp-dec
new file mode 100644
index 0000000..8337ac2
--- /dev/null
+++ b/wiki/tmp-dec
@@ -0,0 +1,8 @@
+"3208": BurglarMask,
+"3209": BanditMask,
+"3211": AutumnMask,
+"2922": YetiMask,
+"2925": OperaMask,
+"2931": SkullMask,
+"3009": FafiMask,
+"3016": TerraniteMask,
diff --git a/wiki/tmp-etc b/wiki/tmp-etc
new file mode 100644
index 0000000..5e1d879
--- /dev/null
+++ b/wiki/tmp-etc
@@ -0,0 +1,25 @@
+Armor:
+ "1150": FourLeafClover,
+ "1154": Doll,
+ "1157": LeatherBall,
+ "1158": ZarkorScroll,
+ "1160": PlushMouboo,
+ "1166": Blanket,
+ "1167": EarthScroll,
+ "1168": CursedSkull,
+ "1172": LeatherQuiver,
+ "1173": IronQuiver,
+ "1174": BronzeQuiver,
+ "1175": PlatinumQuiver,
+ "1176": DragonStar,
+ "1177": MichelSoul,
+ "1179": RubberDucky,
+ "1006": LifestonePendant,
+ "1011": 1,
+ "1020": 2,
+ "1021": 1,
+ "2508": 2,
+ "2509": 3,
+ "2720": 0
+
+
diff --git a/wiki/tmp-wpn b/wiki/tmp-wpn
new file mode 100644
index 0000000..f86e7b4
--- /dev/null
+++ b/wiki/tmp-wpn
@@ -0,0 +1,59 @@
+"3500": 0,
+"3501": 2,
+"3502": 3,
+"3503": 2,
+"3504": 4,
+"3505": 1,
+"3506": 0,
+"3507": 1,
+"3508": 7,
+"3509": 9,
+"3510": 0,
+"3511": 10,
+"3512": 3,
+"3513": 8,
+"3514": 3,
+"3515": 6,
+"3516": 8,
+"3518": 2,
+"3519": 6,
+"3520": 2,
+"3527": 5,
+"3528": 3,
+"3529": 1,
+"3531": 6,
+"3536": 6,
+"3538": 6,
+"3539": 6,
+"3600": 99,
+"6040": 6,
+"3517": 4,
+"3521": 2,
+"3522": 6,
+"3523": 4,
+"3524": 5,
+"3525": 6,
+"3526": 7,
+"3530": 7,
+"3532": 9,
+"3533": 7,
+"3534": 8,
+"3537": 6,
+"3601": 99,
+"3602": 99,
+"3603": 99,
+"6000": 0,
+"6001": 1,
+"6002": 2,
+"6003": 4,
+"6004": 6,
+"6005": 8,
+"6006": 10,
+"6010": 6,
+"6030": 6,
+"6050": 7,
+"7020": 6,
+"7400": 4,
+"7401": 5,
+"7421": 10,
+"3604": 99,
diff --git a/wiki/webwikigen.py b/wiki/webwikigen.py
new file mode 100755
index 0000000..51a8d7a
--- /dev/null
+++ b/wiki/webwikigen.py
@@ -0,0 +1,758 @@
+#! /usr/bin/env python3
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2010-2011 Evol Online
+# Copyright (C) 2018 TMW-2
+# Author: Andrei Karas (4144)
+# Author: Jesusalva
+
+import datetime
+
+wikia=open("Items.md", "w")
+wikib=open("Monsters.md", "w")
+wikic=open("../../client-data/x.diff", "w")
+
+# the TYPEs we use to determine where to pack things
+IT_HEALING=[]
+IT_ETC=[]
+IT_USABLE=[]
+IT_AMMO=[]
+IT_CARD=[]
+IT_PETEGG=[]
+IT_WEAPON={ 'HAND_2': [], # TWO HAND (LR)
+ 'HAND_1':[]} # WEAPONS (R)
+IT_ARMOR={ 'MISC': [], # FOR FAILURE
+ 'EQP_ACC_L': [], # ACCESSORY LEFT
+ 'EQP_ACC_R': [], # ACCESSORT RIGHT
+ 'EQP_HEAD_MID': [], # CHEST
+ 'EQP_SHOES': [], # FEET
+ 'EQP_GARMENT': [], # GLOVES
+ 'EQP_HEAD_LOW':[], # PANTS
+ '1024': [], # NECKLACES (should be EQP_COSTUME_HEAD_TOP instead of number)
+ '2048': [], # RINGS (should be EQP_COSTUME_HEAD_MID instead of number)
+ 'EQP_HEAD_TOP':[], # HATS/HELMETS
+ 'EQP_HAND_L': []} # SHIELDS
+
+Mobs1=[]
+Mobs2=[]
+Mobs3=[]
+Mobs4=[]
+Mobs5=[]
+MobsA=[]
+
+SysDrops=[]
+
+
+def printSeparator():
+ print("--------------------------------------------------------------------------------")
+
+def showHeader():
+ print("TMW2 Wiki Generator")
+ ##print "Evol client data validator."
+ print("Run at: " + datetime.datetime.now().isoformat())
+ ##print "https://gitlab.com/evol/evol-tools/blob/master/testxml/testxml.py"
+ printSeparator()
+
+def showFooter():
+ #pass
+ #printSeparator()
+ print("Done.")
+
+
+
+
+
+def fwalk(wmask):
+ if wmask == 'WATER':
+ return '<font color=#00f>%s</font>' % (wmask)
+ elif wmask == 'AIR':
+ return '<font color=#093>%s</font>' % (wmask)
+ elif wmask == 'WALL':
+ return '<font color=#f00>%s</font>' % (wmask)
+ elif wmask == 'NORMAL' or wmask == 'DEFAULT':
+ return '<font color=#111>%s</font>' % (wmask)
+ else:
+ print("Invalid walk mask: "+wmask)
+ exit(1)
+
+def WhatRace(rac):
+ rc=rac.race
+ if rc == 0:
+ return "Formless"
+ elif rc == 1:
+ return "Undead"
+ elif rc == 2:
+ return "Brute"
+ elif rc == 3:
+ return "Plant"
+ elif rc == 4:
+ return "Insect"
+ elif rc == 5:
+ return "Fish"
+ elif rc == 6:
+ return "-"
+ elif rc == 7:
+ return "SemiHuman"
+ elif rc == 8:
+ return "Angel"
+ elif rc == 9:
+ return "Dragon"
+ elif rc == 10:
+ return "Player"
+ elif rc == 11:
+ return "Boss"
+ elif rc == 12:
+ return "NonBoss"
+ elif rc == 14:
+ return "NonSemiHuman"
+ elif rc == 15:
+ return "NonPlayer"
+ elif rc == 16:
+ return "SemiPlayer"
+ elif rc == 17:
+ return "NonSemiPlayer"
+ else:
+ print("ERROR, INVALID RACE ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+
+def WhatElem(rac):
+ rc=rac.elem
+ tl="ERROR"
+ cl="#F00"
+ if rc == 0:
+ tl,cl="Neutral","#000"
+ elif rc == 1:
+ tl,cl="Water","#00F"
+ elif rc == 2:
+ tl,cl="Earth","#7A0"
+ elif rc == 3:
+ tl,cl="Fire","#F00"
+ elif rc == 4:
+ tl,cl="Wind","#093"
+ elif rc == 5:
+ tl,cl="Poison","#040"
+ elif rc == 6:
+ tl,cl="Holy","#afa"
+ elif rc == 7:
+ tl,cl="Dark","#908"
+ elif rc == 8:
+ tl,cl="Ghost","#404"
+ elif rc == 9:
+ tl,cl="Undead","#440"
+ else:
+ print("ERROR, INVALID ELEM ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+ return "<font color=%s>%s</font>" % (cl, tl)
+
+class Mob:
+ def __init__(self):
+ # Basic
+ self.id="0"
+ #self.aegis="UnknownMonster" # SpriteName is not used anywhere, we are using its ID
+ self.name="Unknown Monster Name"
+ self.view="1"
+
+ # Defensive
+ self.mobpt="0" # Mob Points “Level”
+ self.hp="0"
+ self.xp="0"
+ self.jp="0"
+ self.st=""
+
+ # Offensive
+ self.atk="[0, 0]"
+ self.range="0"
+ self.move="0"
+ self.delay="0"
+ self.drops=[]
+
+ # Elegen Info
+ self.race=-1
+ self.elem=-1
+ self.elel=-1
+ self.walk="NORMAL"
+
+def MobAlloc(ab):
+ try:
+ maab=int(ab.mobpt)
+ except:
+ maab=9901
+
+ if maab <= 20:
+ Mobs1.append(ab)
+ elif maab <= 40:
+ Mobs2.append(ab)
+ elif maab <= 60:
+ Mobs3.append(ab)
+ elif maab <= 80:
+ Mobs4.append(ab)
+ elif maab <= 100:
+ Mobs5.append(ab)
+ else:
+ MobsA.append(ab)
+
+def testMobs():
+ print("Generating Mob Wiki...")
+ src=open("../../server-data/db/re/mob_db.conf", "r")
+ wikib.write("<h1 id='monster-database'>Monster Database</h1>\n")
+ start=False
+ dropper=False
+ x=Mob() # Only for pyflakes2
+
+ for a in src:
+ if a == "{\n":
+ if start:
+ MobAlloc(x)
+ else:
+ start=True
+ x=Mob()
+
+ if " Id:" in a:
+ x.id=stp(a)
+ if x.id == "ID":
+ continue
+
+ if " Name:" in a:
+ x.name=stp(a)
+ elif " Hp:" in a:
+ x.hp=stp(a)
+ elif " Lv:" in a:
+ x.mobpt=stp(a)
+ elif " Exp:" in a:
+ x.xp=stp(a)
+ elif " JExp:" in a:
+ x.jp=stp(a)
+ elif " Attack:" in a:
+ x.atk=stp(a)
+ elif " AttackRange:" in a:
+ x.range=stp(a)
+ elif " MoveSpeed:" in a:
+ x.move=stp(a)
+ elif " ViewRange:" in a:
+ x.view=stp(a)
+ elif " AttackDelay:" in a:
+ x.delay=stp(a)
+ elif " Looter: true" in a:
+ x.st+="<font color=#790>Lot</font>,"
+ elif " Assist: true" in a:
+ x.st+="<font color=#0a0>Ass</font>,"
+ elif " Aggressive: true" in a:
+ x.st+="<font color=#f00>Agr</font>,"
+ elif " WalkMask:" in a:
+ x.walk=stp(a)
+ elif " Element:" in a:
+ tmp=stp(a)
+ tmp2=tmp.split(',')
+ try:
+ x.elem=int(tmp2[0])
+ x.elel=int(tmp2[1])
+ except:
+ print("Invalid Element for mob %s: %s" % (x.id, tmp))
+ exit(1)
+ elif " Race:" in a:
+ try:
+ x.race=int(stp(a))
+ except:
+ print("Invalid Race for mob %s: %s" % (x.id, a))
+ exit(1)
+ elif 'Drops: ' in a:
+ dropper=True
+ elif dropper and '}' in a:
+ dropper=False
+ elif dropper:
+ x.drops.append(stp(a).split(": "))
+ # Write last entry
+ MobAlloc(x)
+
+ writeMob()
+
+ wikib.write('\n\n|Mode|Desc|\n|----|----|\n')
+ wikib.write('|Lot|Looter|\n')
+ wikib.write('|Ass|Assist|\n')
+ wikib.write('|Agr|Aggressive|\n')
+
+ src.close()
+
+def stp(x):
+ return x.replace('\n', '').replace('|', '').replace('(int, defaults to ', '').replace(')', '').replace('basic experience', '').replace('"','').replace(" ","").replace("\t","").replace('(string', '').replace('Name: ','').replace('Element: ','').replace('Race: ','').replace('AttackDelay: ', '').replace('WalkMask: ','').replace('MoveSpeed: ', '').replace('AttackRange: ', '').replace('ViewRange: ','').replace('Attack: ','').replace('ViewRange: ','').replace('Hp: ','').replace('Id: ','').replace('Lv: ','').replace('view range','').replace('attack range','').replace('move speed','').replace('health','').replace('(int','').replace('attack delay','atk.').replace('(','').replace(')','').replace('WALK_','')
+
+
+def MonsterWrite(tbl):
+ # TODO: Check _mobs files to determine the usual monster density (a misc info to aid adding proper drop specs)
+ wikib.write("<table border=1>\n")
+ wikib.write("<tr><th>ID</th><th>Name</th><th>Mob Info</th><th>Elegen</th><th>Misc Info</th><th>Rewards</th><th>Drops</th></tr>\n")
+ for i in tbl:
+ if i.id == 'ID':
+ continue
+ wikib.write('<tr><td><a name="' + i.id + '"></a>' +
+ i.id +"</td><td>"+
+ i.name +"</td><td>"+
+ mb_core(i) +"</td><td>"+
+ mb_eleg(i) +"</td><td>"+
+ mbdt('misc', mb_rdmisc(i)) +"</td><td>"+
+ mbdt('Exp\'s', mb_rdrw(i)) +"</td><td>"+
+ mbdt('drops', mb_rddrop(i)) +"</td></tr>\n"
+ )
+ wikib.write("</table>\n")
+ wikib.write("\n<a href=#monster-database>(↑) Return to top</a>\n\n")
+
+def writeMob():
+ wikib.write("\
++ [Level 0-20](#starter)\n\
++ [Level 21-40](#apprentice)\n\
++ [Level 41-60](#intermediary)\n\
++ [Level 61-80](#advanced)\n\
++ [Level 81-100](#expert)\n\
++ [Level 100+](#out-of-scope)\n\n\
+ ")
+
+ wikib.write("## Starter\n\n")
+ MonsterWrite(Mobs1)
+
+ wikib.write("## Apprentice\n\n")
+ MonsterWrite(Mobs2)
+
+ wikib.write("## Intermediary\n\n")
+ MonsterWrite(Mobs3)
+
+ wikib.write("## Advanced\n\n")
+ MonsterWrite(Mobs4)
+
+ wikib.write("## Expert\n\n")
+ MonsterWrite(Mobs5)
+
+ wikib.write("## Out Of Scope\n\n")
+ MonsterWrite(MobsA)
+
+
+def mbdt(summary, content):
+ return "<details>\
+<summary>"+summary+"</summary>\
+<pre>"+content+"</pre></details>"
+
+def mb_core(mb):
+ buff=""
+ buff+="Lvl: %s<br/>\n" % (mb.mobpt)
+ buff+="HP: <b><font color=#0a0>%s</font></b><br/>\n" % (mb.hp)
+ buff+="ATK: <b><font color=#a00>%s</font></b><br/>\n" % (mb.atk)
+ if mb.st != "":
+ buff+="Modes: <i>%s</i>" % (mb.st)
+ return buff
+
+def mb_eleg(mb):
+ buff=""
+ buff+="Race: %s<br/>\n" % (WhatRace(mb))
+ buff+="Walk: %s<br/>\n" % (fwalk(mb.walk))
+ buff+="Element: %s<br/>\n" % (WhatElem(mb))
+ return buff
+
+def mb_rdmisc(mb):
+ buff=""
+ if "agr" in mb.st.lower():
+ buff+="View Range: %s\n" % (mb.view)
+ buff+="Attack Range: %s\n" % (mb.range)
+ buff+="AtkDelay: %s ms\n" % (mb.delay)
+ buff+="Move speed: %s ms\n" % (mb.move)
+ buff+="Element Level: %d\n" % (mb.elel)
+ return buff
+
+def mb_rdrw(mb):
+ buff=""
+ try:
+ buff+="MobPoints: %d\n" % (int(mb.mobpt)*11/10)
+ except:
+ pass
+ buff+="%s\n" % (mb.xp)
+ buff+="%s\n" % (mb.jp)
+ return buff
+
+def mb_rddrop(mb):
+ buff=""
+ for ax in mb.drops:
+ # Ignore disabled drops
+ if ax[0].startswith("//"):
+ continue
+
+ # Write drop
+ try:
+ buff+=ax[0]+': ' + str(int(ax[1])/100.0) + ' %\n'
+ except IndexError:
+ print("Fatal: invalid %s mob with %s drops" % (mb.name, str(ax)))
+ exit(1)
+ except:
+ print("[Warning] %s incorrect drop: %s" % (mb.name, str(ax)))
+ buff+=ax[0]+': ' + ax[1] + ' ppm\n'
+
+ # Save to SysDrops
+ SysDrops.append([ax[0], ax[1], mb.name])
+
+ return buff
+
+
+class It:
+ def __init__(self):
+ # Basic
+ self.id="0"
+ self.aegis="UnknownItem"
+ self.name="Unknown Item Name"
+ self.price="0" # Sell price, of course
+ self.weight="0"
+ self.type="IT_ETC" # default type
+ self.loc=""
+
+ # Offensive/Defensive
+ self.atk="0"
+ self.matk="0"
+ self.range="0"
+ self.defs="0"
+
+ # Restrictions (EquipLv)
+ self.lvl="0"
+ self.drop=True
+ self.trade=True
+ self.sell=True
+ self.store=True
+
+ # Special settings
+ self.rare=False # DropAnnounce
+ self.script=False
+
+ # Visual
+ self.sl="0" # Slots
+ self.ac=False # Allow Cards
+
+ # Script settings
+ self.minheal="0"
+ self.maxheal="0"
+ self.delheal="0"
+
+def ItAlloc(it):
+ if (it.sl == "0" and it.ac) or (it.sl in ["1","2","3","4"] and not it.ac):
+ print("WARNING, item id "+it.id+" invalid dye/card setting!")
+ if (len(it.sl) > 1):
+ print("WARNING, item id "+it.id+" bad slots length: %d (%s)" % (len(it.sl), it.sl))
+ if it.ac:
+ wikic.write(it.id + ": " + it.name + "\n")
+
+ a=it.type
+ if "IT_HEALING" in a:
+ IT_HEALING.append(it)
+ elif "IT_ETC" in a:
+ IT_ETC.append(it)
+ elif "IT_USABLE" in a:
+ IT_USABLE.append(it)
+ elif "IT_AMMO" in a:
+ IT_AMMO.append(it)
+ elif "IT_CARD" in a:
+ IT_CARD.append(it)
+ elif "IT_PETEGG" in a:
+ IT_PETEGG.append(it)
+
+ elif "IT_WEAPON" in a:
+ if "HAND_L" in it.loc or "EQP_ARMS" in it.loc:
+ IT_WEAPON["HAND_2"].append(it)
+ elif "HAND_R" in it.loc:
+ IT_WEAPON["HAND_1"].append(it)
+ else:
+ raise Exception("Invalid location for weapon: %s" % it.loc)
+
+ elif "IT_ARMOR" in a:
+ if 'EQP_ACC_L' in it.loc:
+ IT_ARMOR['EQP_ACC_L'].append(it)
+ elif 'EQP_ACC_R' in it.loc:
+ IT_ARMOR['EQP_ACC_R'].append(it)
+ elif 'EQP_HEAD_MID' in it.loc:
+ IT_ARMOR['EQP_HEAD_MID'].append(it)
+ elif 'EQP_SHOES' in it.loc:
+ IT_ARMOR['EQP_SHOES'].append(it)
+ elif 'EQP_GARMENT' in it.loc:
+ IT_ARMOR['EQP_GARMENT'].append(it)
+ elif 'EQP_HEAD_LOW' in it.loc:
+ IT_ARMOR['EQP_HEAD_LOW'].append(it)
+ elif 'EQP_HEAD_TOP' in it.loc:
+ IT_ARMOR['EQP_HEAD_TOP'].append(it)
+ elif 'EQP_HAND_L' in it.loc:
+ IT_ARMOR['EQP_HAND_L'].append(it)
+ elif '1024' in it.loc:
+ IT_ARMOR['1024'].append(it)
+ elif '2048' in it.loc:
+ IT_ARMOR['2048'].append(it)
+ elif 'EQP_SHADOW_ACC_R' in it.loc:
+ IT_ARMOR['EQP_ACC_R'].append(it)
+ else:
+ raise Exception("Invalid Loc for ID %s: %s" % (it.id, it.loc))
+
+def newItemDB():
+ print("Generating Item Wiki...")
+ src=open("../../server-data/db/re/item_db.conf", "r")
+
+ x=It()
+ for a in src:
+ if a == "{\n":
+ ItAlloc(x)
+ x=It()
+
+ # sti() block
+ if " Id:" in a:
+ x.id=sti(a)
+ elif " AegisName:" in a:
+ x.aegis=sti(a)
+ elif " Name:" in a:
+ x.name=stin(a)
+ elif " Sell:" in a:
+ x.price=sti(a)
+ elif " Weight:" in a:
+ x.weight=sti(a)
+ elif " Type:" in a:
+ x.type=sti(a)
+ elif " Loc:" in a:
+ x.loc=sti(a)
+ elif " Atk:" in a:
+ x.atk=sti(a)
+ elif " Matk:" in a:
+ x.matk=sti(a)
+ elif " Range:" in a:
+ x.range=sti(a)
+ elif " Def:" in a:
+ x.defs=sti(a)
+ elif " EquipLv:" in a:
+ x.lvl=sti(a)
+ elif " Slots:" in a:
+ x.sl=sti(a)
+ elif " AllowCards:" in a:
+ x.ac=True
+ # Write booleans
+ elif "DropAnnounce: true" in a:
+ x.rare=True
+ elif "nodrop: true" in a:
+ x.drop=False
+ elif "notrade: true" in a:
+ x.trade=False
+ elif "noselltonpc: true" in a:
+ x.sell=False
+ elif "nostorage: true" in a:
+ x.store=False
+ elif "Script" in a:
+ x.script=True
+ # For healing items
+ elif "@min" in a:
+ x.minheal=sti(a)
+ elif "@max" in a:
+ x.maxheal=sti(a)
+ elif "@delay" in a:
+ x.delheal=sti(a)
+
+ # Write last entry
+ ItAlloc(x)
+ writeItems()
+
+ src.close()
+
+def sti(x):
+ return x.replace('\n', '').replace('|', '').replace(')', '').replace('Id: ', '').replace('"','').replace(" ","").replace("\t","").replace('AegisName: ', '').replace('Name: ','').replace('Sell: ', '').replace('Weight: ', '').replace('Type: ', '').replace('Loc: ', '').replace('Atk: ', '').replace('Matk: ', '').replace('Range: ', '').replace('Def: ', '').replace('EquipLv: ', '').replace('Slots: ','').replace(" ", "").replace('@min=','').replace('@max=','').replace('@delay=','').replace(';','')
+
+def stin(x):
+ return x.replace('\n', '').replace('|', '').replace(')', '').replace('Id: ', '').replace('"','').replace(" ","").replace("\t","").replace('Name: ','').replace(';','')
+
+
+def writeItems():
+ wikia.write("# Items\n\
++ [Healing Items](#healing-items)\n\
++ [Usable Items](#usable-items)\n\
++ [Generic Items](#generic-items)\n\
++ [Ammo](#ammo)\n\
++ [Cards](#cards)\n\
++ [Pet Eggs](#pet-eggs)\n\
++ [Weapons](#weapons)\n\
+ + [1H Weapons](#1h-weapons)\n\
+ + [2H Weapons](#2h-weapons)\n\
++ [Armors](#armors)\n\
+ + [Left Accessory](#left-accessory)\n\
+ + [Right Accessory](#right-accessory)\n\
+ + [Headgear](#headgear)\n\
+ + [Chest](#chest)\n\
+ + [Pants](#pants)\n\
+ + [Shoes](#shoes)\n\
+ + [Necklaces](#necklaces)\n\
+ + [Rings](#rings)\n\
+ + [Gloves](#gloves)\n\
+ + [Shields](#shields)\n\
+\n\n")
+ wikia.write("#### Restrictions Reference\n")
+ wikia.write("Special Aegis Name Markers:\n\
++ * - Rare item with drop announce.\n\
++ (dp) - This item cannot be dropped.\n\
++ (tr) - This item cannot de traded.\n\
++ (sl) - This item cannot be sold.\n\
++ (gg) - This item cannot go to storage.\n\n")
+
+ # Healing Items
+ wikia.write("## Healing Items\n\n")
+ ItemWrite(IT_HEALING, ID=True, AEGIS=True, PRICE=True, WEIGHT=True, HEALING=True, DROPPER=True)
+
+ # Usable Items
+ wikia.write("## Usable Items\n")
+ ItemWrite(IT_USABLE, ID=True, AEGIS=True, NAME=True, PRICE=True, WEIGHT=True, DROPPER=True)
+
+ # Generic Items
+ wikia.write("## Generic Items\n")
+ ItemWrite(IT_ETC, ID=True, AEGIS=True, NAME=True, PRICE=True, WEIGHT=True, DROPPER=True)
+
+ # Ammo Items
+ wikia.write("## Ammo\n")
+ ItemWrite(IT_AMMO, ID=True, AEGIS=True, NAME=True, WEIGHT=True, ATK=True)
+
+ # Card Items
+ wikia.write("## Cards\n")
+ ItemWrite(IT_CARD, ID=True, AEGIS=True, NAME=True, PRICE=True, WEIGHT=True)
+
+ # Pet Egg Items
+ wikia.write("## Pet Eggs\n")
+ ItemWrite(IT_PETEGG, ID=True, AEGIS=True, NAME=True, WEIGHT=True)
+
+ ####################################################################
+ wikia.write("# Weapons\n")
+
+ # 1 Hand Items
+ wikia.write("## 1H Weapons\n")
+ ItemWrite(IT_WEAPON['HAND_1'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, LVL=True, DROPPER=True)
+
+ # 2 Hand Items
+ wikia.write("## 2H Weapons\n")
+ ItemWrite(IT_WEAPON['HAND_2'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, LVL=True, RANGE=True)
+
+
+ ####################################################################
+ wikia.write("# Armors\n")
+
+ ArmorWrite("Left Accessory",'EQP_ACC_L')
+ ArmorWrite("Right Accessory",'EQP_ACC_R')
+ ArmorWrite("Headgear",'EQP_HEAD_TOP')
+ ArmorWrite("Chest",'EQP_HEAD_MID')
+ ArmorWrite("Pants",'EQP_HEAD_LOW')
+ ArmorWrite("Shoes",'EQP_SHOES')
+ ArmorWrite("Necklaces",'1024')
+ ArmorWrite("Rings",'2048')
+ ArmorWrite("Gloves",'EQP_GARMENT')
+ ArmorWrite("Shields",'EQP_HAND_L')
+
+# Write AegisName with restrictions
+def hl(it):
+ buff=""
+ if it.rare:
+ buff+="*"
+ buff+=it.aegis
+ buff+=" "
+ if not it.drop:
+ buff+="<a href='#restrictions-reference'>(dp)</a>"
+ if not it.trade:
+ buff+="<a href='#restrictions-reference'>(tr)</a>"
+ if not it.sell:
+ buff+="<a href='#restrictions-reference'>(sl)</a>"
+ if not it.store:
+ buff+="<a href='#restrictions-reference'>(gg)</a>"
+ return buff
+
+# wikia.write("Id|Aegis|Name|Weight|Atk|Matk|\n")
+# wikia.write("Id|Aegis|Name|Price|Weight|\n")
+
+def ItemWrite(tbl, ID=False, AEGIS=False, NAME=False, PRICE=False, WEIGHT=False, DEF=False, LVL=False, ATK=False, RANGE=False, HEALING=False, SCRIPT=False, DROPPER=False):
+ wikia.write("<table border=1>\n")
+ wikia.write("<tr>")
+ if ID:
+ wikia.write("<th>ID</th>")
+ if AEGIS:
+ wikia.write("<th>Aegis</th>")
+ if NAME:
+ wikia.write("<th>Name</th>")
+ if PRICE:
+ wikia.write("<th>Price</th>")
+ if WEIGHT:
+ wikia.write("<th>Weight</th>")
+ if DEF:
+ wikia.write("<th>Def</th>")
+ if LVL:
+ wikia.write("<th>Lvl</th>")
+ if ATK:
+ wikia.write("<th>Atk</th>")
+ wikia.write("<th>Matk</th>")
+ if RANGE:
+ wikia.write("<th>Range</th>")
+ if HEALING:
+ wikia.write("<th>Min</th>")
+ wikia.write("<th>Max</th>")
+ wikia.write("<th>Delay</th>")
+ if SCRIPT:
+ wikia.write("<th>Script</th>")
+ if DROPPER:
+ wikia.write("<th>Mobs</th>")
+
+ wikia.write("</tr>\n")
+
+ for i in tbl:
+ wikia.write('<tr>')
+
+ if ID:
+ wikia.write("<td><a name=\"%s\"></a>%s</td>" % (i.id,i.id))
+ if AEGIS:
+ wikia.write("<td>%s</td>" % hl(i))
+ if NAME:
+ wikia.write("<td>%s</td>" % i.name)
+ if PRICE:
+ wikia.write("<td>%s GP</td>" % i.price)
+ if WEIGHT:
+ wikia.write("<td>%s g</td>" % i.weight)
+ if DEF:
+ wikia.write("<td>Def: %s</td>" % i.defs)
+ if LVL:
+ wikia.write("<td>Lv: %s</td>" % i.lvl)
+ if ATK:
+ wikia.write("<td>Atk: %s</td>" % i.atk)
+ wikia.write("<td>%s</td>" % i.matk)
+ if RANGE:
+ wikia.write("<td>%s</td>" % i.range)
+ if HEALING:
+ wikia.write("<td>%s</td>" % i.minheal)
+ wikia.write("<td>%s</td>" % i.maxheal)
+ wikia.write("<td>%s s</td>" % i.delheal)
+ if SCRIPT:
+ wikia.write("<td>%s</td>" % i.script)
+ # TODO: Check for item Aegis in npc/ folder too, to determine shops and quests.
+ if DROPPER:
+ tmp_droppers=""
+ tmp_drpalign=[]
+ for ax in SysDrops:
+ if ax[0] == i.aegis:
+ tmp_drpalign.append([ax[2], ax[1]])
+ if len(tmp_drpalign) > 0:
+ for a in sorted(tmp_drpalign, key=lambda xcv: xcv[1], reverse=True):
+ try:
+ ppm=int(a[1])/100.0
+ tmp_droppers+=("%s: %.2f %% \n" % (a[0], ppm))
+ except:
+ print("[Warning] %s whodrop error: %s" % (i.name, str(a)))
+ wikia.write("<td>%s</td>" % mbdt("monsters", tmp_droppers))
+ else:
+ wikia.write("<td>-</td>")
+
+ wikia.write("</tr>")
+
+ wikia.write("</table>\n")
+ wikia.write("\n[(↑) Return to top](#items)\n\n")
+
+def ArmorWrite(name,scope):
+ wikia.write("## "+name+"\n")
+ ItemWrite(IT_ARMOR[scope], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, DEF=True, LVL=True, SCRIPT=True)
+
+showHeader()
+
+testMobs()
+newItemDB()
+
+wikia.close()
+wikib.close()
+wikic.close()
+#print(str(SysDrops))
+
+showFooter()
+exit(0)
diff --git a/wiki/wikigen.py b/wiki/wikigen.py
new file mode 100755
index 0000000..c5ce71e
--- /dev/null
+++ b/wiki/wikigen.py
@@ -0,0 +1,898 @@
+#! /usr/bin/env python3
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2010-2011 Evol Online
+# Copyright (C) 2018 TMW-2
+# Author: Andrei Karas (4144)
+# Author: Jesusalva
+
+import datetime
+import sys
+
+wikia=open("Items.md", "w")
+wikib=open("Monsters.md", "w")
+wikic=open("../../client-data/dyes.diff", "w") # Dye Report
+wikid=open("../../server-data/changechase.diff", "w") # ChangeChase Report
+
+# the TYPEs we use to determine where to pack things
+IT_HEALING=[]
+IT_ETC=[]
+IT_USABLE=[]
+IT_AMMO=[]
+IT_CARD=[]
+IT_PETEGG=[]
+IT_WEAPON={ 'HAND_2': [], # TWO HAND (LR)
+ 'HAND_1':[]} # WEAPONS (R)
+IT_ARMOR={ 'MISC': [], # FOR FAILURE
+ 'EQP_ACC_L': [], # ACCESSORY LEFT
+ 'EQP_ACC_R': [], # ACCESSORT RIGHT
+ 'EQP_HEAD_MID': [], # CHEST
+ 'EQP_SHOES': [], # FEET
+ 'EQP_GARMENT': [], # GLOVES
+ 'EQP_HEAD_LOW':[], # PANTS
+ '1024': [], # NECKLACES (should be EQP_COSTUME_HEAD_TOP instead of number)
+ '2048': [], # RINGS (should be EQP_COSTUME_HEAD_MID instead of number)
+ 'EQP_MOUNT':[], # MOUNTS (ie. EQP_SHADOW_SHOES)
+ 'EQP_HEAD_TOP':[], # HATS/HELMETS
+ 'EQP_HAND_L': []} # SHIELDS
+
+Mobs1=[]
+Mobs2=[]
+Mobs3=[]
+Mobs4=[]
+Mobs5=[]
+Mobs6=[]
+MobsA=[]
+
+SysDrops=[]
+
+
+def printSeparator():
+ print("--------------------------------------------------------------------------------")
+
+def showHeader():
+ print("TMW2 Wiki Generator")
+ ##print "Evol client data validator."
+ print("Run at: " + datetime.datetime.now().isoformat())
+ print("Usage: ./wikigen.py [<path_to_serverdata> <path_to_clientdata>]")
+ ##print "https://gitlab.com/evol/evol-tools/blob/master/testxml/testxml.py"
+ printSeparator()
+
+def showFooter():
+ #pass
+ #printSeparator()
+ print("Done.")
+
+
+
+
+
+
+class Mob:
+ def __init__(self):
+ # Basic
+ self.id="0"
+ #self.aegis="UnknownMonster" # SpriteName is not used anywhere, we are using its ID
+ self.name="Unknown Monster Name"
+ self.view="1"
+ self.chch=False
+ self.boss=False
+
+ # Defensive
+ self.mobpt="0" # Mob Points “Level”
+ self.hp="0"
+ self.xp="0"
+ self.jp="0"
+ self.st=""
+
+ # Offensive
+ self.atk="[0, 0]"
+ self.range="0"
+ self.move="0"
+ self.delay="0"
+ self.drops=[]
+
+def MobAlloc(ab):
+ try:
+ maab=int(ab.mobpt)
+ except:
+ maab=9901
+
+ if maab <= 20:
+ Mobs1.append(ab)
+ elif maab <= 40:
+ Mobs2.append(ab)
+ elif maab <= 60:
+ Mobs3.append(ab)
+ elif maab <= 80:
+ Mobs4.append(ab)
+ elif maab <= 100:
+ Mobs5.append(ab)
+ elif maab <= 150:
+ Mobs6.append(ab)
+ elif maab != 9901:
+ MobsA.append(ab)
+ else:
+ print("WARNING, Disregarding \"%s\" (ID: %s) as invalid mob" % (ab.name, ab.id))
+
+def testMobs():
+ print("\nGenerating Mob Wiki...")
+ if len(sys.argv) >= 2:
+ src=open(sys.argv[1]+"/db/re/mob_db.conf", "r")
+ else:
+ src=open("../../server-data/db/re/mob_db.conf", "r")
+
+ wikib.write("# Monster Database\n")
+ start=False
+ dropper=False
+ x=Mob() # Only for pyflakes2
+
+ for a in src:
+ if a == "{\n":
+ if start:
+ MobAlloc(x)
+ else:
+ start=True
+ x=Mob()
+
+ if " Id:" in a:
+ x.id=stp(a)
+ elif " Name:" in a:
+ x.name=stp(a)
+ elif " Hp:" in a:
+ x.hp=stp(a)
+ elif " Lv:" in a:
+ x.mobpt=stp(a)
+ elif " Exp:" in a:
+ x.xp=stp(a)
+ elif " JExp:" in a:
+ x.jp=stp(a)
+ elif " Attack:" in a:
+ x.atk=stp(a)
+ elif " AttackRange:" in a:
+ x.range=stp(a)
+ elif " MoveSpeed:" in a:
+ x.move=stp(a)
+ elif " ViewRange:" in a:
+ x.view=stp(a)
+ elif " AttackDelay:" in a:
+ x.delay=stp(a)
+ elif " Boss: true" in a:
+ x.boss=True
+ elif " Looter: true" in a:
+ x.st+="Lot,"
+ elif " Assist: true" in a:
+ x.st+="Ass,"
+ elif " Aggressive: true" in a:
+ x.st+="Agr,"
+ elif " ChangeChase: true" in a:
+ x.chch=True
+ elif 'Drops: ' in a:
+ dropper=True
+ elif dropper and '}' in a:
+ dropper=False
+ elif dropper:
+ x.drops.append(stp(a).split(": "))
+ # Write last entry
+ MobAlloc(x)
+
+ writeMob()
+
+ wikib.write('\n\n|Mode|Desc|\n|----|----|\n')
+ wikib.write('|Lot|Looter|\n')
+ wikib.write('|Ass|Assist|\n')
+ wikib.write('|Agr|Aggressive|\n')
+
+ src.close()
+
+def stp(x):
+ return x.replace('\n', '').replace('|', '').replace('(int, defaults to ', '').replace(')', '').replace('basic experience', '').replace('"','').replace(" ","").replace("\t","").replace('(string', '').replace('Name: ','').replace('AttackDelay: ', '').replace('MoveSpeed: ', '').replace('AttackRange: ', '').replace('ViewRange: ','').replace('Attack: ','').replace('ViewRange: ','').replace('Hp: ','').replace('Id: ','').replace('Lv: ','').replace('view range','').replace('attack range','').replace('move speed','').replace('health','').replace('(int','').replace('attack delay','atk.')
+
+
+def MonsterWrite(tbl):
+ # TODO: Check _mobs files to determine the usual monster density (a misc info to aid adding proper drop specs)
+ wikib.write("<table border=1>\n")
+ wikib.write("<tr><th>ID</th><th>Name</th><th>HP</th><th>Atk</th><th>Delay</th><th>Modes</th><th>Misc Info</th><th>Rewards</th><th>Drops</th></tr>\n")
+ for i in tbl:
+ if not i.chch:
+ wikid.write("%s:%s\n" % (i.id, i.name))
+ if i.boss:
+ i.name="<b>"+i.name+"</b>"
+ wikib.write('<tr><td><a name="' + i.id + '"></a>' +
+ i.id +"</td><td>"+
+ i.name +"</td><td>HP: "+
+ i.hp +"</td><td>Atk: "+
+ i.atk +"</td><td>"+
+ i.delay +" ms</td><td>"+
+ i.st +"</td><td>"+
+ mbdt('misc', mb_rdmisc(i)) +"</td><td>"+
+ mbdt('Exp\'s', mb_rdrw(i)) +"</td><td>"+
+ mbdt('drops', mb_rddrop(i)) +"</td></tr>\n"
+ )
+ wikib.write("</table>\n")
+ wikib.write("\n[(↑) Return to top](#monster-database)\n\n")
+
+def writeMob():
+ wikib.write("\
++ [Level 0-20](#starter)\n\
++ [Level 21-40](#apprentice)\n\
++ [Level 41-60](#intermediary)\n\
++ [Level 61-80](#advanced)\n\
++ [Level 81-100](#expert)\n\
++ [Level 101-150](#master)\n\
++ [Level 100+](#out-of-scope)\n\n\
+ ")
+
+ wikib.write("## Starter\n\n")
+ MonsterWrite(Mobs1)
+
+ wikib.write("## Apprentice\n\n")
+ MonsterWrite(Mobs2)
+
+ wikib.write("## Intermediary\n\n")
+ MonsterWrite(Mobs3)
+
+ wikib.write("## Advanced\n\n")
+ MonsterWrite(Mobs4)
+
+ wikib.write("## Expert\n\n")
+ MonsterWrite(Mobs5)
+
+ wikib.write("## Master\n\n")
+ MonsterWrite(Mobs6)
+
+ wikib.write("## Out Of Scope\n\n")
+ MonsterWrite(MobsA)
+
+
+def mbdt(summary, content):
+ return "<details>\
+<summary>"+summary+"</summary>\
+<pre>"+content+"</pre></details>"
+
+def mb_rdmisc(mb):
+ buff=""
+ if "agr" in mb.st.lower():
+ buff+="View Range: %s\n" % (mb.view)
+ buff+="Attack Range: %s\n" % (mb.range)
+ buff+="Move speed: %s ms\n" % (mb.move)
+ return buff
+
+def mb_rdrw(mb):
+ buff=""
+ buff+="MobPoints: %s\n" % (mb.mobpt)
+ buff+="%s\n" % (mb.xp)
+ buff+="%s\n" % (mb.jp)
+ return buff
+
+def mb_rddrop(mb):
+ buff=""
+ # sorted
+ for ax in sorted(mb.drops, key=lambda xcv: float(xcv[1]), reverse=True):
+ # Ignore disabled drops
+ if ax[0].startswith("//"):
+ continue
+
+ # Write drop
+ try:
+ buff+=ax[0]+': ' + str(int(ax[1])/100.0) + ' %\n'
+ except IndexError:
+ print("Fatal: invalid %s mob with %s drops" % (mb.name, str(ax)))
+ exit(1)
+ except:
+ print("[Warning] %s incorrect drop: %s" % (mb.name, str(ax)))
+ buff+=ax[0]+': ' + ax[1] + ' ppm\n'
+
+ # Save to SysDrops
+ SysDrops.append([ax[0], ax[1], mb.name])
+
+ return buff
+
+
+class It:
+ def __init__(self):
+ # Basic
+ self.id="0"
+ self.aegis="UnknownItem"
+ self.name="Unknown Item Name"
+ self.price="0" # Sell price, of course
+ self.weight="0"
+ self.type="IT_ETC" # default type
+ self.loc=""
+
+ # Offensive/Defensive
+ self.atk="0"
+ self.matk="0"
+ self.range="0"
+ self.defs="0"
+
+ # Restrictions (EquipLv)
+ self.lvl="0"
+ self.drop=True
+ self.trade=True
+ self.sell=True
+ self.store=True
+
+ # Special settings
+ self.rare=False # DropAnnounce
+ self.script=False
+
+ # Visual
+ self.sl="0" # Slots
+ self.ac=False # Allow Cards
+
+ # Script settings
+ self.minheal="0"
+ self.maxheal="0"
+ self.delheal="0"
+ self.typheal="0"
+ self.rarheal="0"
+
+def ItAlloc(it):
+ if (it.sl == "0" and it.ac) or (it.sl in ["1","2","3","4"] and not it.ac):
+ print("WARNING, item id "+it.id+" invalid dye/card setting!")
+ if (len(it.sl) > 1):
+ print("WARNING, item id "+it.id+" bad slots length: %d (%s)" % (len(it.sl), it.sl))
+ if it.ac:
+ wikic.write(it.id + ": " + it.name + "\n")
+
+ a=it.type
+ if "IT_HEALING" in a:
+ IT_HEALING.append(it)
+ elif "IT_ETC" in a:
+ IT_ETC.append(it)
+ elif "IT_USABLE" in a:
+ IT_USABLE.append(it)
+ elif "IT_AMMO" in a:
+ IT_AMMO.append(it)
+ elif "IT_CARD" in a:
+ IT_CARD.append(it)
+ elif "IT_PETEGG" in a:
+ IT_PETEGG.append(it)
+
+ elif "IT_WEAPON" in a:
+ if "HAND_L" in it.loc or "EQP_ARMS" in it.loc:
+ IT_WEAPON["HAND_2"].append(it)
+ elif "HAND_R" in it.loc:
+ IT_WEAPON["HAND_1"].append(it)
+ else:
+ raise Exception("Invalid location for weapon: %s" % it.loc)
+
+ elif "IT_ARMOR" in a:
+ if 'EQP_ACC_L' in it.loc:
+ IT_ARMOR['EQP_ACC_L'].append(it)
+ elif 'EQP_ACC_R' in it.loc:
+ IT_ARMOR['EQP_ACC_R'].append(it)
+ elif 'EQP_HEAD_MID' in it.loc:
+ IT_ARMOR['EQP_HEAD_MID'].append(it)
+ elif 'EQP_SHOES' in it.loc:
+ IT_ARMOR['EQP_SHOES'].append(it)
+ elif 'EQP_GARMENT' in it.loc:
+ IT_ARMOR['EQP_GARMENT'].append(it)
+ elif 'EQP_HEAD_LOW' in it.loc:
+ IT_ARMOR['EQP_HEAD_LOW'].append(it)
+ elif 'EQP_HEAD_TOP' in it.loc:
+ IT_ARMOR['EQP_HEAD_TOP'].append(it)
+ elif 'EQP_HAND_L' in it.loc:
+ IT_ARMOR['EQP_HAND_L'].append(it)
+ elif '1024' in it.loc:
+ IT_ARMOR['1024'].append(it)
+ elif '2048' in it.loc:
+ IT_ARMOR['2048'].append(it)
+ elif 'EQP_SHADOW_SHOES' in it.loc:
+ IT_ARMOR['EQP_MOUNT'].append(it)
+ elif 'EQP_SHADOW_ACC_R' in it.loc:
+ IT_ARMOR['EQP_ACC_R'].append(it) # Not really
+ else:
+ raise Exception("Invalid Loc for ID %s: %s" % (it.id, it.loc))
+
+def newItemDB():
+ print("\nGenerating Item Wiki...")
+ if len(sys.argv) >= 2:
+ src=open(sys.argv[1]+"/db/re/item_db.conf", "r")
+ else:
+ src=open("../../server-data/db/re/item_db.conf", "r")
+
+ x=It()
+ for a in src:
+ if a == "{\n":
+ ItAlloc(x)
+ x=It()
+
+ # sti() block
+ if " Id:" in a:
+ x.id=sti(a)
+ elif " AegisName:" in a:
+ x.aegis=sti(a)
+ elif " Name:" in a:
+ x.name=stin(a)
+ elif " Sell:" in a:
+ x.price=sti(a)
+ elif " Weight:" in a:
+ x.weight=sti(a)
+ elif " Type:" in a:
+ x.type=sti(a)
+ elif " Loc:" in a:
+ x.loc=sti(a)
+ elif " Atk:" in a:
+ x.atk=sti(a)
+ elif " Matk:" in a:
+ x.matk=sti(a)
+ elif " Range:" in a:
+ x.range=sti(a)
+ elif " Def:" in a:
+ x.defs=sti(a)
+ elif " EquipLv:" in a:
+ x.lvl=sti(a)
+ elif " Slots:" in a:
+ x.sl=sti(a)
+ elif " AllowCards:" in a:
+ x.ac=True
+ # Write booleans
+ elif "DropAnnounce: true" in a:
+ x.rare=True
+ elif "nodrop: true" in a:
+ x.drop=False
+ elif "notrade: true" in a:
+ x.trade=False
+ elif "noselltonpc: true" in a:
+ x.sell=False
+ elif "nostorage: true" in a:
+ x.store=False
+ elif "Script" in a:
+ x.script=True
+ # For healing items
+ elif "@min " in a:
+ x.minheal=sti(a)
+ elif "@max " in a:
+ x.maxheal=sti(a)
+ elif "@delay" in a:
+ x.delheal=sti(a)
+ elif "@type" in a:
+ x.typheal=sti(a)
+ try:
+ x.minheal=str(int(x.rarheal) * (int(x.typheal)*1 + 1)) + " %"
+ x.maxheal=str(int(x.rarheal) * (int(x.typheal)*2 + 1)) + " %"
+ if (x.delheal == "0"):
+ x.delheal=int(x.typheal)*2 + 1
+ except:
+ x.delheal="ERROR"
+ pass
+ elif "@rarity" in a:
+ x.rarheal=sti(a)
+ x.minheal=str(int(x.rarheal) * (int(x.typheal)*1 + 1)) + " %"
+ x.maxheal=str(int(x.rarheal) * (int(x.typheal)*2 + 1)) + " %"
+
+ # Write last entry
+ ItAlloc(x)
+ writeItems()
+
+ src.close()
+
+def sti(x):
+ return x.replace('\n', '').replace('|', '').replace(')', '').replace('Id: ', '').replace('"','').replace(" ","").replace("\t","").replace('AegisName: ', '').replace('Name: ','').replace('Sell: ', '').replace('Weight: ', '').replace('Type: ', '').replace('Loc: ', '').replace('Atk: ', '').replace('Matk: ', '').replace('Range: ', '').replace('Def: ', '').replace('EquipLv: ', '').replace('Slots: ','').replace(" ", "").replace('@min=','').replace('@max=','').replace('@delay=','').replace('@type=','').replace('@rarity=','').replace(';','')
+
+def stin(x):
+ return x.replace('\n', '').replace('|', '').replace(')', '').replace('Id: ', '').replace('"','').replace(" ","").replace("\t","").replace('Name: ','').replace(';','')
+
+
+def writeItems():
+ wikia.write("# Items\n\
++ [Healing Items](#healing-items)\n\
++ [Usable Items](#usable-items)\n\
++ [Generic Items](#generic-items)\n\
++ [Ammo](#ammo)\n\
++ [Cards](#cards)\n\
++ [Pet Eggs](#pet-eggs)\n\
++ [Mounts](#mounts)\n\
++ [Weapons](#weapons)\n\
+ + [1H Weapons](#1h-weapons)\n\
+ + [2H Weapons](#2h-weapons)\n\
++ [Armors](#armors)\n\
+ + [Left Accessory](#left-accessory)\n\
+ + [Right Accessory](#right-accessory)\n\
+ + [Headgear](#headgear)\n\
+ + [Chest](#chest)\n\
+ + [Pants](#pants)\n\
+ + [Shoes](#shoes)\n\
+ + [Necklaces](#necklaces)\n\
+ + [Rings](#rings)\n\
+ + [Gloves](#gloves)\n\
+ + [Shields](#shields)\n\
+\n\n")
+ wikia.write("#### Restrictions Reference\n")
+ wikia.write("Special Aegis Name Markers:\n\
++ * - Rare item with drop announce.\n\
++ (dp) - This item cannot be dropped.\n\
++ (tr) - This item cannot de traded.\n\
++ (sl) - This item cannot be sold.\n\
++ (gg) - This item cannot go to storage.\n\n")
+
+ # Healing Items
+ wikia.write("## Healing Items\n\n")
+ ItemWrite(IT_HEALING, ID=True, AEGIS=True, PRICE=True, WEIGHT=True, HEALING=True, DROPPER=True)
+
+ # Usable Items
+ wikia.write("## Usable Items\n")
+ ItemWrite(IT_USABLE, ID=True, AEGIS=True, NAME=True, PRICE=True, WEIGHT=True, DROPPER=True)
+
+ # Generic Items
+ wikia.write("## Generic Items\n")
+ ItemWrite(IT_ETC, ID=True, AEGIS=True, NAME=True, PRICE=True, WEIGHT=True, DROPPER=True)
+
+ # Ammo Items
+ wikia.write("## Ammo\n")
+ ItemWrite(IT_AMMO, ID=True, AEGIS=True, NAME=True, WEIGHT=True, ATK=True)
+
+ # Card Items
+ wikia.write("## Cards\n")
+ ItemWrite(IT_CARD, ID=True, AEGIS=True, NAME=True, PRICE=True, WEIGHT=True)
+
+ # Pet Egg Items
+ wikia.write("## Pet Eggs\n")
+ ItemWrite(IT_PETEGG, ID=True, AEGIS=True, NAME=True, WEIGHT=True)
+
+ # Mount Items
+ wikia.write("## Mounts\n")
+ ItemWrite(IT_ARMOR['EQP_MOUNT'], ID=True, AEGIS=True, NAME=True, WEIGHT=True)
+
+ ####################################################################
+ wikia.write("# Weapons\n")
+
+ # 1 Hand Items
+ wikia.write("## 1H Weapons\n")
+ ItemWrite(IT_WEAPON['HAND_1'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, LVL=True, DROPPER=True)
+
+ # 2 Hand Items
+ wikia.write("## 2H Weapons\n")
+ ItemWrite(IT_WEAPON['HAND_2'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, LVL=True, RANGE=True)
+
+
+ ####################################################################
+ wikia.write("# Armors\n")
+
+ ArmorWrite("Left Accessory",'EQP_ACC_L', False)
+ ArmorWrite("Right Accessory",'EQP_ACC_R', False)
+ ArmorWrite("Headgear",'EQP_HEAD_TOP')
+ ArmorWrite("Chest",'EQP_HEAD_MID')
+ ArmorWrite("Pants",'EQP_HEAD_LOW')
+ ArmorWrite("Shoes",'EQP_SHOES')
+ ArmorWrite("Necklaces",'1024', False)
+ ArmorWrite("Rings",'2048', False)
+ ArmorWrite("Gloves",'EQP_GARMENT')
+ ArmorWrite("Shields",'EQP_HAND_L')
+
+# Write AegisName with restrictions
+def hl(it):
+ buff=""
+ if it.rare:
+ buff+="*"
+ buff+=it.aegis
+ buff+=" "
+ if not it.drop:
+ buff+="<a href='#restrictions-reference'>(dp)</a>"
+ if not it.trade:
+ buff+="<a href='#restrictions-reference'>(tr)</a>"
+ if not it.sell:
+ buff+="<a href='#restrictions-reference'>(sl)</a>"
+ if not it.store:
+ buff+="<a href='#restrictions-reference'>(gg)</a>"
+ return buff
+
+# wikia.write("Id|Aegis|Name|Weight|Atk|Matk|\n")
+# wikia.write("Id|Aegis|Name|Price|Weight|\n")
+
+def ItemWrite(tbl, ID=False, AEGIS=False, NAME=False, PRICE=False, WEIGHT=False, DEF=False, LVL=False, ATK=False, RANGE=False, HEALING=False, SCRIPT=False, DROPPER=False):
+ wikia.write("<table border=1>\n")
+ wikia.write("<tr>")
+ if ID:
+ wikia.write("<th>ID</th>")
+ if AEGIS:
+ wikia.write("<th>Aegis</th>")
+ if NAME:
+ wikia.write("<th>Name</th>")
+ if PRICE:
+ wikia.write("<th>Price</th>")
+ if WEIGHT:
+ wikia.write("<th>Weight</th>")
+ if DEF:
+ wikia.write("<th>Def</th>")
+ if LVL:
+ wikia.write("<th>Lvl</th>")
+ if ATK:
+ wikia.write("<th>Atk</th>")
+ wikia.write("<th>Matk</th>")
+ if RANGE:
+ wikia.write("<th>Range</th>")
+ if HEALING:
+ wikia.write("<th>Min</th>")
+ wikia.write("<th>Max</th>")
+ wikia.write("<th>Delay</th>")
+ if SCRIPT:
+ wikia.write("<th>Script</th>")
+ if DROPPER:
+ wikia.write("<th>Mobs</th>")
+
+ wikia.write("</tr>\n")
+
+ for i in tbl:
+ """
+ # To sort by weight, for example, uncomment this block.
+ try:
+ zt=int(i.weight)
+ except:
+ tbl.remove(i)
+
+ for i in sorted(tbl, key=lambda xcv: int(xcv.weight), reverse=True):
+ """
+
+ wikia.write('<tr>')
+
+ if ID:
+ wikia.write("<td><a name=\"%s\"></a>%s</td>" % (i.id,i.id))
+ if AEGIS:
+ wikia.write("<td>%s</td>" % hl(i))
+ if NAME:
+ wikia.write("<td>%s</td>" % i.name)
+ if PRICE:
+ wikia.write("<td>%s GP</td>" % i.price)
+ if WEIGHT:
+ wikia.write("<td>%s g</td>" % i.weight)
+ if DEF:
+ wikia.write("<td>Def: %s</td>" % i.defs)
+ if LVL:
+ wikia.write("<td>Lv: %s</td>" % i.lvl)
+ if ATK:
+ wikia.write("<td>Atk: %s</td>" % i.atk)
+ wikia.write("<td>%s</td>" % i.matk)
+ if RANGE:
+ wikia.write("<td>%s</td>" % i.range)
+ if HEALING:
+ wikia.write("<td>%s</td>" % i.minheal)
+ wikia.write("<td>%s</td>" % i.maxheal)
+ wikia.write("<td>%s s</td>" % i.delheal)
+ if SCRIPT:
+ wikia.write("<td>%s</td>" % i.script)
+ # TODO: Check for item Aegis in npc/ folder too, to determine shops and quests.
+ if DROPPER:
+ tmp_droppers=""
+ tmp_drpalign=[]
+ for ax in SysDrops:
+ if ax[0] == i.aegis:
+ tmp_drpalign.append([ax[2], ax[1]])
+ if len(tmp_drpalign) > 0:
+ for a in sorted(tmp_drpalign, key=lambda xcv: float(xcv[1]), reverse=True):
+ try:
+ ppm=int(a[1])/100.0
+ tmp_droppers+=("%s: %.2f %% \n" % (a[0], ppm))
+ except:
+ print("[Warning] %s whodrop error: %s" % (i.name, str(a)))
+ wikia.write("<td>%s</td>" % mbdt("monsters", tmp_droppers))
+ else:
+ wikia.write("<td>-</td>")
+
+ wikia.write("</tr>")
+
+ wikia.write("</table>\n")
+ wikia.write("\n[(↑) Return to top](#items)\n\n")
+
+def ArmorWrite(name,scope,defitem=True):
+ wikia.write("## "+name+"\n")
+ ItemWrite(IT_ARMOR[scope], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, DEF=defitem, LVL=True, DROPPER=True)
+
+
+
+
+
+
+
+
+
+
+
+class Quest:
+ def __init__(self, ide):
+ # Basic
+ self.id=ide
+ self.name="Unknown Quest Name"
+ self.group="Unknown"
+ self.ent=[]
+ self.level=0
+
+class QuestEntry:
+ def __init__(self):
+ # Basic
+ self.complete=False
+ self.entry=[] # collection of <text>
+ self.giver=""
+ self.reward=""
+ self.loc=""
+
+def sortlv(val):
+ return val[1]
+
+def qnt(string):
+ return string.replace(' ','').replace('"','').replace("'","").replace('<','').replace('>','').replace('nowiki=1', '').replace('nowiki', '')
+
+def qnt2(string):
+ return string.replace('##B','**').replace('##b','**').replace('##0','*').replace('##1','*').replace('##2','*').replace('##3','*').replace('##','*')
+
+def DoQuest():
+ print("\nGenerating Quest Wiki...")
+ if len(sys.argv) >= 3:
+ src=open(sys.argv[2]+"/quests.xml", "r")
+ else:
+ src=open("../../client-data/quests.xml", "r")
+
+ qlog=[]
+ q=Quest(-1)
+ qe=QuestEntry()
+ ig=False
+ nw=False
+
+ for e in src:
+ # Handle Comments and Ignored lines
+ if '<!--' in e and '-->' in e:
+ continue
+ elif '<!--' in e:
+ ig=True
+ elif '-->' in e:
+ ig=False
+ if '<effect' in e:
+ continue
+
+ if ig:
+ continue
+
+ # Handle Quest Headers
+ if '</var' in e:
+ if (nw):
+ nw=False
+ else:
+ qlog.append(q)
+ elif '<var' in e:
+ if 'nowiki' in e:
+ nw=True
+ g=qnt(e)
+ try:
+ q=Quest(int( g.replace('varid=','') ))
+ except:
+ print("Invalid quest: %s" % g)
+ exit(1)
+
+ # Handle quest entries
+ if '</quest>' in e:
+ q.ent.append(qe)
+ elif '<quest ' in e:
+ qe=QuestEntry()
+ l=e.split('"')
+ rc=[False, ""]
+ for arg in l:
+ if not rc[0]:
+ if "name" in arg:
+ rc=[True, 'name']
+ elif "group" in arg:
+ rc=[True, 'group']
+ elif "complete" in arg:
+ rc=[True, arg.replace('=','').replace(' ','')]
+ else:
+ if rc[1] == "name":
+ q.name=arg
+ elif rc[1] == "group":
+ q.group=arg
+ elif rc[1] == "complete":
+ qe.complete=True
+ elif rc[1] == "incomplete":
+ qe.complete=False
+ else:
+ print("Invalid <quest> tag: %s (arg was %s) (line was %s)" % (e, rc[1], l))
+ exit(1)
+ rc=[False, ""]
+
+ # Fill stuff in Quest Entry
+ if '<text' in e:
+ a=qnt2(e)
+ qe.entry.append( a.replace('<text>','').replace('</text>','').replace('<text ','<').replace("@@1", "text").replace("@@", "").strip() )
+ elif '<wiki' in e:
+ a=qnt2(e)
+ qe.entry.append( a.replace('<wiki>','').replace('</wiki>','').replace('<wiki ','<').replace("@@1", "text").replace("@@", "").strip() )
+ elif '<questgiver' in e:
+ a=qnt2(e)
+ qe.giver=a.replace('<questgiver>','').replace('</questgiver>','').strip()
+ elif '<reward' in e:
+ a=qnt2(e)
+ qe.reward=a.replace('<reward>','').replace('</reward>','').replace("@@", "text").replace('<reward ','<').strip()
+ elif '<coord' in e:
+ a=qnt2(e)
+ b=a.split('>')
+ qe.loc=b[1].replace('</coordinates','').strip()
+ elif '<level' in e:
+ a=qnt2(e)
+ qe.entry.append( "Required Level: " + a.replace('<level>','').replace('</level>','').strip() )
+ if (not q.level):
+ try:
+ q.level=int(a.replace('<level>','').replace('</level>','').strip())
+ except:
+ pass
+
+ # Done reading file
+ src.close()
+ aktbl={}
+ aksort=[]
+ print("\033[32;1mTotal quests: %d\033[0m" % len(qlog))
+ for i in qlog:
+ if i.name=="Unknown Quest Name":
+ print("Warning, invalid quest: %d" % (i.id))
+ qlog.remove(i)
+ continue
+ # Total Table
+ #print(str(i.id)+": "+i.name)
+ try:
+ if (i.level):
+ aktbl[i.group].append(("[%s](q/%d) (Lv %d)" % (i.name, i.id, i.level), i.level))
+ else:
+ aktbl[i.group].append(("[%s](q/%d)" % (i.name, i.id), i.level))
+ except:
+ if (i.level):
+ aktbl[i.group]=[("[%s](q/%d) (Lv %d)" % (i.name, i.id, i.level), i.level)]
+ else:
+ aktbl[i.group]=[("[%s](q/%d)" % (i.name, i.id), i.level)]
+ aksort.append(i.group)
+
+ for key in aktbl:
+ #print(aktbl[key]);
+ aktbl[key]=sorted(aktbl[key], key=sortlv)
+
+ # Individual file
+ f=open("../../wiki/q/"+str(i.id)+'.md', "w")
+ f.write("<!-- --- title: %d: %s -->\n\n" % (i.id, i.name))
+ f.write("# %s\n" % i.name)
+ f.write('\n')
+
+ totalcnt=0
+ for a in i.ent:
+ totalcnt+=1
+ f.write('\n### %d Stage\n\n' % totalcnt)
+ if a.complete:
+ f.write('*This completes quest*\n\n')
+
+ if a.giver != "" or a.reward != "" or a.loc != "":
+ f.write('```\n')
+ if a.giver != "":
+ f.write('Quest Giver: %s\n' % a.giver)
+ if a.reward != "":
+ f.write('Reward: %s\n' % a.reward.replace('@@', ''))
+ if a.loc != "":
+ f.write('Location: %s\n' % a.loc)
+ f.write('```\n\n')
+
+ for line in a.entry:
+ f.write('%s\n' % line)
+
+ f.write('\n\n****\nThis file is generated automatically. Editing it will have no effect.\n')
+ f.close()
+
+ # Write total table
+ f=open("Quests.txt", "w")
+ f.write("***Total quests: %d***\n" % len(qlog))
+ for key in aksort:
+ f.write('\n## %s\n\n' % key)
+ # TODO: Sort by Quest Level
+ for a in aktbl[key]:
+ f.write('+ '+a[0]+'\n')
+ f.close()
+
+showHeader()
+
+testMobs()
+newItemDB()
+DoQuest()
+
+wikia.close()
+wikib.close()
+wikic.close()
+wikid.close()
+#print(str(SysDrops))
+
+showFooter()
+exit(0)