# 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: These map "linked stats" to code in properties.txt. # If None is given, the stat is excluded from the output. # 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": "dmg-fire", "firemaxdam": None, "lightmindam": "dmg-ltng", "lightmaxdam": None, "magicmindam": "dmg-mag", "magicmaxdam": None, "coldmindam": "dmg-cold", "coldmaxdam": None, "coldlength": None, "poisonmindam": "dmg-pois", "poisonmaxdam": None, "poisonlength": None, "mindamage": "dmg-norm", "maxdamage": None, "item_maxdamage_percent": "dmg%", # max before min for this stat "item_mindamage_percent": None, } # 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": "secondary_mindamage"}, "dmg-max": {"stat1": "secondary_maxdamage"}, "dmg%": {"stat1": "item_maxdamage_percent", "stat2": "item_mindamage_percent"}, } 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"], } 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, statdat in itemstatcost.items(): if stat in special_stats: if special_stats[stat] is None: continue else: prop = properties[special_stats[stat]] obj = { "text": prop["tooltip"], "save_bits": [], "save_add": itemstatcost[prop["stats"][0]]["save_add"], "save_param_bits": itemstatcost[prop["stats"][0]]["save_param_bits"], } for prop_stat in prop["stats"]: 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: try: prop = properties[stat_to_prop[stat]] except KeyError: print( f"Failed getting property for stat {stat}. Skipping! (See `special_stats` for fixing)" ) continue obj = { "text": prop["tooltip"], "save_bits": [statdat["save_bits"]], "save_add": statdat["save_add"], "save_param_bits": statdat["save_param_bits"], } stats[statdat["id"]] = obj with open("stats.json", "w", newline="\n") as f: json.dump(stats, f, indent=4) f.write("\n")