8.4 KiB
8.4 KiB
Loadout Manager Redesign - Cost Tracking Focus
Current Problems
-
Three overlapping armor systems:
- Legacy:
armor_decay_pec+protection_stab/cut/etc - EquippedArmor:
equipped_armorwith ArmorPiece/ArmorSet - New system:
current_armor_*fields
- Legacy:
-
Serialization nightmare: ProtectionProfile, ArmorPiece, complex nested objects
-
Session integration broken: Cost values don't flow from Loadout → Session
Core Insight
For cost tracking, we only need three numbers:
weapon_cost_per_shot(PED)armor_cost_per_hit(PED)healing_cost_per_heal(PED)
Everything else is display metadata.
Proposed Simple Structure
@dataclass
class LoadoutConfig:
"""Simple loadout focused on cost tracking."""
name: str
# === COST DATA (Required for tracking) ===
# Weapon
weapon_cost_per_shot: Decimal # Pre-calculated total (decay + ammo)
# Armor
armor_cost_per_hit: Decimal # Pre-calculated decay per hit
# Healing
healing_cost_per_heal: Decimal # Pre-calculated decay per heal
# === DISPLAY METADATA (Optional, for UI only) ===
weapon_name: str = "Unknown"
weapon_damage: Decimal = Decimal("0")
weapon_decay_pec: Decimal = Decimal("0") # Raw value for reference
weapon_ammo_pec: Decimal = Decimal("0") # Raw value for reference
armor_name: str = "None"
armor_decay_pec: Decimal = Decimal("0") # Raw value for reference
healing_name: str = "None"
healing_decay_pec: Decimal = Decimal("0") # Raw value for reference
# === UI STATE (Not serialized) ===
# These are populated when loading from API/database
# but not saved to JSON - they're derived data
weapon_api_id: Optional[int] = None
armor_api_id: Optional[int] = None
healing_api_id: Optional[int] = None
def to_dict(self) -> dict:
"""Simple serialization - just the basics."""
return {
'name': self.name,
'weapon_cost_per_shot': str(self.weapon_cost_per_shot),
'armor_cost_per_hit': str(self.armor_cost_per_hit),
'healing_cost_per_heal': str(self.healing_cost_per_heal),
'weapon_name': self.weapon_name,
'weapon_damage': str(self.weapon_damage),
'weapon_decay_pec': str(self.weapon_decay_pec),
'weapon_ammo_pec': str(self.weapon_ammo_pec),
'armor_name': self.armor_name,
'armor_decay_pec': str(self.armor_decay_pec),
'healing_name': self.healing_name,
'healing_decay_pec': str(self.healing_decay_pec),
}
@classmethod
def from_dict(cls, data: dict) -> "LoadoutConfig":
"""Simple deserialization with safe defaults."""
def get_decimal(key, default="0"):
try:
return Decimal(str(data.get(key, default)))
except:
return Decimal(default)
return cls(
name=data.get('name', 'Unnamed'),
weapon_cost_per_shot=get_decimal('weapon_cost_per_shot'),
armor_cost_per_hit=get_decimal('armor_cost_per_hit'),
healing_cost_per_heal=get_decimal('healing_cost_per_heal'),
weapon_name=data.get('weapon_name', 'Unknown'),
weapon_damage=get_decimal('weapon_damage'),
weapon_decay_pec=get_decimal('weapon_decay_pec'),
weapon_ammo_pec=get_decimal('weapon_ammo_pec'),
armor_name=data.get('armor_name', 'None'),
armor_decay_pec=get_decimal('armor_decay_pec'),
healing_name=data.get('healing_name', 'None'),
healing_decay_pec=get_decimal('healing_decay_pec'),
)
UI Simplification
Loadout Manager Dialog
Single purpose: Configure gear and calculate costs
Layout:
┌─────────────────────────────────────────┐
│ Loadout: [Name ] │
├─────────────────────────────────────────┤
│ ⚔️ WEAPON │
│ [Select Weapon...] ArMatrix BP-25 │
│ Damage: 85 Decay: 0.688 PEC │
│ Ammo: 848 Cost/Shot: 0.091 PED │
├─────────────────────────────────────────┤
│ 🛡️ ARMOR │
│ [Select Armor...] Ghost │
│ Decay/Hit: 0.015 PEC = 0.00015 PED │
├─────────────────────────────────────────┤
│ 💚 HEALING │
│ [Select Healing...] Regen Chip 4 │
│ Heal: 45 HP Cost/Heal: 0.028 PED │
├─────────────────────────────────────────┤
│ 💰 SESSION COST SUMMARY │
│ Cost/Shot: 0.091 PED │
│ Cost/Hit: 0.00015 PED │
│ Cost/Heal: 0.028 PED │
├─────────────────────────────────────────┤
│ [Save] [Cancel] │
└─────────────────────────────────────────┘
Key Changes
- No more armor piece management - Just select armor set, get decay value
- No more plate management - Include plate decay in armor_cost_per_hit
- Pre-calculated costs - All conversions happen on save
- Simple JSON - Only cost values + display names
Session Integration
Flow: Start Session
# In LoadoutSelectionDialog
loadout_info = {
'id': 0, # 0 = file-based
'name': 'ArMatrix Ghost Hunt',
'source': 'file',
'costs': {
'cost_per_shot': Decimal('0.091'),
'cost_per_hit': Decimal('0.00015'),
'cost_per_heal': Decimal('0.028'),
},
'display': {
'weapon_name': 'ArMatrix BP-25 (L)',
'armor_name': 'Ghost',
'healing_name': 'Regeneration Chip 4 (L)',
}
}
# In MainWindow._on_loadout_selected_for_session
self._session_costs = loadout_info['costs']
self._session_display = loadout_info['display']
# In MainWindow.start_session
self.hud.start_session(
weapon=self._session_display['weapon_name'],
armor=self._session_display['armor_name'],
healing=self._session_display['healing_name'],
cost_per_shot=self._session_costs['cost_per_shot'],
cost_per_hit=self._session_costs['cost_per_hit'],
cost_per_heal=self._session_costs['cost_per_heal'],
)
Cost Tracking Logic
# In MainWindow (log event handlers)
def on_shot_fired():
"""Called when weapon is fired."""
if self._session_costs:
self.hud.update_weapon_cost(self._session_costs['cost_per_shot'])
def on_damage_taken(amount):
"""Called when player takes damage (armor hit)."""
if self._session_costs:
self.hud.update_armor_cost(self._session_costs['cost_per_hit'])
def on_heal_used():
"""Called when healing tool is used."""
if self._session_costs:
self.hud.update_healing_cost(self._session_costs['cost_per_heal'])
Migration Strategy
- Keep existing files - Don't break old loadouts
- Add version field -
version: 2for new format - On load: If old format, extract decay values and convert
- On save: Always write new simple format
@classmethod
def from_dict(cls, data: dict) -> "LoadoutConfig":
version = data.get('version', 1)
if version == 1:
# Legacy format - extract cost data
return cls._from_legacy(data)
else:
# New simple format
return cls._from_v2(data)
Files to Modify
ui/loadout_manager.py- Complete rewrite of LoadoutConfigui/loadout_selection_dialog.py- Simplify to extract costs onlyui/main_window.py- Clean cost tracking integrationui/hud_overlay.py- Already accepts costs, just verify display
Benefits
- Simple serialization - No more Decimal/ProtectionProfile issues
- Clear data flow - Costs calculated once, used everywhere
- Easy debugging - Look at JSON, see exactly what costs are
- Fast loading - No complex object reconstruction
- Reliable - Less code = fewer bugs