Debug and inventory

This commit is contained in:
erik 2025-06-19 17:46:19 +00:00
parent 1febf6e918
commit 80a0a16bab
15 changed files with 2764 additions and 341 deletions

View file

@ -254,6 +254,18 @@ def translate_item_type(item_type_id: int) -> str:
item_types = ENUM_MAPPINGS.get('item_types', {})
return item_types.get(item_type_id, f"Unknown_ItemType_{item_type_id}")
def derive_item_type_from_object_class(object_class: int, item_data: dict = None) -> str:
"""Derive ItemType from ObjectClass using the object_classes enum."""
# Use the object_classes enum directly for accurate classifications
object_classes = ENUM_MAPPINGS.get('object_classes', {})
item_type = object_classes.get(object_class)
if item_type:
return item_type
else:
# Fallback to "Misc" if ObjectClass not found in enum
return "Misc"
def translate_object_class(object_class_id: int, item_data: dict = None) -> str:
"""Translate object class ID to human-readable name with context-aware detection."""
# Use the extracted ObjectClass enum first
@ -573,6 +585,17 @@ def translate_equipment_slot(wielded_location: int) -> str:
}
return name_mapping.get(slot_name, slot_name)
# Handle common equipment slots that may be missing from enum database
common_slots = {
30: "Shirt", # ChestWear + AbdomenWear + UpperArmWear + LowerArmWear for shirts
786432: "Left Ring, Right Ring", # 262144 + 524288 for rings that can go in either slot
262144: "Left Ring",
524288: "Right Ring"
}
if wielded_location in common_slots:
return common_slots[wielded_location]
# If no exact match, decode bit flags
slot_parts = []
for mask_value, slot_name in equip_mask_map.items():
@ -627,7 +650,7 @@ def translate_equipment_slot(wielded_location: int) -> str:
else:
return f"Special Slot ({wielded_location})"
return f"Unknown Slot ({wielded_location})"
return "-"
def translate_workmanship(workmanship_value: int) -> str:
"""Translate workmanship value to descriptive text."""
@ -845,10 +868,21 @@ def get_comprehensive_translations(item_data: Dict[str, Any]) -> Dict[str, Any]:
translations['material_name'] = translate_material_type(material_id)
translations['material_id'] = material_id
# Translate item type if present
# Translate item type if present (check ItemType field or IntValues[1])
item_type_id = item_data.get('ItemType')
if item_type_id is None:
# Check IntValues for ItemType (key 1)
int_values = item_data.get('IntValues', {})
if isinstance(int_values, dict):
item_type_id = int_values.get('1', int_values.get(1))
if item_type_id is not None:
translations['item_type_name'] = translate_item_type(item_type_id)
else:
# Fallback: derive ItemType from ObjectClass
object_class = item_data.get('ObjectClass')
if object_class is not None:
translations['item_type_name'] = derive_item_type_from_object_class(object_class, item_data)
# Translate object class using WeenieType enum
object_class = item_data.get('ObjectClass')
@ -880,7 +914,7 @@ def extract_item_properties(item_data: Dict[str, Any]) -> Dict[str, Any]:
# Item state
'bonded': int_values.get('33', int_values.get(33, 0)),
'attuned': int_values.get('114', int_values.get(114, 0)),
'unique': int_values.get('279', int_values.get(279, 0)) != 0,
'unique': bool(int_values.get('279', int_values.get(279, 0))),
# Stack/Container properties
'stack_size': int_values.get('12', int_values.get(12, 1)),
@ -941,11 +975,11 @@ def extract_item_properties(item_data: Dict[str, Any]) -> Dict[str, Any]:
'equip_skill': item_data.get('EquipSkill'),
},
'enhancements': {
'material': item_data.get('Material'),
'material': None, # Will be set below with proper logic
'imbue': item_data.get('Imbue'),
'tinks': item_data.get('Tinks', -1),
'workmanship': item_data.get('Workmanship', -1.0),
'item_set': int_values.get('265', int_values.get(265)) if int_values.get('265', int_values.get(265)) else None,
'item_set': None, # Will be set below with translation
# Advanced tinkering
'num_times_tinkered': int_values.get('171', int_values.get(171, -1)),
@ -997,6 +1031,32 @@ def extract_item_properties(item_data: Dict[str, Any]) -> Dict[str, Any]:
}
}
# Handle material field properly - check if already translated or needs translation
material_field = item_data.get('Material')
if material_field and isinstance(material_field, str):
# Material is already a translated string (like "Gold", "Iron", "Brass")
properties['enhancements']['material'] = material_field
else:
# Material needs translation from IntValues[131]
material_id = int_values.get('131', int_values.get(131))
if material_id:
material_name = translate_material_type(material_id)
# Only store if translation succeeded (not "Unknown_Material_*")
if not material_name.startswith('Unknown_Material_'):
properties['enhancements']['material'] = material_name
# Translate item_set ID to name for database storage
item_set_id = int_values.get('265', int_values.get(265))
if item_set_id:
dictionaries = ENUM_MAPPINGS.get('dictionaries', {})
attribute_set_info = dictionaries.get('AttributeSetInfo', {}).get('values', {})
set_name = attribute_set_info.get(str(item_set_id))
if set_name:
properties['enhancements']['item_set'] = set_name
else:
# Fallback to just store the ID as string
properties['enhancements']['item_set'] = str(item_set_id)
# Get comprehensive translations
translations = get_comprehensive_translations(item_data)
if translations:
@ -1113,6 +1173,14 @@ async def process_inventory(inventory: InventoryItem):
# Simple INSERT since we cleared the table first
basic = properties['basic']
# Debug logging for problematic items
if item_id in [-2133380247, -2144880287, -2136150336]:
logger.info(f"Debug item {item_id}: basic={basic}")
logger.info(f"Debug item {item_id}: name='{basic['name']}' type={type(basic['name'])}")
logger.info(f"Debug item {item_id}: current_wielded_location={basic['current_wielded_location']} type={type(basic['current_wielded_location'])}")
logger.info(f"Debug item {item_id}: enhancements={properties['enhancements']}")
item_stmt = sa.insert(Item).values(
character_name=inventory.character_name,
item_id=item_id,
@ -1316,9 +1384,12 @@ async def get_character_inventory(
# Add translated properties to the item
processed_item['translated_properties'] = properties
# Add material translation
# Add material name - use material directly if it's already a string
if processed_item.get('material'):
processed_item['material_name'] = translate_material_type(processed_item['material'])
if isinstance(processed_item['material'], str):
processed_item['material_name'] = processed_item['material']
else:
processed_item['material_name'] = translate_material_type(processed_item['material'])
# Add object class translation
if processed_item.get('object_class'):
@ -1430,6 +1501,8 @@ async def get_character_inventory(
processed_item['material_name'] = trans['material_name']
if trans.get('material_id'):
processed_item['material_id'] = trans['material_id']
if trans.get('item_type_name'):
processed_item['item_type_name'] = trans['item_type_name']
# Continue with other enhancements
if 'enhancements' in translated_props:
@ -2092,11 +2165,15 @@ async def search_items(
if item.get('material') or properties.get('translations', {}).get('material_name'):
material_name = None
if item.get('material'):
material_name = translate_material_type(item['material'])
# Check if material is already a string or needs translation
if isinstance(item['material'], str):
material_name = item['material']
else:
material_name = translate_material_type(item['material'])
elif properties.get('translations', {}).get('material_name'):
material_name = properties['translations']['material_name']
if material_name:
if material_name and not material_name.startswith('Unknown_Material_'):
item['material_name'] = material_name
# Apply material prefix to item name
original_name = item['name']
@ -2108,6 +2185,13 @@ async def search_items(
if item.get('object_class'):
item['object_class_name'] = translate_object_class(item['object_class'], original_json)
# Add item type translation
if properties.get('translations', {}).get('item_type_name'):
item['item_type_name'] = properties['translations']['item_type_name']
elif item.get('object_class'):
# Fallback: derive ItemType from object_class when translation is missing
item['item_type_name'] = derive_item_type_from_object_class(item['object_class'], {'Name': item.get('name', '')})
# Add spell information
if 'spells' in properties:
spell_info = properties['spells']
@ -2168,11 +2252,15 @@ async def search_items(
if slot_name and slot_name not in slot_names:
slot_names.append(slot_name)
item['slot_name'] = ', '.join(slot_names) if slot_names else f"Slot_{equippable_slots}"
# Debug logging for slot issues
if not slot_names and equippable_slots in [30, 786432]:
logger.warning(f"No slot names found for item '{item['name']}' with equippable_slots={equippable_slots}, slot_options={slot_options}")
item['slot_name'] = ', '.join(slot_names) if slot_names else "-"
else:
item['slot_name'] = "Unknown"
item['slot_name'] = "-"
else:
item['slot_name'] = "Unknown"
item['slot_name'] = "-"
# Use gear totals as display ratings when individual ratings don't exist
# For armor/clothing, ratings are often stored as gear totals (370, 372, 374)
@ -3487,33 +3575,6 @@ def decode_coverage_to_slots(coverage_mask, object_class=None, item_name=''):
return list(set(slots)) # Remove duplicates
def translate_equipment_slot(slot_option):
"""
Translate equipment slot option to human-readable slot name.
This handles the slot options returned by get_sophisticated_slot_options.
"""
# Equipment slot mappings based on EquipMask values
slot_mappings = {
1: "Head",
2: "Chest",
4: "Abdomen",
8: "Upper Arms",
16: "Lower Arms",
32: "Hands",
64: "Upper Legs",
128: "Lower Legs",
256: "Feet",
512: "Shield",
1024: "Neck",
2048: "Left Wrist",
4096: "Right Wrist",
8192: "Left Ring",
16384: "Right Ring",
32768: "Trinket"
}
return slot_mappings.get(slot_option, f"Slot_{slot_option}")
def categorize_items_by_set(items):
"""Categorize items by equipment set for efficient set-based optimization."""