Files
metin2-client/tools/server/biolog/import_ava2.py
T

57 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""Extract AVA2's active biology table and command body, retaining reference provenance.
Only command dispatch, notification transport and three PORT point numbers are adapted.
"""
import argparse, hashlib, json, re
from pathlib import Path
HERE=Path(__file__).resolve().parent
NAMES={
'POINT_DEF_GRADE_BONUS':'防御力','POINT_ATT_GRADE_BONUS':'攻击力','POINT_ATTBONUS_MONSTER':'对怪物伤害 %',
'POINT_ATTBONUS_STONE':'对陨石伤害 %','POINT_ATTBONUS_BOSS':'对 Boss 伤害 %','POINT_MAX_HP':'生命值',
'POINT_CASTING_SPEED':'施法速度','POINT_RESIST_WARRIOR':'战士抗性 %','POINT_RESIST_ASSASSIN':'忍者抗性 %',
'POINT_RESIST_SURA':'修罗抗性 %','POINT_RESIST_SHAMAN':'萨满抗性 %','POINT_ATTBONUS_WARRIOR':'对战士伤害 %',
'POINT_ATTBONUS_ASSASSIN':'对忍者伤害 %','POINT_ATTBONUS_SURA':'对修罗伤害 %','POINT_ATTBONUS_SHAMAN':'对萨满伤害 %',
'POINT_RESIST_HUMAN':'半人抗性 %','POINT_MELEE_MAGIC_ATT_BONUS_PER':'近战及魔法攻击 %','POINT_ATTBONUS_HUMAN':'对半人伤害 %'}
def extract(reference):
constants=(reference/'constants.cpp').read_bytes().decode('latin1')
table=re.search(r'const DWORD bio_data\[\]\[17\] = \{(.*?)\n\};',constants,re.S).group(1)
rows=[]
for match in re.finditer(r'\{([^{}]+)\}', table):
values=[x.strip() for x in match[1].split(',')]; assert len(values)==17
rows.append([v if v.startswith('POINT_') else int(v) for v in values])
assert len(rows)==14
return rows
def generate(reference,output):
rows=extract(reference)
source=(reference/'cmd_general.cpp').read_bytes().decode('latin1').replace('\r','')
start=source.index('ACMD(do_bio)\n');end=source.index('\n#endif',start)
body=source[start:end]
# Maintain the upstream algorithm verbatim. Adapter guards live outside this function.
body=body.replace('ACMD(do_bio)', 'static ACMD(do_ava2_bio)')
body=body.replace('split_argument(argument, vecArgs);','BioSplitArgument(argument, vecArgs);')
notices={'562':'level','563':'material','564':'cooldown','565':'success','566':'failure','567':'stone','568':'reward','569':'next','570':'complete'}
for key,name in notices.items():
body=body.replace('CHAT_TYPE_INFO, "'+key+'"','CHAT_TYPE_COMMAND, "BioNotice '+name+'"')
body=body.replace('CHAT_TYPE_INFO, "'+key+' I%d"','CHAT_TYPE_COMMAND, "BioNotice '+name+' %d"')
body=body.replace('CHAT_TYPE_INFO, "'+key+' E%d:%d"','CHAT_TYPE_COMMAND, "BioNotice '+name+' %d %d"')
body=body.replace('pointToApply(', 'BioPointIdentity(')
# The level notice in upstream forgot its printf placeholder. Keep its level argument visible.
body=body.replace('"BioNotice level"','"BioNotice level %d"')
table='static const unsigned bio_data[][17] = {\n'+''.join(' {'+', '.join(map(str,row))+'},\n' for row in rows)+'};\nstatic const unsigned bio_max = 14;\n'
generated='// Generated from AVA2 constants.cpp + cmd_general.cpp by import_ava2.py.\n'+table+body+'\n'
output.mkdir(parents=True,exist_ok=True)
(output/'ava2_biolog_core.h').write_text(generated)
catalog=[]
for i,row in enumerate(rows[1:],1):
catalog.append(dict(index=i,level=row[0],material=row[1],count=row[2],cooldown=row[3],chance=row[4],stone=row[5],
bonuses=[dict(point=row[j],label=NAMES[row[j]],value=row[j+1]) for j in (6,8,10,12) if row[j]],choose=bool(row[14]),enhancer=row[15],skip=row[16]))
(output/'catalog.json').write_text(json.dumps(catalog,ensure_ascii=False,indent=2)+'\n')
(output/'reference.json').write_text(json.dumps({name:hashlib.sha256((reference/name).read_bytes()).hexdigest() for name in ('constants.cpp','cmd_general.cpp','char.cpp')},indent=2)+'\n')
return catalog
if __name__=='__main__':
parser=argparse.ArgumentParser();parser.add_argument('reference',type=Path);parser.add_argument('output',type=Path)
args=parser.parse_args();generate(args.reference,args.output)