# Parse itemstatcost.txt and properties.txt into aggregated json. import csv import json import os import sys path = sys.argv[1] if len(sys.argv) >= 2 else "." # NOTE: Special handling of stats. None = Skip stat. # Note that All Resistance is actually 4 stats in the save files, that the game combines # into one. Same applies for all stats. special_stats = { "firemindam": {"template": "dmg-fire"}, "firemaxdam": { "template": "fire-max" }, # Cathan's rule, no other max ele dmg source exists "lightmindam": {"template": "dmg-ltng"}, "lightmaxdam": None, "magicmindam": {"template": "dmg-mag"}, "magicmaxdam": None, "coldmindam": {"template": "dmg-cold"}, "coldmaxdam": None, "coldlength": None, "poisonmindam": {"template": "dmg-pois"}, "poisonmaxdam": None, "poisonlength": None, "mindamage": {"template": "dmg-min"}, "maxdamage": {"template": "dmg-max"}, "secondary_mindamage": {"template": "dmg-min"}, "secondary_maxdamage": {"template": "dmg-max"}, "item_throw_mindamage": {"template": "dmg-min"}, "item_throw_maxdamage": {"template": "dmg-max"}, "item_maxdamage_percent": {"template": "dmg%"}, # max before min for this stat "item_mindamage_percent": None, "item_addclassskills": { "template": "ama", "param_tooltips": ["ama", "pal", "nec", "sor", "bar", "dru", "ass"], }, "item_extrablood": {"text": "Extra Blood"}, } # Patching of missing data in properties.txt # NOTE: Ideally we should implement the description functions referenced in itemstatcost.txt # instead of relying on the debug tooltips of properties.txt, since the game does not # actually use those, but rather they are present for the excel developers. properties_patches = { "dmg-min": {"stat1": "VARIABLE"}, "dmg-max": {"stat1": "VARIABLE"}, "dmg%": {"stat1": "item_maxdamage_percent", "stat2": "item_mindamage_percent"}, "indestruct": {"stat1": "item_indesctructible"}, } properties = {} stat_to_prop = {} with open(os.path.join(path, "properties.txt")) as f: reader = csv.DictReader(f, delimiter="\t") for row in reader: if row["code"] in properties_patches: row.update(properties_patches[row["code"]]) if len(row["stat1"]) == 0 or len(row["*Tooltip"]) == 0: continue prop = { "stats": [row[f"stat{i}"] for i in range(1, 8) if len(row[f"stat{i}"]) > 0], "tooltip": row["*Tooltip"], "val1": None if len(row["val1"]) == 0 else int(row["val1"]), } properties[row["code"]] = prop if len(prop["stats"]) == 1 and prop["stats"][0] not in stat_to_prop: stat_to_prop[prop["stats"][0]] = row["code"] itemstatcost = {} with open(os.path.join(path, "itemstatcost.txt")) as f: reader = csv.DictReader(f, delimiter="\t") for row in reader: itemstatcost[row["Stat"]] = { "id": row["*ID"], "save_bits": 0 if len(row["Save Bits"]) == 0 else int(row["Save Bits"]), "save_add": 0 if len(row["Save Add"]) == 0 else int(row["Save Add"]), "save_param_bits": None if len(row["Save Param Bits"]) == 0 else int(row["Save Param Bits"]), } stats = {} for stat in itemstatcost: try: special = ( special_stats[stat] if stat in special_stats else {"template": stat_to_prop[stat]} ) except KeyError: print( f"Failed getting property for stat {stat}. Skipping! (See `special_stats` for fixing)" ) continue # special = None -> Skip if special is None: continue # special.template: properties.txt row if "template" in special: prop = properties[special["template"]] obj = { "text": prop["tooltip"], "save_bits": [], "save_add": itemstatcost[stat]["save_add"], "save_param_bits": itemstatcost[stat]["save_param_bits"], } for prop_stat in prop["stats"]: if prop_stat == "VARIABLE": prop_stat = stat if itemstatcost[prop_stat]["save_add"] != obj["save_add"]: print( f"Unexpected divergence in save_add for stats in prop {special_stats[stat]}" ) obj["save_bits"].append(itemstatcost[prop_stat]["save_bits"]) # else: use stat row + custom data else: obj = { "text": "" if "text" not in special else special["text"], "save_bits": [itemstatcost[stat]["save_bits"]] if "save_bits" not in special else special["save_bits"], "save_add": itemstatcost[stat]["save_add"] if "save_add" not in special else special["save_add"], "save_param_bits": itemstatcost[stat]["save_param_bits"] if "save_param_bits" not in special else special["save_param_bits"], } # special.param_tooltips: extra tooltips depending on param value if "param_tooltips" in special: tooltips = [] for id in special["param_tooltips"]: prop = properties[id] tooltips.append( { "param": prop["val1"], "text": prop["tooltip"], } ) obj["text"] = tooltips stats[itemstatcost[stat]["id"]] = obj with open("stats.json", "w", newline="\n") as f: json.dump(stats, f, indent=4) f.write("\n")