# Loadout Manager Redesign - Cost Tracking Focus ## Current Problems 1. **Three overlapping armor systems:** - Legacy: `armor_decay_pec` + `protection_stab/cut/etc` - EquippedArmor: `equipped_armor` with ArmorPiece/ArmorSet - New system: `current_armor_*` fields 2. **Serialization nightmare:** ProtectionProfile, ArmorPiece, complex nested objects 3. **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 ```python @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 1. **No more armor piece management** - Just select armor set, get decay value 2. **No more plate management** - Include plate decay in armor_cost_per_hit 3. **Pre-calculated costs** - All conversions happen on save 4. **Simple JSON** - Only cost values + display names ## Session Integration ### Flow: Start Session ```python # 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 ```python # 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 1. **Keep existing files** - Don't break old loadouts 2. **Add version field** - `version: 2` for new format 3. **On load:** If old format, extract decay values and convert 4. **On save:** Always write new simple format ```python @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 1. `ui/loadout_manager.py` - Complete rewrite of LoadoutConfig 2. `ui/loadout_selection_dialog.py` - Simplify to extract costs only 3. `ui/main_window.py` - Clean cost tracking integration 4. `ui/hud_overlay.py` - Already accepts costs, just verify display ## Benefits 1. **Simple serialization** - No more Decimal/ProtectionProfile issues 2. **Clear data flow** - Costs calculated once, used everywhere 3. **Easy debugging** - Look at JSON, see exactly what costs are 4. **Fast loading** - No complex object reconstruction 5. **Reliable** - Less code = fewer bugs