Switch from affixes.json to stats.json

This commit is contained in:
2023-10-23 16:19:49 +02:00
parent b72166be0b
commit f6bfad2d4b
3 changed files with 73 additions and 291 deletions

View File

@@ -1,215 +0,0 @@
{
"0": {"bits": [8], "bias": 32, "text": "+{0} to Strength"},
"1": {"bits": [7], "bias": 32, "text": "+{0} to Energy"},
"2": {"bits": [7], "bias": 32, "text": "+{0} to Dexterity"},
"3": {"bits": [7], "bias": 32, "text": "+{0} to Vitality"},
"7": {"bits": [9], "bias": 32, "text": "+{0} to Life"},
"9": {"bits": [8], "bias": 32, "text": "+{0} to Mana"},
"11": {"bits": [8], "bias": 32, "text": "+{0} to Maximum Stamina"},
"16": {"bits": [9], "bias": 0, "text": "+{0}% Enhanced Defense"},
"17": {"bits": [9, 9], "bias": 0, "text": "+{0}% Enhanced Damage"},
"19": {"bits": [10], "bias": 0, "text": "+{0} to Attack rating"},
"20": {"bits": [6], "bias": 0, "text": "+{0}% Increased chance of blocking"},
"21": {"bits": [6], "bias": 0, "text": "+{0} to Minimum 1-handed damage"},
"22": {"bits": [7], "bias": 0, "text": "+{0} to Maximum 1-handed damage"},
"23": {"bits": [6], "bias": 0, "text": "+{0} to Minimum 2-handed damage"},
"24": {"bits": [7], "bias": 0, "text": "+{0} to Maximum 2-handed damage"},
"25": {"bits": [8], "bias": 0, "text": "Unknown (Invisible)"},
"26": {"bits": [8], "bias": 0, "text": "Unknown (Invisible)"},
"27": {"bits": [8], "bias": 0, "text": "Regenerate Mana {0}%"},
"28": {"bits": [8], "bias": 0, "text": "Heal Stamina {0}%"},
"31": {"bits": [11], "bias": 10, "text": "+{0} Defense"},
"32": {"bits": [9], "bias": 0, "text": "+{0} vs. Missile"},
"33": {"bits": [8], "bias": 10, "text": "+{0} vs. Melee"},
"34": {"bits": [6], "bias": 0, "text": "Damage Reduced by {0}"},
"35": {"bits": [6], "bias": 0, "text": "Magic Damage Reduced by {0}"},
"36": {"bits": [8], "bias": 0, "text": "Damage Reduced by {0}%"},
"37": {"bits": [8], "bias": 0, "text": "Magic Resist +{0}%"},
"38": {"bits": [8], "bias": 0, "text": "+{0}% to Maximum Magic Resist"},
"39": {"bits": [8], "bias": 50, "text": "Fire Resist +{0}%"},
"40": {"bits": [5], "bias": 0, "text": "+{0}% to Maximum Fire Resist"},
"41": {"bits": [8], "bias": 50, "text": "Lightning Resist +{0}%"},
"42": {"bits": [5], "bias": 0, "text": "+{0}% to Maximum Lightning Resist"},
"43": {"bits": [9], "bias": 200, "text": "Cold Resist +{0}%"},
"44": {"bits": [5], "bias": 0, "text": "+{0}% to Maximum Cold Resist"},
"45": {"bits": [8], "bias": 50, "text": "Poison Resist +{0}%"},
"46": {"bits": [5], "bias": 0, "text": "+{0}% to Maximum Poison Resist"},
"48": {"bits": [8, 9], "bias": 0, "text": "Adds {0}-{1} Fire Damage"},
"49": {"bits": [9], "bias": 0, "text": "+{0} to Maximum Fire Damage"},
"50": {"bits": [6, 10], "bias": 0, "text": "Adds {0}-{1} Lightning Damage"},
"52": {"bits": [8, 9], "bias": 0, "text": "Adds {0}-{1} Magic Damage"},
"54": {"bits": [8, 9, 8], "bias": 0, "text": "Adds {0}-{1} Cold Damage"},
"57": {"bits": [10, 10, 9], "bias": 0, "text": "Adds {0}-{1} Poison Damage over {2} Seconds"},
"60": {"bits": [7], "bias": 0, "text": "{0}% Life Stolen Per Hit"},
"62": {"bits": [7], "bias": 0, "text": "{0}% Mana Stolen Per Hit"},
"67": {"bits": [7], "bias": 30, "text": "Unknown (Invisible)"},
"68": {"bits": [7], "bias": 30, "text": "Unknown (Invisible)"},
"71": {"bits": [8], "bias": 100, "text": "Unknown (Invisible)"},
"72": {"bits": [9], "bias": 0, "text": "Unknown (Invisible)"},
"73": {"bits": [8], "bias": 0, "text": "+{0} Maximum Durability"},
"74": {"bits": [6], "bias": 30, "text": "Replenish Life +{0}"},
"75": {"bits": [7], "bias": 20, "text": "Increase Maximum Durability {0}%"},
"76": {"bits": [6], "bias": 10, "text": "Increase Maximum Life {0}%"},
"77": {"bits": [6], "bias": 10, "text": "Increase Maximum Mana {0}%"},
"78": {"bits": [7], "bias": 0, "text": "Attacker Takes Damage of {0}"},
"79": {"bits": [9], "bias": 100, "text": "{0}% Extra Gold from Monsters"},
"80": {"bits": [8], "bias": 100, "text": "{0}% Better Chance of Getting Magic Items"},
"81": {"bits": [7], "bias": 0, "text": "Knockback"},
"82": {"bits": [9], "bias": 20, "text": "Unknown (Invisible)"},
"83": {"bits": [3, 3], "bias": 0, "text": "+{1} to {0} Skill Levels"},
"84": {"bits": [3, 3], "bias": 0, "text": "+{1} to {0} Skill Levels"},
"85": {"bits": [9], "bias": 50, "text": "{0}% To Experience Gained"},
"86": {"bits": [7], "bias": 0, "text": "+{0} Life After Each Kill"},
"87": {"bits": [7], "bias": 0, "text": "Reduces Prices {0}%"},
"88": {"bits": [1], "bias": 0, "text": "Unknown (Invisible)"},
"89": {"bits": [4], "bias": 4, "text": "+{0} to Light Radius"},
"90": {"bits": [5], "bias": 0, "text": "Ambient light"},
"91": {"bits": [8], "bias": 100, "text": "Requirements {0}%"},
"92": {"bits": [7], "bias": 0, "text": "Level requirements +{0} (Invisible)"},
"93": {"bits": [7], "bias": 20, "text": "{0}% Increased Attack Speed"},
"94": {"bits": [7], "bias": 64, "text": "Unknown (Invisible)"},
"96": {"bits": [7], "bias": 20, "text": "{0}% Faster Run/Walk"},
"97": {"bits": [9, 6], "bias": 0, "text": "+{1} To {0}"},
"98": {"bits": [8, 1], "bias": 0, "text": "{1}+ to {0} (Visual effect only)"},
"99": {"bits": [7], "bias": 20, "text": "{0}% Faster Hit Recovery"},
"102": {"bits": [7], "bias": 20, "text": "{0}% Faster Block Rate"},
"105": {"bits": [7], "bias": 20, "text": "{0}% Faster Cast Rate"},
"107": {"bits": [9, 3], "bias": 0, "text": "+{1} To {0}"},
"108": {"bits": [1], "bias": 0, "text": "Rest In Peace"},
"109": {"bits": [9], "bias": 0, "text": "Unknown (Invisible)"},
"181": {"bits": [9, 5], "bias": 0, "text": "+{1} to spell {0} (char_class Only)"},
"182": {"bits": [9, 5], "bias": 0, "text": "+{1} to spell {0} (char_class Only)"},
"183": {"bits": [9, 5], "bias": 0, "text": "+{1} to spell {0} (char_class Only)"},
"184": {"bits": [9, 5], "bias": 0, "text": "+{1} to spell {0} (char_class Only)"},
"185": {"bits": [9, 5], "bias": 0, "text": "+{1} to spell {0} (char_class Only)"},
"186": {"bits": [9, 5], "bias": 0, "text": "+{1} to spell {0} (char_class Only)"},
"187": {"bits": [9, 5], "bias": 0, "text": "+{1} to spell {0} (char_class Only)"},
"110": {"bits": [8], "bias": 20, "text": "Poison Length Reduced by {0}%"},
"111": {"bits": [9], "bias": 20, "text": "Damage +{0}"},
"112": {"bits": [7], "bias": 0, "text": "Hit Causes Monsters to Flee {0}%"},
"113": {"bits": [7], "bias": 0, "text": "Hit Blinds Target +{0}"},
"114": {"bits": [6], "bias": 0, "text": "{0}% Damage Taken Goes to Mana"},
"115": {"bits": [1], "bias": 0, "text": "Ignore Target Defense"},
"116": {"bits": [7], "bias": 0, "text": "{0}% Target Defense"},
"117": {"bits": [7], "bias": 0, "text": "Prevent Monster Heal"},
"118": {"bits": [1], "bias": 0, "text": "Half Freeze Duration"},
"119": {"bits": [9], "bias": 20, "text": "{0}% Bonus to Attack Rating"},
"120": {"bits": [7], "bias": 128, "text": "{0} to Monster Defense Per Hit"},
"121": {"bits": [9], "bias": 20, "text": "+{0}% Damage to Demons"},
"122": {"bits": [9], "bias": 20, "text": "+{0}% Damage to Undead"},
"123": {"bits": [10], "bias": 128, "text": "+{0} to Attack Rating against Demons"},
"124": {"bits": [10], "bias": 128, "text": "+{0} to Attack Rating against Undead"},
"125": {"bits": [1], "bias": 0, "text": "Throwable"},
"126": {"bits": [3, 3], "bias": 0, "text": "+{0} to Fire Skills"},
"127": {"bits": [3], "bias": 0, "text": "+{0} to All Skill Levels"},
"128": {"bits": [5], "bias": 0, "text": "Attacker Takes Lightning Damage of {0}"},
"134": {"bits": [5], "bias": 0, "text": "Freezes Target +{0}"},
"135": {"bits": [7], "bias": 0, "text": "{0}% Chance of Open Wounds"},
"136": {"bits": [7], "bias": 0, "text": "{0}% Chance of Crushing Blow"},
"137": {"bits": [7], "bias": 0, "text": "+{0} Kick Damage"},
"138": {"bits": [7], "bias": 0, "text": "+{0} to Mana After Each Kill"},
"139": {"bits": [7], "bias": 0, "text": "+{0} Life after each Demon Kill"},
"140": {"bits": [7], "bias": 0, "text": "Extra Blood (Invisible)"},
"141": {"bits": [7], "bias": 0, "text": "{0}% Deadly Strike"},
"142": {"bits": [7], "bias": 0, "text": "Fire Absorb {0}%"},
"143": {"bits": [7], "bias": 0, "text": "+{0} Fire Absorb"},
"144": {"bits": [7], "bias": 0, "text": "Lightning Absorb {0}%"},
"145": {"bits": [7], "bias": 0, "text": "+{0} Lightning Absorb"},
"146": {"bits": [7], "bias": 0, "text": "Magic Absorb {0}%"},
"147": {"bits": [7], "bias": 0, "text": "+{0} Magic Absorb"},
"148": {"bits": [7], "bias": 0, "text": "Cold Absorb {0}%"},
"149": {"bits": [7], "bias": 0, "text": "+{0} Cold Absorb"},
"150": {"bits": [7], "bias": 0, "text": "Slows Target by {0}%"},
"151": {"bits": [9, 5], "bias": 0, "text": "Level +{1} {0} When Equipped"},
"152": {"bits": [1], "bias": 0, "text": "Indestructible"},
"153": {"bits": [1], "bias": 0, "text": "Cannot Be Frozen"},
"154": {"bits": [7], "bias": 20, "text": "{0}% Slower Stamina Drain"},
"155": {"bits": [10, 7], "bias": 0, "text": "{0}% Chance to Reanimate Target"},
"156": {"bits": [7], "bias": 0, "text": "Piercing Attack"},
"157": {"bits": [7], "bias": 0, "text": "Fires Magic Arrows"},
"158": {"bits": [7], "bias": 0, "text": "Fires Explosive Arrows or Bolts"},
"159": {"bits": [6], "bias": 0, "text": "+{0} to Minimum Throw Damage"},
"160": {"bits": [7], "bias": 0, "text": "+{0} to Maximum Throw Damage"},
"179": {"bits": [3], "bias": 0, "text": "+{0} to Druid Skill Levels"},
"180": {"bits": [3], "bias": 0, "text": "+{0} to Assassin Skill Levels"},
"188": {"bits": [3, 13, 3], "bias": 0, "text": "+{2} to {0} Skills ({1} only)"},
"189": {"bits": [10, 9], "bias": 0, "text": "+{0} to {1} Skills (char_class Only)"},
"190": {"bits": [10, 9], "bias": 0, "text": "+{0} to {1} Skills (char_class Only)"},
"191": {"bits": [10, 9], "bias": 0, "text": "+{0} to {1} Skills (char_class Only)"},
"192": {"bits": [10, 9], "bias": 0, "text": "+{0} to {1} Skills (char_class Only)"},
"193": {"bits": [10, 9], "bias": 0, "text": "+{0} to {1} Skills (char_class Only)"},
"194": {"bits": [4], "bias": 0, "text": "Adds {0} extra sockets to the item"},
"195": {"bits": [6, 10, 7], "bias": 0, "text": "{2}% Chance to Cast Level {0} {1} When you die"},
"196": {"bits": [6, 10, 7], "bias": 0, "text": "{2}% Chance to Cast Level {0} {1} When you die"},
"197": {"bits": [6, 10, 7], "bias": 0, "text": "{2}% Chance to Cast Level {0} {1} When you die"},
"198": {"bits": [6, 10, 7], "bias": 0, "text": "{2}% Chance to Cast Level {0} {1} On Striking"},
"199": {"bits": [6, 10, 7], "bias": 0, "text": "{2}% Chance to Cast Level {0} {1} On Striking"},
"200": {"bits": [6, 10, 7], "bias": 0, "text": "{2}% Chance to Cast Level {0} {1} On Striking"},
"201": {"bits": [6, 10, 7], "bias": 0, "text": "{2}% Chance to Cast Level {0} {1} When Struck"},
"202": {"bits": [6, 10, 7], "bias": 0, "text": "{2}% Chance to Cast Level {0} {1} When Struck"},
"203": {"bits": [6, 10, 7], "bias": 0, "text": "{2}% Chance to Cast Level {0} {1} When Struck"},
"204": {"bits": [6, 10, 8, 8], "bias": 0, "text": "Level {0} {1} ({2}/{3} Charges)"},
"205": {"bits": [6, 10, 8, 8], "bias": 0, "text": "Level {0} {1} ({2}/{3} Charges)"},
"206": {"bits": [6, 10, 8, 8], "bias": 0, "text": "Level {0} {1} ({2}/{3} Charges)"},
"207": {"bits": [6, 10, 8, 8], "bias": 0, "text": "Level {0} {1} ({2}/{3} Charges)"},
"208": {"bits": [6, 10, 8, 8], "bias": 0, "text": "Level {0} {1} ({2}/{3} Charges)"},
"209": {"bits": [6, 10, 8, 8], "bias": 0, "text": "Level {0} {1} ({2}/{3} Charges)"},
"210": {"bits": [6, 10, 8, 8], "bias": 0, "text": "Level {0} {1} ({2}/{3} Charges)"},
"211": {"bits": [6, 10, 8, 8], "bias": 0, "text": "Level {0} {1} ({2}/{3} Charges)"},
"212": {"bits": [6, 10, 8, 8], "bias": 0, "text": "Level {0} {1} ({2}/{3} Charges)"},
"213": {"bits": [6, 10, 8, 8], "bias": 0, "text": "Level {0} {1} ({2}/{3} Charges)"},
"214": {"bits": [6], "bias": 0, "text": "+{0} to Defense (Based on Character Level)"},
"215": {"bits": [6], "bias": 0, "text": "{0}% Enhanced Defense (Based on Character Level)"},
"216": {"bits": [6], "bias": 0, "text": "+{0} to Life (Based on Character Level)"},
"217": {"bits": [6], "bias": 0, "text": "+{0} to Mana (Based on Character Level)"},
"218": {"bits": [6], "bias": 0, "text": "+{0} to Maximum Damage (Based on Character Level)"},
"219": {"bits": [6], "bias": 0, "text": "{0}% Enhanced Maximum Damage (Based on Character Level)"},
"220": {"bits": [6], "bias": 0, "text": "+{0} to Strength (Based on Character Level)"},
"221": {"bits": [6], "bias": 0, "text": "+{0} to Dexterity (Based on Character Level)"},
"222": {"bits": [6], "bias": 0, "text": "+{0} to Energy (Based on Character Level)"},
"223": {"bits": [6], "bias": 0, "text": "+{0} to Vitality (Based on Character Level)"},
"224": {"bits": [6], "bias": 0, "text": "+{0} to Attack Rating (Based on Character Level)"},
"225": {"bits": [6], "bias": 0, "text": "{0}% Bonus to Attack Rating (Based on Character Level)"},
"226": {"bits": [6], "bias": 0, "text": "+{0} Cold Damage (Based on Character Level)"},
"227": {"bits": [6], "bias": 0, "text": "+{0} Fire Damage (Based on Character Level)"},
"228": {"bits": [6], "bias": 0, "text": "+{0} Lightning Damage (Based on Character Level)"},
"229": {"bits": [6], "bias": 0, "text": "+{0} Poison Damage (Based on Character Level)"},
"230": {"bits": [6], "bias": 0, "text": "Cold Resist +{0}% (Based on Character Level)"},
"231": {"bits": [6], "bias": 0, "text": "Fire Resist +{0}% (Based on Character Level)"},
"232": {"bits": [6], "bias": 0, "text": "Lightning Resist +{0}% (Based on Character Level)"},
"233": {"bits": [6], "bias": 0, "text": "Poison Resist +{0}% (Based on Character Level)"},
"234": {"bits": [6], "bias": 0, "text": "+{0} Cold Absorb (Based on Character Level)"},
"235": {"bits": [6], "bias": 0, "text": "+{0} Fire Absorb (Based on Character Level)"},
"236": {"bits": [6], "bias": 0, "text": "+{0} Lightning Absorb (Based on Character Level)"},
"237": {"bits": [6], "bias": 0, "text": "{0} Poison Absorb (Based on Character Level)"},
"238": {"bits": [5], "bias": 0, "text": "Attacker Takes Damage of {0} (Based on Character Level)"},
"239": {"bits": [6], "bias": 0, "text": "{0}% Extra Gold from Monsters (Based on Character Level)"},
"240": {"bits": [6], "bias": 0, "text": "{0}% Better Chance of Getting Magic Items (Based on Character Level)"},
"241": {"bits": [6], "bias": 0, "text": "Heal Stamina Plus {0}% (Based on Character Level)"},
"242": {"bits": [6], "bias": 0, "text": "+{0} Maxmium Stamina (Based on Character Level)"},
"243": {"bits": [6], "bias": 0, "text": "{0}% Damage to Demons (Based on Character Level)"},
"244": {"bits": [6], "bias": 0, "text": "{0}% Damage to Undead (Based on Character Level)"},
"245": {"bits": [6], "bias": 0, "text": "+{0} to Attack Rating against Demons (Based on Character Level)"},
"246": {"bits": [6], "bias": 0, "text": "+{0} to Attack Rating against Undead (Based on Character Level)"},
"247": {"bits": [6], "bias": 0, "text": "{0}% Chance of Crushing Blow (Based on Character Level)"},
"248": {"bits": [6], "bias": 0, "text": "{0}% Chance of Open Wounds (Based on Character Level)"},
"249": {"bits": [6], "bias": 0, "text": "+{0} Kick Damage (Based on Character Level)"},
"250": {"bits": [6], "bias": 0, "text": "{0}% to Deadly Strike (Based on Character Level)"},
"252": {"bits": [6], "bias": 0, "text": "Repairs 1 Durability in {0} Seconds"},
"253": {"bits": [6], "bias": 0, "text": "Replenishes Quantity"},
"254": {"bits": [8], "bias": 0, "text": "Increased Stack Size"},
"305": {"bits": [8], "bias": 50, "text": "{0} Pierce Cold"},
"306": {"bits": [8], "bias": 50, "text": "{0} Pierce Fire"},
"307": {"bits": [8], "bias": 50, "text": "{0} Pierce Lightning"},
"308": {"bits": [8], "bias": 50, "text": "{0} Pierce Poision"},
"324": {"bits": [6], "bias": 0, "text": "Unknown (Invisible)"},
"329": {"bits": [9], "bias": 50, "text": "{0}% To Fire Skill Damage"},
"330": {"bits": [9], "bias": 50, "text": "{0}% To Lightning Skill Damage"},
"331": {"bits": [9], "bias": 50, "text": "{0}% To Cold Skill Damage"},
"332": {"bits": [9], "bias": 50, "text": "{0}% To Poison Skill Damage"},
"333": {"bits": [8], "bias": 0, "text": "-{0}% To Enemy Fire Resistance"},
"334": {"bits": [8], "bias": 0, "text": "-{0}% To Enemy Lightning Resistance"},
"335": {"bits": [8], "bias": 0, "text": "-{0}% To Enemy Cold Resistance"},
"336": {"bits": [8], "bias": 0, "text": "-{0}% To Enemy Poison Resistance"},
"356": {"bits": [2], "bias": 0, "text": "Quest Item Difficulty +{0} (Invisible)"}
}

