Skip to content

Instantly share code, notes, and snippets.

@vstumpf
Last active January 11, 2018 07:36
Show Gist options
  • Save vstumpf/51e81038200769e3940319a3a9c9deac to your computer and use it in GitHub Desktop.
Save vstumpf/51e81038200769e3940319a3a9c9deac to your computer and use it in GitHub Desktop.
Converts whole mob_db, how 2 modes?
#! /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()
# -
# 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