diff options
Diffstat (limited to 'hercules')
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("&", "&"); + data = data.replace("<", "<"); + data = data.replace(">", ">"); + 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 Binary files differnew file mode 100644 index 0000000..f360c63 --- /dev/null +++ b/hercules/templates/collision.png 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 Binary files differnew file mode 100644 index 0000000..f36ccbb --- /dev/null +++ b/hercules/templates/tileset.png 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) |