View File

@@ -16,13 +16,14 @@
# Mercator. If not, see <https://www.gnu.org/licenses/>. # Mercator. If not, see <https://www.gnu.org/licenses/>.
import json import json
import os import os
import re
from bitarray import bitarray from bitarray import bitarray
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum from enum import Enum
_data_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data") _data_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
_basetype_map = None _basetype_map = None
_affix_map = None _stats_map = None
class Quality(Enum): class Quality(Enum):
@@ -50,22 +51,19 @@ class LowQualityType(Enum):
@dataclass @dataclass
class Affix: class Stat:
name_id: int id: int | None = None # TODO: These 3 should probably not be optional
stat_id: int | None = None # TODO: These 3 should probably not be optional values: list[int] | None = None
stat_values: list[int] | None = None parameter: int | None = None
stat_text: str | None = None text: str | None = None
def print(self, indent=5): def print(self, indent=5):
# TODO: name lookup subst_text = self.text
if self.stat_text: for val in self.values:
subst_text = self.stat_text subst_text = subst_text.replace("#", str(val), 1)
for i, val in enumerate(self.stat_values): if self.parameter:
replace = "{" + str(i) + "}" subst_text = re.sub("\[[^\]]*\]", str(self.parameter), subst_text, 1)
subst_text = subst_text.replace(replace, str(val)) print(" " * indent, subst_text)
else:
subst_text = "<No text>"
print(" " * indent, f"{hex(self.name_id)}: {subst_text}")
def txtbits(bits: bitarray) -> str: def txtbits(bits: bitarray) -> str:
@@ -93,8 +91,8 @@ class Item:
graphic: int | None = None graphic: int | None = None
inherent: int | None = None inherent: int | None = None
low_quality: LowQualityType | None = None low_quality: LowQualityType | None = None
prefixes: list[Affix] | None = None prefixes: list[int] | None = None
suffixes: list[Affix] | None = None suffixes: list[int] | None = None
set_id: int | None = None set_id: int | None = None
unique_id: int | None = None unique_id: int | None = None
nameword1: int | None = None nameword1: int | None = None
@@ -106,6 +104,7 @@ class Item:
max_durability: int | None = None max_durability: int | None = None
sockets: int | None = None sockets: int | None = None
quantity: int | None = None quantity: int | None = None
stats: list[Stat] | None = None
def print(self, indent=5, with_raw=False): def print(self, indent=5, with_raw=False):
properties = [] properties = []
@@ -129,13 +128,9 @@ class Item:
if self.quality: if self.quality:
print(" " * indent, self.quality) print(" " * indent, self.quality)
if self.prefixes: if self.prefixes:
print(" " * indent, "Prefixes:") print(" " * indent, "Prefixes:", self.prefixes)
for prefix in self.prefixes:
prefix.print(indent + 4)
if self.suffixes: if self.suffixes:
print(" " * indent, "Suffixes:") print(" " * indent, "Suffixes:", self.suffixes)
for suffix in self.suffixes:
suffix.print(indent + 4)
if self.set_id: if self.set_id:
print(" " * indent, f"Set Id: {self.set_id}") # TODO: name lookup print(" " * indent, f"Set Id: {self.set_id}") # TODO: name lookup
if self.unique_id: if self.unique_id:
@@ -155,6 +150,10 @@ class Item:
print(" " * indent, f"Num Sockets: {self.sockets}") print(" " * indent, f"Num Sockets: {self.sockets}")
if self.quantity: if self.quantity:
print(" " * indent, f"Quantity: {self.quantity}") print(" " * indent, f"Quantity: {self.quantity}")
if self.stats:
print(" " * indent, "Stats:")
for stat in self.stats:
stat.print(indent + 4)
if with_raw: if with_raw:
print(" " * indent, "Raw Item Data:") print(" " * indent, "Raw Item Data:")
bits = bitarray(endian="little") bits = bitarray(endian="little")
@@ -172,9 +171,9 @@ def lookup_basetype(code: str) -> dict:
return _basetype_map[code] return _basetype_map[code]
def lookup_affix(code: int) -> dict: def lookup_stat(id: int) -> dict:
global _affix_map global _stats_map
if _affix_map is None: if _stats_map is None:
with open(os.path.join(_data_path, "affixes.json")) as f: with open(os.path.join(_data_path, "stats.json")) as f:
_affix_map = json.load(f) _stats_map = json.load(f)
return _affix_map[str(code)] return _stats_map[str(id)]

