Add runeword parsing
This commit is contained in:
27
d2warehouse/contrib/runewords.py
Normal file
27
d2warehouse/contrib/runewords.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import csv
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
path = sys.argv[1] if len(sys.argv) >= 2 else "."
|
||||||
|
|
||||||
|
runewords = {}
|
||||||
|
id = 0
|
||||||
|
with open(os.path.join(path, "runes.txt")) as f:
|
||||||
|
dr = csv.DictReader(f, delimiter="\t")
|
||||||
|
for row in dr:
|
||||||
|
id += 1
|
||||||
|
if row["complete"] != "1":
|
||||||
|
continue
|
||||||
|
runewords[id + 26] = {
|
||||||
|
"name": row["*Rune Name"],
|
||||||
|
"itembases": [
|
||||||
|
row[f"itype{i}"] for i in range(1, 7) if len(row[f"itype{i}"]) > 0
|
||||||
|
],
|
||||||
|
"runes": [row[f"Rune{i}"] for i in range(1, 7) if len(row[f"Rune{i}"]) > 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
with open("runewords.json", "w", newline="\n") as f:
|
||||||
|
json.dump(runewords, f, indent=4)
|
||||||
|
f.write("\n")
|
||||||
1120
d2warehouse/data/runewords.json
Normal file
1120
d2warehouse/data/runewords.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@ _basetype_map = None
|
|||||||
_stats_map = None
|
_stats_map = None
|
||||||
_unique_map = None
|
_unique_map = None
|
||||||
_set_item_map = None
|
_set_item_map = None
|
||||||
|
_runeword_map = None
|
||||||
|
|
||||||
|
|
||||||
class Quality(Enum):
|
class Quality(Enum):
|
||||||
@@ -127,6 +128,8 @@ class Item:
|
|||||||
properties.append("Unidentified")
|
properties.append("Unidentified")
|
||||||
if self.is_socketed:
|
if self.is_socketed:
|
||||||
properties.append("Socketed")
|
properties.append("Socketed")
|
||||||
|
if self.is_runeword:
|
||||||
|
properties.append("Runeword")
|
||||||
if properties:
|
if properties:
|
||||||
print(" " * indent, ", ".join(properties))
|
print(" " * indent, ", ".join(properties))
|
||||||
print(" " * indent, f"at {self.pos_x}, {self.pos_y}")
|
print(" " * indent, f"at {self.pos_x}, {self.pos_y}")
|
||||||
@@ -143,7 +146,8 @@ class Item:
|
|||||||
itm = lookup_unique(self.unique_id)
|
itm = lookup_unique(self.unique_id)
|
||||||
print(" " * indent, f"{itm['name']} ({self.unique_id})")
|
print(" " * indent, f"{itm['name']} ({self.unique_id})")
|
||||||
if self.runeword_id:
|
if self.runeword_id:
|
||||||
print(" " * indent, f"Runeword Id: {self.runeword_id}") # TODO: name lookup
|
rw = lookup_runeword(self.runeword_id)
|
||||||
|
print(" " * indent, f"{rw['name']} runeword")
|
||||||
if self.personal_name:
|
if self.personal_name:
|
||||||
print(" " * indent, f"Personal name: {self.personal_name}")
|
print(" " * indent, f"Personal name: {self.personal_name}")
|
||||||
if self.defense:
|
if self.defense:
|
||||||
@@ -200,3 +204,11 @@ def lookup_set_item(id: int) -> dict:
|
|||||||
with open(os.path.join(_data_path, "sets.json")) as f:
|
with open(os.path.join(_data_path, "sets.json")) as f:
|
||||||
_set_item_map = json.load(f)
|
_set_item_map = json.load(f)
|
||||||
return _set_item_map[str(id)]
|
return _set_item_map[str(id)]
|
||||||
|
|
||||||
|
|
||||||
|
def lookup_runeword(id: int) -> dict:
|
||||||
|
global _runeword_map
|
||||||
|
if _runeword_map is None:
|
||||||
|
with open(os.path.join(_data_path, "runewords.json")) as f:
|
||||||
|
_runeword_map = json.load(f)
|
||||||
|
return _runeword_map[str(id)]
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ def parse_item(data: bytes) -> tuple[bytes, Item]:
|
|||||||
quality_end += inherent_end
|
quality_end += inherent_end
|
||||||
|
|
||||||
if item.is_runeword:
|
if item.is_runeword:
|
||||||
item.runeword_id, runeword_end = parse_runeword(bits[quality_end:], item)
|
item.runeword_id, runeword_end = parse_runeword(bits[quality_end:])
|
||||||
runeword_end += quality_end
|
runeword_end += quality_end
|
||||||
else:
|
else:
|
||||||
runeword_end = quality_end
|
runeword_end = quality_end
|
||||||
@@ -279,6 +279,7 @@ def parse_affix(bits: bitarray) -> tuple[int | None, int]:
|
|||||||
|
|
||||||
def parse_runeword(bits: bitarray) -> tuple[int, int]:
|
def parse_runeword(bits: bitarray) -> tuple[int, int]:
|
||||||
id = ba2int(bits[0:12])
|
id = ba2int(bits[0:12])
|
||||||
|
# + 4 unknown bits
|
||||||
return id, 16
|
return id, 16
|
||||||
|
|
||||||
|
|
||||||
@@ -346,6 +347,9 @@ def parse_enchantments(bits: bitarray, item: Item) -> tuple[Item, int]:
|
|||||||
}
|
}
|
||||||
num_lists = 1 + table[val]
|
num_lists = 1 + table[val]
|
||||||
ptr += 5
|
ptr += 5
|
||||||
|
elif item.is_runeword:
|
||||||
|
# Runewords have one empty dummy list of only 0x1ff followed by the runeword stats
|
||||||
|
num_lists = 2
|
||||||
else:
|
else:
|
||||||
num_lists = 1
|
num_lists = 1
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from d2warehouse.parser import parse_item
|
from d2warehouse.parser import parse_item
|
||||||
from d2warehouse.item import Quality
|
from d2warehouse.item import Quality, lookup_runeword
|
||||||
|
|
||||||
|
|
||||||
class ParseItemTest(unittest.TestCase):
|
class ParseItemTest(unittest.TestCase):
|
||||||
@@ -80,3 +80,22 @@ class ParseItemTest(unittest.TestCase):
|
|||||||
self.assertEqual(item.code, "spr")
|
self.assertEqual(item.code, "spr")
|
||||||
self.assertEqual(str(item.stats[0]), "+13% Enhanced Damage")
|
self.assertEqual(str(item.stats[0]), "+13% Enhanced Damage")
|
||||||
self.assertEqual(str(item.stats[2]), "+2 to Maximum Damage")
|
self.assertEqual(str(item.stats[2]), "+2 to Maximum Damage")
|
||||||
|
|
||||||
|
def test_runeworld_lore(self):
|
||||||
|
# Lore: Ort Sol
|
||||||
|
data = bytes.fromhex(
|
||||||
|
"1008800405c055c637f1073af4558697412981070881506049e87f005516fb134582ff1000a0003500e07cbb001000a0003504e07c9800"
|
||||||
|
)
|
||||||
|
data, item = parse_item(data)
|
||||||
|
# TODO: requires socket parsing
|
||||||
|
# self.assertEqual(data, b"")
|
||||||
|
self.assertTrue(item.is_runeword)
|
||||||
|
self.assertEqual(item.sockets, 2)
|
||||||
|
rw = lookup_runeword(item.runeword_id)
|
||||||
|
self.assertEqual(rw["name"], "Lore")
|
||||||
|
for stat in item.stats:
|
||||||
|
print(str(stat))
|
||||||
|
self.assertEqual(str(item.stats[4]), "+1 to All Skills") # runeword stat
|
||||||
|
self.assertEqual(str(item.stats[2]), "+10 to Energy") # runeword stat
|
||||||
|
# TODO: requires socket parsing
|
||||||
|
# self.assertEqual(str(item.stats[1]), "Lightning Resist 30%") # sol rune stat
|
||||||
|
|||||||
Reference in New Issue
Block a user