Files
d2warehouse/contrib/stats.py
2023-10-25 12:51:00 +02:00

112 lines
4.0 KiB
Python

# 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")