View File

@@ -19,12 +19,12 @@ from bitarray import bitarray
from bitarray.util import ba2int from bitarray.util import ba2int
from d2warehouse.stash import Stash, StashTab from d2warehouse.stash import Stash, StashTab
from d2warehouse.item import ( from d2warehouse.item import (
Affix,
Item, Item,
LowQualityType, LowQualityType,
Quality, Quality,
lookup_affix, Stat,
lookup_basetype, lookup_basetype,
lookup_stat,
) )
import d2warehouse.huffman as huffman import d2warehouse.huffman as huffman
@@ -134,7 +134,6 @@ def parse_item(data: bytes) -> tuple[bytes, Item]:
raise UnsupportedItemError("Ear items are not supported") raise UnsupportedItemError("Ear items are not supported")
simple_byte_sz = int((sockets_end + 7) / 8) simple_byte_sz = int((sockets_end + 7) / 8)
print("simple size", simple_byte_sz)
item = Item( item = Item(
data[:simple_byte_sz], data[:simple_byte_sz],
is_identified, is_identified,
@@ -185,10 +184,9 @@ def parse_item(data: bytes) -> tuple[bytes, Item]:
enchantments_end += itembase_end enchantments_end += itembase_end
extended_byte_size = int((enchantments_end + 7) / 8) extended_byte_size = int((enchantments_end + 7) / 8)
print("extended size", extended_byte_size)
item.raw_data = data[:] item.raw_data = data[:extended_byte_size]
return b"", item # TODO: properly return remaining data return data[extended_byte_size:], item
def parse_item_graphic(bits: bitarray) -> tuple[int | None, int]: def parse_item_graphic(bits: bitarray) -> tuple[int | None, int]:
@@ -242,8 +240,8 @@ def parse_high_quality_data(bits: bitarray, item: Item) -> tuple[Item, int]:
def parse_magic_data(bits: bitarray, item: Item) -> tuple[Item, int]: def parse_magic_data(bits: bitarray, item: Item) -> tuple[Item, int]:
item.prefixes = [Affix(name_id=ba2int(bits[0:11]))] item.prefixes = [ba2int(bits[0:11])]
item.suffixes = [Affix(name_id=ba2int(bits[11:22]))] item.suffixes = [ba2int(bits[11:22])]
return item, 22 return item, 22
@@ -272,11 +270,11 @@ def parse_unique_data(bits: bitarray, item: Item) -> tuple[Item, int]:
return item, 12 return item, 12
def parse_affix(bits: bitarray) -> tuple[Affix | None, int]: def parse_affix(bits: bitarray) -> tuple[int | None, int]:
if not bits[0]: if not bits[0]:
return None, 1 return None, 1
else: else:
return Affix(name_id=ba2int(bits[1:12])), 12 return ba2int(bits[1:12]), 12
def parse_runeword(bits: bitarray) -> tuple[int, int]: def parse_runeword(bits: bitarray) -> tuple[int, int]:
@@ -302,10 +300,7 @@ def parse_basetype_data(bits: bitarray, item: Item) -> tuple[Item, int]:
if cls == "tome": if cls == "tome":
ptr += 5 # unknown field ptr += 5 # unknown field
# Unknown bit here, supposedly 96 bits of realm data?? follow if set # Unknown bit here
if bits[ptr : ptr + 1]:
print("Unknown bit set, realm data follows?")
# ptr += 96 -- doesnt seem right
ptr += 1 ptr += 1
if cls == "armor": if cls == "armor":
@@ -354,58 +349,61 @@ def parse_enchantments(bits: bitarray, item: Item) -> tuple[Item, int]:
else: else:
num_lists = 1 num_lists = 1
# TODO: One extra list for runewords?
if num_lists > 0:
item.stats = []
while num_lists > 0: while num_lists > 0:
affixes, ptr = parse_enchantment_list(bits[ptr:]) stats, sz = parse_enchantment_list(bits[ptr:])
ptr += sz
num_lists -= 1 num_lists -= 1
item.stats.extend(stats)
for i, affix in enumerate(affixes):
# TODO: this works for magic, rare & crafted. But set & unique items do not have
# names specified in the save data, but do instead need to be read from data files
# to know if the mod is prefix or suffix. For now we add them all to prefixes.
# TODO: Also note, that sets have lists of mods they get from the set. Which should go
# seperately (assuming they are in the data files when not equipped...?)
if item.quality in [Quality.UNIQUE, Quality.SET]:
item.prefixes.append(Affix(name_id=0))
mod = item.prefixes[-1]
else:
# TODO: Here we assume that they are the same ordering as previously
mod = (
item.prefixes[i]
if i < len(item.prefixes)
else item.suffixes[i - len(item.prefixes)]
)
mod.stat_id = affix.stat_id
mod.stat_values = affix.stat_values
mod.stat_text = affix.stat_text
return item, ptr return item, ptr
def parse_enchantment_list(bits: bitarray) -> tuple[list[Affix], int]: def parse_enchantment_list(bits: bitarray) -> tuple[list[Stat], int]:
ptr = 0 # what is all this data? ptr = 0
affixes = [] stats = []
while True: while True:
id = ba2int(bits[ptr : ptr + 9]) id = ba2int(bits[ptr : ptr + 9])
ptr += 9 ptr += 9
if id == 0x1FF: if id == 0x1FF:
break break
print("id", id) print("stat id", id)
affix = lookup_affix(id) if id == 107:
print(id, 107, "remainind data:")
print(bits)
stat = lookup_stat(id)
values = [] values = []
for b in affix["bits"]: param = None
print("bits", b) if stat["save_param_bits"]:
param = ba2int(bits[ptr : ptr + stat["save_param_bits"]])
ptr += stat["save_param_bits"]
for b in stat["save_bits"]:
values.append(ba2int(bits[ptr : ptr + b])) values.append(ba2int(bits[ptr : ptr + b]))
ptr += b ptr += b
values = [v - affix["bias"] for v in values] values = [v - stat["save_add"] for v in values]
text = affix["text"] text = stat["text"]
affixes.append(Affix(name_id=0, stat_id=id, stat_values=values, stat_text=text)) stat = Stat(
id=id,
values=values,
parameter=param,
text=text,
)
return affixes, ptr print("Stat:")
stat.print()
stats.append(stat)
return stats, ptr
def parse_items(data: bytes) -> list[Item]: def parse_items(data: bytes) -> list[Item]: