forked from omicron/d2warehouse
153 lines
5.4 KiB
Python
153 lines
5.4 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: 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")
|