summaryrefslogtreecommitdiff
path: root/hercules
diff options
context:
space:
mode:
Diffstat (limited to 'hercules')
-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
93 files changed, 6172 insertions, 0 deletions
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)