Last active
January 11, 2018 07:36
-
-
Save vstumpf/51e81038200769e3940319a3a9c9deac to your computer and use it in GitHub Desktop.
Converts whole mob_db, how 2 modes?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/python | |
import os | |
import sys | |
import re | |
import shutil | |
MAX_MOB_DROP = 10 | |
MAX_MVP_DROP = 3 | |
MAX_STEAL_DROP = 7 | |
MOB_DB_TXT = "mob_db.txt" | |
MOB_DROP_TXT = "mob_drop.txt" | |
MOB_DROP_YAML = "mob_drop.yml" | |
def read_db(path): | |
"""Read the databases from path, return a database""" | |
comma_split = re.compile(",") | |
flen = 31 + 2 * (MAX_MOB_DROP + MAX_MVP_DROP) | |
mobdbtxt = path + MOB_DB_TXT | |
mobdroptxt = path + MOB_DROP_TXT | |
mobdb = [] | |
with open(mobdbtxt + ".old", "r") as db: | |
for line in db: | |
entry = {} | |
line = line.strip() | |
if len(line) < 5: | |
continue | |
if line[:2] == "//": | |
entry['comment'] = 1 | |
line = line[2:] | |
line = line.partition("//")[0].strip() | |
fields = [a.strip() for a in comma_split.split(line)] | |
if len(fields) != flen: | |
continue | |
if fields[0] == 'ID': | |
continue | |
entry['mob_id'] = int(fields[0]) | |
entry['sprite_name'] = fields[1] | |
entry['kROName'] = fields[2] | |
entry['iROName'] = fields[3] | |
entry['LV'] = fields[4] | |
entry['HP'] = fields[5] | |
entry['SP'] = fields[6] | |
entry['EXP'] = fields[7] | |
entry['JEXP'] = fields[8] | |
entry['Range'] = fields[9] + "~" + fields[20] + "~" + fields[21] | |
entry['ATK'] = fields[10] + "~" | |
if fields[11] != "": | |
entry['ATK'] += fields[11] | |
else: | |
entry['ATK'] += fields[10] | |
entry['DEF'] = fields[12] | |
entry['MDEF'] = fields[13] | |
entry['Stats'] = {} | |
entry['Stats']['str'] = fields[14] | |
entry['Stats']['agi'] = fields[15] | |
entry['Stats']['vit'] = fields[16] | |
entry['Stats']['int'] = fields[17] | |
entry['Stats']['dex'] = fields[18] | |
entry['Stats']['luk'] = fields[19] | |
entry['Scale'] = fields[22] | |
entry['Race'] = fields[23] | |
entry['Element'] = fields[24] | |
entry['Mode'] = fields[25] | |
entry['Speed'] = fields[26] | |
entry['aDelay'] = fields[27] | |
entry['aMotion'] = fields[28] | |
entry['dMotion'] = fields[29] | |
entry['MEXP'] = fields[30] | |
entry['drops'] = [] | |
entry['mvp_drops'] = [] | |
for i in range(MAX_MVP_DROP): | |
idx = 31 + (i * 2) | |
if fields[idx] == '0': | |
continue | |
it = {} | |
it['nameid'] = fields[idx] | |
it['rate'] = fields[idx + 1] | |
entry['mvp_drops'].append(it) | |
for i in range(MAX_MOB_DROP): | |
idx = 31 + (i + MAX_MVP_DROP) * 2 | |
if fields[idx] == '0': | |
continue | |
it = {} | |
it['nameid'] = fields[idx] | |
it['rate'] = fields[idx + 1] | |
if i >= MAX_STEAL_DROP: | |
it['steal_protected'] = True | |
entry['drops'].append(it) | |
# print(entry) | |
mobdb.append(entry) | |
with open(mobdroptxt, "r") as db: | |
for line in db: | |
mob_comment = 0 | |
line = line.strip() | |
drop = {} | |
if len(line) < 5: | |
continue | |
if line[:2] == "//": | |
mob_comment = 1 | |
line = line[2:] | |
line = line.partition("//")[0] | |
fields = [a.strip() for a in comma_split.split(line)] | |
if len(fields) < 3 or len(fields) > 5: | |
continue | |
if fields[0] == '<mobid>': | |
continue | |
mob_id = int(fields[0]) | |
drop['nameid'] = fields[1] | |
drop['rate'] = fields[2] | |
drop['rog'] = fields[3] if len(fields) > 3 else "RDMOPTG_None" | |
flag = int(fields[4]) if len(fields) > 4 else 0 | |
drop['steal_protected'] = flag & 0x1 | |
mob = None | |
for mobe in mobdb: | |
if mobe['mob_id'] != mob_id: | |
continue | |
if mobe.has_key('comment') != mob_comment: | |
continue | |
mob = mobe | |
if mob is None: | |
mob = {} | |
mob['mob_id'] = mob_id | |
mob['mvp_drops'] = [] | |
mob['drops'] = [] | |
mobdb.append(mob) | |
if flag & 0x2: | |
drops = mob['mvp_drops'] | |
else: | |
drops = mob['drops'] | |
idx = None | |
for i, _drop in enumerate(drops): | |
if _drop['nameid'] != drop['nameid']: | |
continue | |
idx = i | |
if idx is None: | |
drops.append(drop) | |
else: | |
drops.remove(idx) | |
drops.insert(idx, drop) | |
continue | |
return mobdb | |
def write_drop(md, drop, comment): | |
md.write(comment + " -\n") | |
md.write(comment + " ItemId: " + | |
drop['nameid'] + "\n") | |
md.write(comment + " Rate: " + | |
drop['rate'] + "\n") | |
if drop.has_key('steal_protected') and drop['steal_protected']: | |
md.write(comment + " StealProtected: true\n") | |
if drop.has_key('rog') and drop['rog'] != "RDMOPTG_None": | |
md.write(comment + | |
' RandomOptionGroup: {0}\n'.format(drop['rog'])) | |
def convert_race(race): | |
races = ["FORMLESS", "UNDEAD", "BRUTE", "PLANT", "INSECT", "FISH", "DEMON", "DEMIHUMAN", "ANGEL", "DRAGON", "PLAYER"] | |
return "RC_" + races[int(race)] | |
def convert_element(element): | |
eles = ["NEUTRAL", "WATER" , "EARTH" , "FIRE" , "WIND" , "POISON" , "HOLY" , "SHADOW" , "GHOST" , "UNDEAD"] | |
return "ELE_" + eles[int(element) % 20] + "_" + str(int(element) // 20) | |
def write_mode(md, mode, comment): | |
modes = ["MD_CANMOVE", "MD_LOOTER", "MD_AGGRESSIVE", "MD_ASSIST", "MD_CASTSENSOR_IDLE"] | |
modes.extend(["MD_NORANDOM_WALK", "MD_NOCAST_SKILL", "MD_CANATTACK", "MD_NONE"]) | |
modes.extend(["MD_CASTSENSOR_CHASE", "MD_CHANGECHASE", "MD_ANGRY", "MD_CHANGETARGET_MELEE"]) | |
modes.extend(["MD_CHANGETARGET_CHASE", "MD_TARGETWEAK", "MD_RANDOMTARGET", "MD_IGNOREMELEE"]) | |
modes.extend(["MD_IGNOREMAGIC", "MD_IGNORERANGED", "MD_MVP", "MD_IGNOREMISC"]) | |
modes.extend(["MD_KNOCKBACK_IMMUNE", "MD_TELEPORT_BLOCK", "MD_NONE", "MD_FIXED_ITEMDROP"]) | |
modes.extend(["MD_DETECTOR", "MD_STATUS_IMMUNE", "MD_SKILL_IMMUNE"]) | |
mob = [] | |
md.write(comment + " Mode: \n") | |
for i, m in enumerate(modes): | |
if mode & 1 << i: | |
md.write(comment + " {0}: ~\n".format(m)) | |
def write_stats(md, stats, comment): | |
md.write(comment + " Stats:" + "\n") | |
md.write(comment + " Str: " + stats['str'] + "\n") | |
md.write(comment + " Agi: " + stats['agi'] + "\n") | |
md.write(comment + " Vit: " + stats['vit'] + "\n") | |
md.write(comment + " Int: " + stats['int'] + "\n") | |
md.write(comment + " Dex: " + stats['dex'] + "\n") | |
md.write(comment + " Luk: " + stats['luk'] + "\n") | |
def write_db(path, mob_db): | |
mobdbtxt = path + MOB_DB_TXT | |
mobdropyaml = path + MOB_DROP_YAML | |
with open(mobdbtxt + ".new", "w") as db, open(mobdropyaml, "w") as md: | |
db.write(MOBDBHEADER) | |
md.write(MOBDROPHEADER) | |
md.write("MobDrops:\n") | |
for entry in mob_db: | |
if entry.has_key('comment'): | |
comment = "#" | |
db.write("// ") | |
else: | |
comment = "" | |
if entry.has_key('mob_db'): | |
db.write(entry['mob_db'] + '\n') | |
md.write(comment + " -\n") | |
md.write(comment + " MobId: " + str(entry['mob_id']) + "\n") | |
md.write(comment + " SpriteName: " + | |
entry['sprite_name'] + "\n") | |
md.write(comment + " kROName: " + entry['kROName'] + "\n") | |
md.write(comment + " iROName: " + entry['iROName'] + "\n") | |
md.write(comment + " LV: " + entry['LV'] + "\n") | |
md.write(comment + " HP: " + entry['HP'] + "\n") | |
md.write(comment + " SP: " + entry['SP'] + "\n") | |
md.write(comment + " EXP: " + entry['EXP'] + "\n") | |
md.write(comment + " JEXP: " + entry['JEXP'] + "\n") | |
md.write(comment + " Range: " + entry['Range'] + "\n") | |
md.write(comment + " ATK: " + entry['ATK'] + "\n") | |
md.write(comment + " DEF: " + entry['DEF'] + "\n") | |
md.write(comment + " MDEF: " + entry['MDEF'] + "\n") | |
write_stats(md, entry['Stats'], comment) | |
md.write(comment + " Scale: " + entry['Scale'] + "\n") | |
md.write(comment + " Race: " + convert_race(entry['Race']) + "\n") | |
md.write(comment + " Element: " + convert_element(entry['Element']) + "\n") | |
write_mode(md, int(entry['Mode'], 16), comment) | |
md.write(comment + " Speed: " + entry['Speed'] + "\n") | |
md.write(comment + " aDelay: " + entry['aDelay'] + "\n") | |
md.write(comment + " aMotion: " + entry['aMotion'] + "\n") | |
md.write(comment + " dMotion: " + entry['dMotion'] + "\n") | |
md.write(comment + " MEXP: " + entry['MEXP'] + "\n") | |
if len(entry['drops']) > 0: | |
md.write(comment + " NormalDrop:\n") | |
for drop in entry['drops']: | |
write_drop(md, drop, comment) | |
if len(entry['mvp_drops']) > 0: | |
md.write(comment + " MvPDrop:\n") | |
for drop in entry['mvp_drops']: | |
write_drop(md, drop, comment) | |
def printUsage(): | |
print("mob_drop.py database-path [...]") | |
print("\tExample Usage: mob_drop.py ../db/pre-re/ ../db/import/") | |
print("This will convert mob_db.txt + mob_drop.txt -> mob_db.txt + mob_drop.yml") | |
def main(): | |
if len(sys.argv) <= 1: | |
printUsage() | |
exit(0) | |
for path in sys.argv[1:]: | |
files = [path + MOB_DB_TXT, path + MOB_DROP_TXT] | |
for file in files: | |
if not os.path.exists(file): | |
print("File {0} does not exist!".format(file)) | |
print("Exiting without changes...") | |
exit(0) | |
[shutil.copy2(file, file + ".old") for file in files] | |
files = [path + MOB_DROP_YAML] | |
for file in files: | |
if os.path.exists(file): | |
c = raw_input("Overwrite {0}? (y/n): ".format(file)) | |
if c[0] != 'y' and c[0] != 'Y': | |
exit(0) | |
shutil.copy2(file, file + ".old") | |
db = read_db(path) | |
print(str(len(db)) + " entries read in path " + path) | |
write_db(path, db) | |
MOBDBHEADER = """// Monster Database | |
// | |
// Structure of Database : | |
// ID,Sprite_Name,kROName,iROName,LV,HP,SP,EXP,JEXP,Range1,ATK1,ATK2,DEF,MDEF,STR,AGI,VIT,INT,DEX,LUK,Range2,Range3,Scale,Race,Element,Mode,Speed,aDelay,aMotion,dMotion,MEXP | |
// Note: Keep the Sprite_Name field as it is in the game client. | |
""" | |
MOBDROPHEADER = """ | |
# - | |
# MobId: <mob_id> | |
# NormalDrop: | |
# - | |
# ItemId: <nameid> | |
# Rate: <rate (out of 10000)> | |
# StealProtected: <yes, no> (defaults to no) | |
# RandomOptionGroup: <rand_opt_group> (optional, defaults RDMOPTG_None) | |
# MvPDrop: | |
# - | |
# ItemId: <nameid> | |
# Rate: <rate (out of 10000)> | |
# StealProtected: <yes, no> (defaults to no) | |
# RandomOptionGroup: <rand_opt_group> (optional, defaults RDMOPTG_None) | |
""" | |
if __name__ == "__main__": | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# - | |
# MobId: <mob_id> | |
# NormalDrop: | |
# - | |
# ItemId: <nameid> | |
# Rate: <rate (out of 10000)> | |
# StealProtected: <yes, no> (defaults to no) | |
# RandomOptionGroup: <rand_opt_group> (optional, defaults RDMOPTG_None) | |
# MvPDrop: | |
# - | |
# ItemId: <nameid> | |
# Rate: <rate (out of 10000)> | |
# StealProtected: <yes, no> (defaults to no) | |
# RandomOptionGroup: <rand_opt_group> (optional, defaults RDMOPTG_None) | |
MobDrops: | |
- | |
MobId: 1001 | |
SpriteName: SCORPION | |
kROName: Scorpion | |
iROName: Scorpion | |
LV: 24 | |
HP: 1109 | |
SP: 0 | |
EXP: 287 | |
JEXP: 176 | |
Range: 1~10~12 | |
ATK: 80~135 | |
DEF: 30 | |
MDEF: 0 | |
Stats: | |
Str: 1 | |
Agi: 24 | |
Vit: 24 | |
Int: 5 | |
Dex: 52 | |
Luk: 5 | |
Scale: 0 | |
Race: RC_INSECT | |
Element: ELE_FIRE_1 | |
Mode: | |
MD_CANMOVE: ~ | |
MD_AGGRESSIVE: ~ | |
MD_CASTSENSOR_IDLE: ~ | |
MD_CANATTACK: ~ | |
MD_CHANGETARGET_MELEE: ~ | |
MD_CHANGETARGET_CHASE: ~ | |
MD_DETECTOR: ~ | |
Speed: 200 | |
aDelay: 1564 | |
aMotion: 864 | |
dMotion: 576 | |
MEXP: 0 | |
NormalDrop: | |
- | |
ItemId: 990 | |
Rate: 70 | |
- | |
ItemId: 904 | |
Rate: 5500 | |
- | |
ItemId: 757 | |
Rate: 57 | |
- | |
ItemId: 943 | |
Rate: 210 | |
- | |
ItemId: 7041 | |
Rate: 100 | |
- | |
ItemId: 508 | |
Rate: 200 | |
- | |
ItemId: 625 | |
Rate: 20 | |
- | |
ItemId: 4068 | |
Rate: 1 | |
StealProtected: true | |
- | |
MobId: 1038 | |
SpriteName: OSIRIS | |
kROName: Osiris | |
iROName: Osiris | |
LV: 78 | |
HP: 415400 | |
SP: 0 | |
EXP: 71500 | |
JEXP: 28600 | |
Range: 1~10~12 | |
ATK: 780~2880 | |
DEF: 10 | |
MDEF: 25 | |
Stats: | |
Str: 1 | |
Agi: 75 | |
Vit: 30 | |
Int: 37 | |
Dex: 86 | |
Luk: 40 | |
Scale: 1 | |
Race: RC_UNDEAD | |
Element: ELE_UNDEAD_4 | |
Mode: | |
MD_CANMOVE: ~ | |
MD_AGGRESSIVE: ~ | |
MD_CASTSENSOR_IDLE: ~ | |
MD_CANATTACK: ~ | |
MD_CASTSENSOR_CHASE: ~ | |
MD_CHANGECHASE: ~ | |
MD_CHANGETARGET_MELEE: ~ | |
MD_CHANGETARGET_CHASE: ~ | |
MD_MVP: ~ | |
MD_KNOCKBACK_IMMUNE: ~ | |
MD_DETECTOR: ~ | |
MD_STATUS_IMMUNE: ~ | |
Speed: 100 | |
aDelay: 1072 | |
aMotion: 672 | |
dMotion: 384 | |
MEXP: 35750 | |
NormalDrop: | |
- | |
ItemId: 617 | |
Rate: 2000 | |
- | |
ItemId: 1232 | |
Rate: 150 | |
- | |
ItemId: 2235 | |
Rate: 200 | |
- | |
ItemId: 1255 | |
Rate: 600 | |
- | |
ItemId: 1009 | |
Rate: 1000 | |
- | |
ItemId: 5053 | |
Rate: 150 | |
- | |
ItemId: 1285 | |
Rate: 100 | |
- | |
ItemId: 4144 | |
Rate: 1 | |
StealProtected: true | |
MvPDrop: | |
- | |
ItemId: 603 | |
Rate: 4000 | |
- | |
ItemId: 608 | |
Rate: 3000 | |
- | |
ItemId: 751 | |
Rate: 500 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment