5 Commits
main ... main

Author SHA1 Message Date
b8b79a0ea5 currency count no longer counts deleted items 2025-10-01 22:21:47 +02:00
20fbdfdea9 Update data files with correct set and unique names 2025-10-01 20:13:14 +00:00
a8938264b0 Make set/unique extract script use item-names.json 2025-10-01 20:13:14 +00:00
8cca6e3464 Implement names for stat 107 and 188 2025-09-28 12:08:51 +02:00
9e7d69f667 Add skills.json parser & data
Build a lookup table to map from Id to string.
2025-09-27 13:44:52 +02:00
7 changed files with 1479 additions and 120 deletions

28
contrib/skills.py Normal file
View File

@@ -0,0 +1,28 @@
import json
import csv
import os
import sys
path = sys.argv[1] if len(sys.argv) >= 2 else "."
items = {}
item_patches = {
"tbk": {"class": "tome"},
"ibk": {"class": "tome"},
}
with open(os.path.join(path, "skills.json"), encoding="utf-8-sig") as f:
rows = json.load(f)
lookup_table = {}
for entry in rows:
key = entry["Key"]
text = entry["enUS"]
if len(text.strip()) == 0:
continue
lookup_table[key] = text
with open("skills.json", "w", newline="\n") as f:
json.dump(lookup_table, f, indent=4)
f.write("\n")

View File

@@ -1,21 +1,36 @@
import csv
import json
import os
import sys
import argparse
from pathlib import Path
path = sys.argv[1] if len(sys.argv) >= 2 else "."
parser = argparse.ArgumentParser(
description="Process unique and set items from game data"
)
parser.add_argument(
"DATA_DIR", help="Path to d2 data dir containing local/ and global/"
)
parser.add_argument("OUTPUT_DIR", help="Path to destination directory")
args = parser.parse_args()
excelpath = Path(args.DATA_DIR) / "global/excel"
outputpath = Path(args.OUTPUT_DIR)
namespath = Path(args.DATA_DIR) / "local/lng/strings/item-names.json"
with namespath.open(encoding="utf-8-sig") as f:
names = json.load(f)
names = {name["Key"]: name["enUS"] for name in names}
category = "Base"
setitems = {}
with open(os.path.join(path, "setitems.txt")) as f:
with (excelpath / "setitems.txt").open() as f:
dr = csv.DictReader(f, delimiter="\t")
for row in dr:
if row["index"] == "Expansion":
category = row["index"]
continue
setitems[row["*ID"]] = {
"name": row["index"],
"set": row["set"],
"name": names[row["index"]],
"set": names[row["set"]],
"itembase": row["item"],
"req_lvl": int(row["lvl req"]),
"ilvl": int(row["lvl"]),
@@ -25,7 +40,7 @@ with open(os.path.join(path, "setitems.txt")) as f:
category = "Base"
uniqueitems = {}
with open(os.path.join(path, "uniqueitems.txt")) as f:
with (excelpath / "uniqueitems.txt").open() as f:
dr = csv.DictReader(f, delimiter="\t")
for row in dr:
if row["index"] in [
@@ -42,7 +57,7 @@ with open(os.path.join(path, "uniqueitems.txt")) as f:
if len(row["lvl req"]) == 0:
continue # deleted uniques
uniqueitems[row["*ID"]] = {
"name": row["index"],
"name": names[row["index"]],
"itembase": row["code"],
"req_lvl": int(row["lvl req"]),
"ilvl": int(row["lvl"]),
@@ -50,10 +65,10 @@ with open(os.path.join(path, "uniqueitems.txt")) as f:
"category": category,
}
with open("uniques.json", "w", newline="\n") as f:
with (outputpath / "uniques.json").open("w", newline="\n") as f:
json.dump(uniqueitems, f, indent=4)
f.write("\n")
with open("sets.json", "w", newline="\n") as f:
with (outputpath / "sets.json").open("w", newline="\n") as f:
json.dump(setitems, f, indent=4)
f.write("\n")

View File

@@ -325,7 +325,7 @@ def storage_currency_counts(item_codes: list[str], stash_name: str) -> dict:
for code in item_codes:
currencies[code] = {
"count": db.execute(
"SELECT COUNT(id) FROM item WHERE code = ?", (code,)
"SELECT COUNT(id) FROM item WHERE code = ? AND deleted IS NULL", (code,)
).fetchone()[0],
"name": lookup_basetype(code)["name"],
}

View File

@@ -397,7 +397,7 @@
},
"44": {
"name": "Berserker's Headgear",
"set": "Berserker's Garb",
"set": "Berserker's Arsenal",
"itembase": "hlm",
"req_lvl": 3,
"ilvl": 5,
@@ -406,7 +406,7 @@
},
"45": {
"name": "Berserker's Hauberk",
"set": "Berserker's Garb",
"set": "Berserker's Arsenal",
"itembase": "spl",
"req_lvl": 3,
"ilvl": 5,
@@ -415,7 +415,7 @@
},
"46": {
"name": "Berserker's Hatchet",
"set": "Berserker's Garb",
"set": "Berserker's Arsenal",
"itembase": "2ax",
"req_lvl": 3,
"ilvl": 5,
@@ -451,7 +451,7 @@
},
"50": {
"name": "Angelic Sickle",
"set": "Angelical Raiment",
"set": "Angelic Raiment",
"itembase": "sbr",
"req_lvl": 12,
"ilvl": 17,
@@ -460,7 +460,7 @@
},
"51": {
"name": "Angelic Mantle",
"set": "Angelical Raiment",
"set": "Angelic Raiment",
"itembase": "rng",
"req_lvl": 12,
"ilvl": 17,
@@ -469,7 +469,7 @@
},
"52": {
"name": "Angelic Halo",
"set": "Angelical Raiment",
"set": "Angelic Raiment",
"itembase": "rin",
"req_lvl": 12,
"ilvl": 17,
@@ -478,7 +478,7 @@
},
"53": {
"name": "Angelic Wings",
"set": "Angelical Raiment",
"set": "Angelic Raiment",
"itembase": "amu",
"req_lvl": 12,
"ilvl": 17,
@@ -612,7 +612,7 @@
"category": "Expansion"
},
"68": {
"name": "Aldur's Gauntlet",
"name": "Aldur's Rhythm",
"set": "Aldur's Watchtower",
"itembase": "9mt",
"req_lvl": 42,
@@ -684,7 +684,7 @@
"category": "Expansion"
},
"76": {
"name": "Tal Rasha's Fire-Spun Cloth",
"name": "Tal Rasha's Fine-Spun Cloth",
"set": "Tal Rasha's Wrappings",
"itembase": "zmb",
"req_lvl": 53,
@@ -711,7 +711,7 @@
"category": "Expansion"
},
"79": {
"name": "Tal Rasha's Howling Wind",
"name": "Tal Rasha's Guardianship",
"set": "Tal Rasha's Wrappings",
"itembase": "uth",
"req_lvl": 71,
@@ -747,7 +747,7 @@
"category": "Expansion"
},
"83": {
"name": "Griswolds's Redemption",
"name": "Griswold's Redemption",
"set": "Griswold's Legacy",
"itembase": "7ws",
"req_lvl": 53,
@@ -882,7 +882,7 @@
"category": "Expansion"
},
"98": {
"name": "Spiritual Custodian",
"name": "Dark Adherent",
"set": "The Disciple",
"itembase": "uui",
"req_lvl": 43,
@@ -909,7 +909,7 @@
"category": "Expansion"
},
"101": {
"name": "Heaven's Taebaek",
"name": "Taebaek's Glory",
"set": "Heaven's Brethren",
"itembase": "uts",
"req_lvl": 81,
@@ -918,7 +918,7 @@
"category": "Expansion"
},
"102": {
"name": "Haemosu's Adament",
"name": "Haemosu's Adamant",
"set": "Heaven's Brethren",
"itembase": "xrs",
"req_lvl": 44,
@@ -963,7 +963,7 @@
"category": "Expansion"
},
"107": {
"name": "Wihtstan's Guard",
"name": "Whitstan's Guard",
"set": "Orphan's Call",
"itembase": "xml",
"req_lvl": 29,
@@ -990,7 +990,7 @@
"category": "Expansion"
},
"110": {
"name": "Hwanin's Seal",
"name": "Hwanin's Blessing",
"set": "Hwanin's Majesty",
"itembase": "mbl",
"req_lvl": 35,
@@ -1071,7 +1071,7 @@
"category": "Expansion"
},
"119": {
"name": "Cow King's Hoofs",
"name": "Cow King's Hooves",
"set": "Cow King's Leathers",
"itembase": "vbt",
"req_lvl": 13,
@@ -1081,7 +1081,7 @@
},
"120": {
"name": "Naj's Puzzler",
"set": "Naj's Ancient Set",
"set": "Naj's Ancient Vestige",
"itembase": "6cs",
"req_lvl": 78,
"ilvl": 43,
@@ -1090,7 +1090,7 @@
},
"121": {
"name": "Naj's Light Plate",
"set": "Naj's Ancient Set",
"set": "Naj's Ancient Vestige",
"itembase": "ult",
"req_lvl": 71,
"ilvl": 43,
@@ -1099,7 +1099,7 @@
},
"122": {
"name": "Naj's Circlet",
"set": "Naj's Ancient Set",
"set": "Naj's Ancient Vestige",
"itembase": "ci0",
"req_lvl": 28,
"ilvl": 43,
@@ -1107,8 +1107,8 @@
"category": "Expansion"
},
"123": {
"name": "McAuley's Paragon",
"set": "McAuley's Folly",
"name": "Sander's Paragon",
"set": "Sander's Folly",
"itembase": "cap",
"req_lvl": 25,
"ilvl": 20,
@@ -1116,8 +1116,8 @@
"category": "Expansion"
},
"124": {
"name": "McAuley's Riprap",
"set": "McAuley's Folly",
"name": "Sander's Riprap",
"set": "Sander's Folly",
"itembase": "vbt",
"req_lvl": 20,
"ilvl": 20,
@@ -1125,8 +1125,8 @@
"category": "Expansion"
},
"125": {
"name": "McAuley's Taboo",
"set": "McAuley's Folly",
"name": "Sander's Taboo",
"set": "Sander's Folly",
"itembase": "vgl",
"req_lvl": 28,
"ilvl": 20,
@@ -1134,8 +1134,8 @@
"category": "Expansion"
},
"126": {
"name": "McAuley's Superstition",
"set": "McAuley's Folly",
"name": "Sander's Superstition",
"set": "Sander's Folly",
"itembase": "bwn",
"req_lvl": 25,
"ilvl": 20,

1251
d2warehouse/data/skills.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,7 @@
"category": "Base"
},
"3": {
"name": "Mindrend",
"name": "Skull Splitter",
"itembase": "mpi",
"req_lvl": 21,
"ilvl": 28,
@@ -40,7 +40,7 @@
"category": "Base"
},
"5": {
"name": "Fechmars Axe",
"name": "Axe of Fechmar",
"itembase": "lax",
"req_lvl": 8,
"ilvl": 11,
@@ -56,7 +56,7 @@
"category": "Base"
},
"7": {
"name": "The Chieftan",
"name": "The Chieftain",
"itembase": "btx",
"req_lvl": 19,
"ilvl": 26,
@@ -72,7 +72,7 @@
"category": "Base"
},
"9": {
"name": "The Humongous",
"name": "Humongous",
"itembase": "gix",
"req_lvl": 29,
"ilvl": 39,
@@ -80,7 +80,7 @@
"category": "Base"
},
"10": {
"name": "Iros Torch",
"name": "Torch of Iro",
"itembase": "wnd",
"req_lvl": 5,
"ilvl": 7,
@@ -88,7 +88,7 @@
"category": "Base"
},
"11": {
"name": "Maelstromwrath",
"name": "Maelstrom",
"itembase": "ywn",
"req_lvl": 14,
"ilvl": 19,
@@ -104,7 +104,7 @@
"category": "Base"
},
"13": {
"name": "Umes Lament",
"name": "Ume's Lament",
"itembase": "gwn",
"req_lvl": 28,
"ilvl": 38,
@@ -168,7 +168,7 @@
"category": "Base"
},
"21": {
"name": "The Generals Tan Do Li Ga",
"name": "The General's Tan Do Li Ga",
"itembase": "fla",
"req_lvl": 21,
"ilvl": 28,
@@ -184,7 +184,7 @@
"category": "Base"
},
"23": {
"name": "Bonesob",
"name": "Bonesnap",
"itembase": "mau",
"req_lvl": 24,
"ilvl": 32,
@@ -200,7 +200,7 @@
"category": "Base"
},
"25": {
"name": "Rixots Keen",
"name": "Rixot's Keen",
"itembase": "ssd",
"req_lvl": 2,
"ilvl": 3,
@@ -216,7 +216,7 @@
"category": "Base"
},
"27": {
"name": "Krintizs Skewer",
"name": "Skewer of Krintiz",
"itembase": "sbr",
"req_lvl": 10,
"ilvl": 14,
@@ -240,7 +240,7 @@
"category": "Base"
},
"30": {
"name": "Griswolds Edge",
"name": "Griswold's Edge",
"itembase": "bsd",
"req_lvl": 17,
"ilvl": 23,
@@ -256,7 +256,7 @@
"category": "Base"
},
"32": {
"name": "Culwens Point",
"name": "Culwen's Point",
"itembase": "wsd",
"req_lvl": 29,
"ilvl": 39,
@@ -280,7 +280,7 @@
"category": "Base"
},
"35": {
"name": "Kinemils Awl",
"name": "Kinemil's Awl",
"itembase": "gis",
"req_lvl": 23,
"ilvl": 31,
@@ -336,7 +336,7 @@
"category": "Base"
},
"42": {
"name": "Irices Shard",
"name": "Spectral Shard",
"itembase": "bld",
"req_lvl": 25,
"ilvl": 34,
@@ -384,7 +384,7 @@
"category": "Base"
},
"48": {
"name": "Dimoaks Hew",
"name": "Dimoak's Hew",
"itembase": "bar",
"req_lvl": 8,
"ilvl": 11,
@@ -448,7 +448,7 @@
"category": "Base"
},
"56": {
"name": "Lazarus Spire",
"name": "Spire of Lazarus",
"itembase": "cst",
"req_lvl": 18,
"ilvl": 24,
@@ -488,7 +488,7 @@
"category": "Base"
},
"61": {
"name": "Rimeraven",
"name": "Raven Claw",
"itembase": "lbw",
"req_lvl": 15,
"ilvl": 20,
@@ -496,7 +496,7 @@
"category": "Base"
},
"62": {
"name": "Piercerib",
"name": "Rogue's Bow",
"itembase": "cbw",
"req_lvl": 20,
"ilvl": 27,
@@ -504,7 +504,7 @@
"category": "Base"
},
"63": {
"name": "Pullspite",
"name": "Stormstrike",
"itembase": "sbb",
"req_lvl": 25,
"ilvl": 34,
@@ -560,7 +560,7 @@
"category": "Base"
},
"70": {
"name": "Doomspittle",
"name": "Doomslinger",
"itembase": "rxb",
"req_lvl": 28,
"ilvl": 38,
@@ -568,7 +568,7 @@
"category": "Base"
},
"71": {
"name": "War Bonnet",
"name": "Biggin's Bonnet",
"itembase": "cap",
"req_lvl": 3,
"ilvl": 4,
@@ -640,7 +640,7 @@
"category": "Base"
},
"80": {
"name": "Blinkbats Form",
"name": "Blinkbat's Form",
"itembase": "lea",
"req_lvl": 12,
"ilvl": 16,
@@ -688,7 +688,7 @@
"category": "Base"
},
"86": {
"name": "Venomsward",
"name": "Venom Ward",
"itembase": "brs",
"req_lvl": 20,
"ilvl": 27,
@@ -736,7 +736,7 @@
"category": "Base"
},
"92": {
"name": "Victors Silk",
"name": "Silks of the Victor",
"itembase": "aar",
"req_lvl": 28,
"ilvl": 38,
@@ -896,7 +896,7 @@
"category": "Base"
},
"112": {
"name": "Lenyms Cord",
"name": "Lenymo",
"itembase": "lbl",
"req_lvl": 7,
"ilvl": 10,
@@ -1016,7 +1016,7 @@
"category": "Base"
},
"127": {
"name": "KhalimFlail",
"name": "Khalim's Flail",
"itembase": "qf1",
"req_lvl": 0,
"ilvl": 0,
@@ -1024,7 +1024,7 @@
"category": "Base"
},
"128": {
"name": "SuperKhalimFlail",
"name": "Khalim's Will",
"itembase": "qf2",
"req_lvl": 0,
"ilvl": 0,
@@ -1056,7 +1056,7 @@
"category": "Expansion"
},
"132": {
"name": "Pompe's Wrath",
"name": "Pompeii's Wrath",
"itembase": "9mp",
"req_lvl": 45,
"ilvl": 53,
@@ -1104,7 +1104,7 @@
"category": "Expansion"
},
"138": {
"name": "The Minataur",
"name": "The Minotaur",
"itembase": "9gi",
"req_lvl": 45,
"ilvl": 53,
@@ -1288,7 +1288,7 @@
"category": "Expansion"
},
"161": {
"name": "The Atlantian",
"name": "The Atlantean",
"itembase": "9wd",
"req_lvl": 42,
"ilvl": 50,
@@ -1496,7 +1496,7 @@
"category": "Expansion"
},
"187": {
"name": "Skullcollector",
"name": "Skull Collector",
"itembase": "8ws",
"req_lvl": 41,
"ilvl": 49,
@@ -1536,7 +1536,7 @@
"category": "Expansion"
},
"192": {
"name": "Whichwild String",
"name": "Witchwild String",
"itembase": "8s8",
"req_lvl": 39,
"ilvl": 47,
@@ -1560,7 +1560,7 @@
"category": "Expansion"
},
"195": {
"name": "Godstrike Arch",
"name": "Goldstrike Arch",
"itembase": "8lw",
"req_lvl": 46,
"ilvl": 54,
@@ -1576,7 +1576,7 @@
"category": "Expansion"
},
"197": {
"name": "Pus Spiter",
"name": "Pus Spitter",
"itembase": "8mx",
"req_lvl": 36,
"ilvl": 44,
@@ -1600,7 +1600,7 @@
"category": "Expansion"
},
"201": {
"name": "Peasent Crown",
"name": "Peasant Crown",
"itembase": "xap",
"req_lvl": 28,
"ilvl": 36,
@@ -1632,7 +1632,7 @@
"category": "Armor"
},
"205": {
"name": "Valkiry Wing",
"name": "Valkyrie Wing",
"itembase": "xhm",
"req_lvl": 44,
"ilvl": 52,
@@ -1656,7 +1656,7 @@
"category": "Armor"
},
"208": {
"name": "Vampiregaze",
"name": "Vampire Gaze",
"itembase": "xh9",
"req_lvl": 41,
"ilvl": 49,
@@ -1680,7 +1680,7 @@
"category": "Armor"
},
"211": {
"name": "Skin of the Flayerd One",
"name": "Skin of the Flayed One",
"itembase": "xla",
"req_lvl": 31,
"ilvl": 39,
@@ -1688,7 +1688,7 @@
"category": "Armor"
},
"212": {
"name": "Ironpelt",
"name": "Iron Pelt",
"itembase": "xtu",
"req_lvl": 33,
"ilvl": 41,
@@ -1696,7 +1696,7 @@
"category": "Armor"
},
"213": {
"name": "Spiritforge",
"name": "Spirit Forge",
"itembase": "xng",
"req_lvl": 35,
"ilvl": 43,
@@ -1776,7 +1776,7 @@
"category": "Armor"
},
"223": {
"name": "Que-Hegan's Wisdon",
"name": "Que-Hegan's Wisdom",
"itembase": "xtp",
"req_lvl": 51,
"ilvl": 59,
@@ -1792,7 +1792,7 @@
"category": "Armor"
},
"225": {
"name": "Mosers Blessed Circle",
"name": "Moser's Blessed Circle",
"itembase": "xml",
"req_lvl": 31,
"ilvl": 39,
@@ -1816,7 +1816,7 @@
"category": "Armor"
},
"228": {
"name": "Kerke's Sanctuary",
"name": "Gerke's Sanctuary",
"itembase": "xow",
"req_lvl": 44,
"ilvl": 52,
@@ -1824,7 +1824,7 @@
"category": "Armor"
},
"229": {
"name": "Radimant's Sphere",
"name": "Radament's Sphere",
"itembase": "xts",
"req_lvl": 50,
"ilvl": 58,
@@ -1872,7 +1872,7 @@
"category": "Armor"
},
"235": {
"name": "Lavagout",
"name": "Lava Gout",
"itembase": "xtg",
"req_lvl": 42,
"ilvl": 50,
@@ -1912,7 +1912,7 @@
"category": "Armor"
},
"240": {
"name": "Wartraveler",
"name": "War Traveler",
"itembase": "xtb",
"req_lvl": 42,
"ilvl": 50,
@@ -1920,7 +1920,7 @@
"category": "Armor"
},
"241": {
"name": "Gorerider",
"name": "Gore Rider",
"itembase": "xhb",
"req_lvl": 47,
"ilvl": 55,
@@ -1944,7 +1944,7 @@
"category": "Armor"
},
"244": {
"name": "Gloomstrap",
"name": "Gloom's Trap",
"itembase": "zmb",
"req_lvl": 36,
"ilvl": 45,
@@ -1960,7 +1960,7 @@
"category": "Armor"
},
"246": {
"name": "Thudergod's Vigor",
"name": "Thundergod's Vigor",
"itembase": "zhb",
"req_lvl": 47,
"ilvl": 55,
@@ -2120,7 +2120,7 @@
"category": "Elite Uniques"
},
"268": {
"name": "Bul Katho's Wedding Band",
"name": "Bul-Kathos' Wedding Band",
"itembase": "rin",
"req_lvl": 58,
"ilvl": 66,
@@ -2256,7 +2256,7 @@
"category": "Class Specific"
},
"286": {
"name": "Cutthroat1",
"name": "Bartuc's Cut-Throat",
"itembase": "9tw",
"req_lvl": 42,
"ilvl": 50,
@@ -2288,7 +2288,7 @@
"category": "Patch 1.10+"
},
"290": {
"name": "Djinnslayer",
"name": "Djinn Slayer",
"itembase": "7sm",
"req_lvl": 65,
"ilvl": 73,
@@ -2312,7 +2312,7 @@
"category": "Patch 1.10+"
},
"293": {
"name": "Gutsiphon",
"name": "Gut Siphon",
"itembase": "6rx",
"req_lvl": 71,
"ilvl": 79,
@@ -2320,7 +2320,7 @@
"category": "Patch 1.10+"
},
"294": {
"name": "Razoredge",
"name": "Razor's Edge",
"itembase": "7ha",
"req_lvl": 67,
"ilvl": 75,
@@ -2328,7 +2328,7 @@
"category": "Patch 1.10+"
},
"296": {
"name": "Demonlimb",
"name": "Demon Limb",
"itembase": "7sp",
"req_lvl": 63,
"ilvl": 71,
@@ -2336,7 +2336,7 @@
"category": "Patch 1.10+"
},
"297": {
"name": "Steelshade",
"name": "Steel Shade",
"itembase": "ulm",
"req_lvl": 62,
"ilvl": 70,
@@ -2352,7 +2352,7 @@
"category": "Patch 1.10+"
},
"299": {
"name": "Deaths's Web",
"name": "Death's Web",
"itembase": "7gw",
"req_lvl": 66,
"ilvl": 74,
@@ -2408,7 +2408,7 @@
"category": "Patch 1.10+"
},
"308": {
"name": "Jadetalon",
"name": "Jade Talon",
"itembase": "7wb",
"req_lvl": 66,
"ilvl": 74,
@@ -2416,7 +2416,7 @@
"category": "Patch 1.10+"
},
"309": {
"name": "Shadowdancer",
"name": "Shadow Dancer",
"itembase": "uhb",
"req_lvl": 71,
"ilvl": 79,
@@ -2424,7 +2424,7 @@
"category": "Patch 1.10+"
},
"310": {
"name": "Cerebus",
"name": "Cerebus' Bite",
"itembase": "drb",
"req_lvl": 63,
"ilvl": 71,
@@ -2440,7 +2440,7 @@
"category": "Patch 1.10+"
},
"312": {
"name": "Souldrain",
"name": "Soul Drainer",
"itembase": "umg",
"req_lvl": 74,
"ilvl": 82,
@@ -2448,7 +2448,7 @@
"category": "Patch 1.10+"
},
"313": {
"name": "Runemaster",
"name": "Rune Master",
"itembase": "72a",
"req_lvl": 72,
"ilvl": 80,
@@ -2456,7 +2456,7 @@
"category": "Patch 1.10+"
},
"314": {
"name": "Deathcleaver",
"name": "Death Cleaver",
"itembase": "7wa",
"req_lvl": 70,
"ilvl": 78,
@@ -2488,7 +2488,7 @@
"category": "Patch 1.10+"
},
"319": {
"name": "Wisp",
"name": "Wisp Projector",
"itembase": "rin",
"req_lvl": 76,
"ilvl": 84,
@@ -2552,7 +2552,7 @@
"category": "Patch 1.10+"
},
"327": {
"name": "Spiritkeeper",
"name": "Spirit Keeper",
"itembase": "drd",
"req_lvl": 67,
"ilvl": 75,
@@ -2576,7 +2576,7 @@
"category": "Patch 1.10+"
},
"330": {
"name": "Darkforge Spawn",
"name": "Darkforce Spawn",
"itembase": "nef",
"req_lvl": 64,
"ilvl": 72,
@@ -2592,7 +2592,7 @@
"category": "Patch 1.10+"
},
"332": {
"name": "Bloodraven's Charge",
"name": "Blood Raven's Charge",
"itembase": "amb",
"req_lvl": 71,
"ilvl": 79,
@@ -2608,7 +2608,7 @@
"category": "Patch 1.10+"
},
"334": {
"name": "Shadowkiller",
"name": "Shadow Killer",
"itembase": "7cs",
"req_lvl": 78,
"ilvl": 85,
@@ -2664,7 +2664,7 @@
"category": "Patch 1.10+"
},
"342": {
"name": "Steelpillar",
"name": "Steel Pillar",
"itembase": "7p7",
"req_lvl": 69,
"ilvl": 77,
@@ -2704,7 +2704,7 @@
"category": "Patch 1.10+"
},
"348": {
"name": "Steel Carapice",
"name": "Steel Carapace",
"itembase": "uul",
"req_lvl": 66,
"ilvl": 74,
@@ -2744,7 +2744,7 @@
"category": "Patch 1.10+"
},
"354": {
"name": "Fathom",
"name": "Death's Fathom",
"itembase": "obf",
"req_lvl": 73,
"ilvl": 81,
@@ -2840,7 +2840,7 @@
"category": "Patch 1.10+"
},
"367": {
"name": "Eschuta's temper",
"name": "Eschuta's Temper",
"itembase": "obc",
"req_lvl": 72,
"ilvl": 80,
@@ -2904,7 +2904,7 @@
"category": "Patch 1.10+"
},
"376": {
"name": "Verdugo's Hearty Cord",
"name": "Verdungo's Hearty Cord",
"itembase": "umc",
"req_lvl": 63,
"ilvl": 71,
@@ -2920,7 +2920,7 @@
"category": "Patch 1.10+"
},
"379": {
"name": "Giantskull",
"name": "Giant Skull",
"itembase": "uh9",
"req_lvl": 65,
"ilvl": 73,
@@ -2928,7 +2928,7 @@
"category": "Patch 1.10+"
},
"380": {
"name": "Ironward",
"name": "Astreon's Iron Ward",
"itembase": "7ws",
"req_lvl": 60,
"ilvl": 68,
@@ -2968,7 +2968,7 @@
"category": "Patch 1.10+"
},
"385": {
"name": "Earthshifter",
"name": "Earth Shifter",
"itembase": "7gm",
"req_lvl": 69,
"ilvl": 77,
@@ -2976,7 +2976,7 @@
"category": "Patch 1.10+"
},
"386": {
"name": "Wraithflight",
"name": "Wraith Flight",
"itembase": "7gl",
"req_lvl": 76,
"ilvl": 84,
@@ -3000,7 +3000,7 @@
"category": "Patch 1.10+"
},
"389": {
"name": "The Reedeemer",
"name": "The Redeemer",
"itembase": "7sc",
"req_lvl": 72,
"ilvl": 80,
@@ -3008,7 +3008,7 @@
"category": "Patch 1.10+"
},
"390": {
"name": "Headhunter's Glory",
"name": "Head Hunter's Glory",
"itembase": "ush",
"req_lvl": 75,
"ilvl": 83,

View File

@@ -31,6 +31,7 @@ _unique_map = None
_set_item_map = None
_runeword_map = None
_affix_map = None
_skills_map = None
class Quality(IntEnum):
@@ -77,8 +78,18 @@ class Stat:
for val in self.values:
subst_text = subst_text.replace("#", str(val), 1)
if param:
subst_text = re.sub(r"\[[^\]]*\]", str(param), subst_text, 1)
subst_text = self.try_add_skill_text(subst_text)
return subst_text
def try_add_skill_text(self, subst_text: str) -> str | None:
if self.id == 107: # +X to [Skill] ([Class] only)
return re.sub(r"\[[^\]]*\]", lookup_skill_name(self.parameter), subst_text, 1)
elif self.id == 188: # +X to [Skill]
if '[' in subst_text:
return subst_text[:subst_text.find('[')] + lookup_random_skill_tab(self.parameter)
return re.sub(r"\[[^\]]*\]", lookup_random_skill_tab(self.parameter), subst_text, 1)
else:
return re.sub(r"\[[^\]]*\]", str(self.parameter), subst_text, 1)
def txtbits(bits: bitarray) -> str:
@@ -546,3 +557,57 @@ def lookup_affix(id: int, prefix: bool) -> dict:
with open(os.path.join(_data_path, "affixes.json")) as f:
_affix_map = json.load(f)
return _affix_map["prefixes" if prefix else "suffixes"][str(id)]
def _get_skills_map():
global _skills_map
if _skills_map is None:
with open(os.path.join(_data_path, "skills.json")) as f:
_skills_map = json.load(f)
return _skills_map
def lookup_skill_name(id: int) -> str:
# FIXME: This hackish way of calculating the key is because we directly index into local/lng/strings/skills.json
# but the actual ID points to global/excel/skills.txt
skills_map = _get_skills_map()
try:
try:
return skills_map[f"Skillname{id + 1}"]
except KeyError:
return skills_map[f"skillname{id}"]
except KeyError:
return f"<Invalid key: skillname{id} or Skillname{id + 1}>"
def lookup_random_skill_tab(id: int) -> str:
# (ClassId * 8) is the id of the first tab for that class
skills_map = _get_skills_map()
class_name = lookup_class(int(id / 8))
two_letter_class_code = lookup_class(int(id / 8))[:2]
tab_index = 3 - id % 8 # for some reason local/lng/strings/skills.json index backwards (0 -> 3, 1 -> 2, ...)
try:
return skills_map[f"SkillCategory{two_letter_class_code}{tab_index}"] + f" ({class_name} only)"
except KeyError:
return f"<Invalid random skill tab: {id}>"
def lookup_class(id: int) -> str:
match id:
case 0:
return "Amazon"
case 1:
return "Sorceress"
case 2:
return "Necromancer"
case 3:
return "Paladin"
case 4:
return "Barbarian"
case 5:
return "Druid"
case 6:
return "Assassin"
case _:
# TODO: In 3.11 replace this with assert_never
assert False, f"{id} Should be unreachable"