refactor: simplified cost-focused Loadout Manager
- New LoadoutManagerSimple with clean cost-focused design - LoadoutConfig now stores only: cost_per_shot/hit/heal + display names - Legacy format support for backward compatibility - Simplified LoadoutSelectionDialog with clear cost preview - Updated MainWindow to use new simplified structure - Removed 3 overlapping armor systems, replaced with single decay value - JSON serialization is now simple and reliable Key principle: Only store what's needed for cost tracking.
This commit is contained in:
parent
83084252cc
commit
cdc9f5b825
|
|
@ -0,0 +1,667 @@
|
|||
"""
|
||||
Lemontropia Suite - Loadout Manager UI v4.0
|
||||
Simplified cost-focused loadout system.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from decimal import Decimal
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QFormLayout,
|
||||
QLineEdit, QLabel, QPushButton, QGroupBox,
|
||||
QMessageBox, QListWidget, QListWidgetItem,
|
||||
QSplitter, QWidget, QFrame, QGridLayout,
|
||||
)
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
|
||||
from core.nexus_full_api import get_nexus_api, NexusWeapon
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Simple Cost-Focused Loadout Config
|
||||
# ============================================================================
|
||||
|
||||
@dataclass
|
||||
class LoadoutConfig:
|
||||
"""Simple loadout configuration focused on cost tracking.
|
||||
|
||||
Core principle: Only store what's needed for cost calculations.
|
||||
Everything else is display metadata.
|
||||
"""
|
||||
# Identity
|
||||
name: str = "Unnamed"
|
||||
version: int = 2 # Version 2 = simplified format
|
||||
|
||||
# === COST DATA (Required for tracking) ===
|
||||
# All values in PED (not PEC)
|
||||
weapon_cost_per_shot: Decimal = Decimal("0")
|
||||
armor_cost_per_hit: Decimal = Decimal("0")
|
||||
healing_cost_per_heal: Decimal = Decimal("0")
|
||||
|
||||
# === DISPLAY METADATA (For UI only) ===
|
||||
weapon_name: str = "None"
|
||||
weapon_damage: Decimal = Decimal("0")
|
||||
weapon_decay_pec: Decimal = Decimal("0") # Raw for reference
|
||||
weapon_ammo_pec: Decimal = Decimal("0") # Raw for reference
|
||||
|
||||
armor_name: str = "None"
|
||||
armor_decay_pec: Decimal = Decimal("0") # Raw for reference
|
||||
|
||||
healing_name: str = "None"
|
||||
healing_decay_pec: Decimal = Decimal("0") # Raw for reference
|
||||
|
||||
# === API REFERENCES (For re-loading from API) ===
|
||||
weapon_api_id: Optional[int] = None
|
||||
armor_api_id: Optional[int] = None
|
||||
healing_api_id: Optional[int] = None
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Serialize to simple dictionary."""
|
||||
return {
|
||||
'name': self.name,
|
||||
'version': self.version,
|
||||
'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),
|
||||
'weapon_api_id': self.weapon_api_id,
|
||||
'armor_api_id': self.armor_api_id,
|
||||
'healing_api_id': self.healing_api_id,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: dict) -> "LoadoutConfig":
|
||||
"""Deserialize from dictionary with legacy support."""
|
||||
version = data.get('version', 1)
|
||||
|
||||
if version == 1:
|
||||
return cls._from_legacy(data)
|
||||
else:
|
||||
return cls._from_v2(data)
|
||||
|
||||
@classmethod
|
||||
def _from_v2(cls, data: dict) -> "LoadoutConfig":
|
||||
"""Parse version 2 (current) format."""
|
||||
def get_decimal(key: str, default: str = "0") -> Decimal:
|
||||
try:
|
||||
return Decimal(str(data.get(key, default)))
|
||||
except Exception:
|
||||
return Decimal(default)
|
||||
|
||||
return cls(
|
||||
name=data.get('name', 'Unnamed'),
|
||||
version=2,
|
||||
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', 'None'),
|
||||
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'),
|
||||
weapon_api_id=data.get('weapon_api_id'),
|
||||
armor_api_id=data.get('armor_api_id'),
|
||||
healing_api_id=data.get('healing_api_id'),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _from_legacy(cls, data: dict) -> "LoadoutConfig":
|
||||
"""Convert legacy format to new simple format."""
|
||||
def get_decimal(key: str, default: str = "0") -> Decimal:
|
||||
try:
|
||||
return Decimal(str(data.get(key, default)))
|
||||
except Exception:
|
||||
return Decimal(default)
|
||||
|
||||
# Calculate costs from legacy fields
|
||||
weapon_decay = get_decimal('weapon_decay_pec')
|
||||
weapon_ammo = get_decimal('weapon_ammo_pec')
|
||||
weapon_cost_per_shot = (weapon_decay / Decimal("100")) + (weapon_ammo * Decimal("0.0001"))
|
||||
|
||||
armor_decay = get_decimal('armor_decay_pec')
|
||||
armor_cost_per_hit = armor_decay / Decimal("100")
|
||||
|
||||
heal_decay = get_decimal('heal_cost_pec')
|
||||
healing_cost_per_heal = heal_decay / Decimal("100")
|
||||
|
||||
return cls(
|
||||
name=data.get('name', 'Unnamed'),
|
||||
version=2,
|
||||
weapon_cost_per_shot=weapon_cost_per_shot,
|
||||
armor_cost_per_hit=armor_cost_per_hit,
|
||||
healing_cost_per_heal=healing_cost_per_heal,
|
||||
weapon_name=data.get('weapon_name', data.get('weapon', 'None')),
|
||||
weapon_damage=get_decimal('weapon_damage'),
|
||||
weapon_decay_pec=weapon_decay,
|
||||
weapon_ammo_pec=weapon_ammo,
|
||||
armor_name=data.get('armor_set_name', data.get('armor_name', 'None')),
|
||||
armor_decay_pec=armor_decay,
|
||||
healing_name=data.get('heal_name', 'None'),
|
||||
healing_decay_pec=heal_decay,
|
||||
)
|
||||
|
||||
def get_summary(self) -> Dict[str, Any]:
|
||||
"""Get cost summary for display."""
|
||||
return {
|
||||
'name': self.name,
|
||||
'weapon': self.weapon_name,
|
||||
'armor': self.armor_name,
|
||||
'healing': self.healing_name,
|
||||
'cost_per_shot': self.weapon_cost_per_shot,
|
||||
'cost_per_hit': self.armor_cost_per_hit,
|
||||
'cost_per_heal': self.healing_cost_per_heal,
|
||||
}
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Simple Loadout Manager Dialog
|
||||
# ============================================================================
|
||||
|
||||
class LoadoutManagerDialog(QDialog):
|
||||
"""Simplified loadout manager focused on cost configuration."""
|
||||
|
||||
loadout_saved = pyqtSignal(LoadoutConfig)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("Loadout Manager")
|
||||
self.setMinimumSize(600, 500)
|
||||
|
||||
# State
|
||||
self.config_dir = Path.home() / ".lemontropia" / "loadouts"
|
||||
self.config_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.current_config: Optional[LoadoutConfig] = None
|
||||
|
||||
# Cached API data
|
||||
self._cached_weapons: Optional[list] = None
|
||||
self._cached_armors: Optional[list] = None
|
||||
self._cached_healing: Optional[list] = None
|
||||
|
||||
self._setup_ui()
|
||||
self._load_saved_loadouts()
|
||||
|
||||
def _setup_ui(self):
|
||||
"""Setup simplified UI."""
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setSpacing(10)
|
||||
|
||||
# === Top: Loadout Name ===
|
||||
name_layout = QHBoxLayout()
|
||||
name_layout.addWidget(QLabel("Loadout Name:"))
|
||||
self.name_edit = QLineEdit()
|
||||
self.name_edit.setPlaceholderText("e.g., ArMatrix Ghost Hunt")
|
||||
name_layout.addWidget(self.name_edit)
|
||||
layout.addLayout(name_layout)
|
||||
|
||||
# === Main Content Splitter ===
|
||||
splitter = QSplitter(Qt.Orientation.Horizontal)
|
||||
|
||||
# Left: Saved Loadouts
|
||||
left_widget = QWidget()
|
||||
left_layout = QVBoxLayout(left_widget)
|
||||
left_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
left_layout.addWidget(QLabel("Saved Loadouts:"))
|
||||
self.saved_list = QListWidget()
|
||||
self.saved_list.itemClicked.connect(self._on_loadout_selected)
|
||||
self.saved_list.itemDoubleClicked.connect(self._on_loadout_double_clicked)
|
||||
left_layout.addWidget(self.saved_list)
|
||||
|
||||
btn_layout = QHBoxLayout()
|
||||
self.new_btn = QPushButton("New")
|
||||
self.new_btn.clicked.connect(self._new_loadout)
|
||||
self.delete_btn = QPushButton("Delete")
|
||||
self.delete_btn.clicked.connect(self._delete_loadout)
|
||||
btn_layout.addWidget(self.new_btn)
|
||||
btn_layout.addWidget(self.delete_btn)
|
||||
left_layout.addLayout(btn_layout)
|
||||
|
||||
splitter.addWidget(left_widget)
|
||||
|
||||
# Right: Configuration
|
||||
right_widget = QWidget()
|
||||
right_layout = QVBoxLayout(right_widget)
|
||||
right_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# -- Weapon Section --
|
||||
weapon_group = QGroupBox("⚔️ Weapon")
|
||||
weapon_layout = QFormLayout(weapon_group)
|
||||
|
||||
self.weapon_btn = QPushButton("Select Weapon...")
|
||||
self.weapon_btn.clicked.connect(self._select_weapon)
|
||||
weapon_layout.addRow("Weapon:", self.weapon_btn)
|
||||
|
||||
self.weapon_info = QLabel("None selected")
|
||||
self.weapon_info.setStyleSheet("color: #888;")
|
||||
weapon_layout.addRow(self.weapon_info)
|
||||
|
||||
self.weapon_decay_label = QLabel("0 PEC")
|
||||
weapon_layout.addRow("Decay:", self.weapon_decay_label)
|
||||
|
||||
self.weapon_ammo_label = QLabel("0")
|
||||
weapon_layout.addRow("Ammo:", self.weapon_ammo_label)
|
||||
|
||||
self.weapon_cost_label = QLabel("0.0000 PED")
|
||||
self.weapon_cost_label.setStyleSheet("font-weight: bold; color: #7FFF7F;")
|
||||
weapon_layout.addRow("Cost/Shot:", self.weapon_cost_label)
|
||||
|
||||
right_layout.addWidget(weapon_group)
|
||||
|
||||
# -- Armor Section --
|
||||
armor_group = QGroupBox("🛡️ Armor")
|
||||
armor_layout = QFormLayout(armor_group)
|
||||
|
||||
self.armor_btn = QPushButton("Select Armor...")
|
||||
self.armor_btn.clicked.connect(self._select_armor)
|
||||
armor_layout.addRow("Armor:", self.armor_btn)
|
||||
|
||||
self.armor_info = QLabel("None selected")
|
||||
self.armor_info.setStyleSheet("color: #888;")
|
||||
armor_layout.addRow(self.armor_info)
|
||||
|
||||
self.armor_cost_label = QLabel("0.0000 PED")
|
||||
self.armor_cost_label.setStyleSheet("font-weight: bold; color: #7FFF7F;")
|
||||
armor_layout.addRow("Cost/Hit:", self.armor_cost_label)
|
||||
|
||||
right_layout.addWidget(armor_group)
|
||||
|
||||
# -- Healing Section --
|
||||
healing_group = QGroupBox("💚 Healing")
|
||||
healing_layout = QFormLayout(healing_group)
|
||||
|
||||
self.healing_btn = QPushButton("Select Healing...")
|
||||
self.healing_btn.clicked.connect(self._select_healing)
|
||||
healing_layout.addRow("Healing:", self.healing_btn)
|
||||
|
||||
self.healing_info = QLabel("None selected")
|
||||
self.healing_info.setStyleSheet("color: #888;")
|
||||
healing_layout.addRow(self.healing_info)
|
||||
|
||||
self.healing_cost_label = QLabel("0.0000 PED")
|
||||
self.healing_cost_label.setStyleSheet("font-weight: bold; color: #7FFF7F;")
|
||||
healing_layout.addRow("Cost/Heal:", self.healing_cost_label)
|
||||
|
||||
right_layout.addWidget(healing_group)
|
||||
|
||||
# -- Summary Section --
|
||||
summary_group = QGroupBox("💰 Session Cost Summary")
|
||||
summary_layout = QGridLayout(summary_group)
|
||||
|
||||
summary_layout.addWidget(QLabel("Cost per Shot:"), 0, 0)
|
||||
self.summary_shot = QLabel("0.0000 PED")
|
||||
summary_layout.addWidget(self.summary_shot, 0, 1)
|
||||
|
||||
summary_layout.addWidget(QLabel("Cost per Hit:"), 1, 0)
|
||||
self.summary_hit = QLabel("0.0000 PED")
|
||||
summary_layout.addWidget(self.summary_hit, 1, 1)
|
||||
|
||||
summary_layout.addWidget(QLabel("Cost per Heal:"), 2, 0)
|
||||
self.summary_heal = QLabel("0.0000 PED")
|
||||
summary_layout.addWidget(self.summary_heal, 2, 1)
|
||||
|
||||
summary_layout.setColumnStretch(1, 1)
|
||||
right_layout.addWidget(summary_group)
|
||||
|
||||
right_layout.addStretch()
|
||||
|
||||
splitter.addWidget(right_widget)
|
||||
splitter.setSizes([200, 400])
|
||||
|
||||
layout.addWidget(splitter)
|
||||
|
||||
# === Bottom Buttons ===
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.addStretch()
|
||||
|
||||
self.save_btn = QPushButton("💾 Save Loadout")
|
||||
self.save_btn.clicked.connect(self._save_loadout)
|
||||
self.save_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #2E7D32;
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #388E3C;
|
||||
}
|
||||
""")
|
||||
button_layout.addWidget(self.save_btn)
|
||||
|
||||
self.cancel_btn = QPushButton("Cancel")
|
||||
self.cancel_btn.clicked.connect(self.reject)
|
||||
button_layout.addWidget(self.cancel_btn)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
def _select_weapon(self):
|
||||
"""Open weapon selector dialog."""
|
||||
from ui.weapon_selector import WeaponSelectorDialog
|
||||
|
||||
dialog = WeaponSelectorDialog(self)
|
||||
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||
weapon = dialog.get_selected_weapon()
|
||||
if weapon:
|
||||
self._set_weapon(weapon)
|
||||
|
||||
def _set_weapon(self, weapon: NexusWeapon):
|
||||
"""Set weapon and calculate cost."""
|
||||
# Calculate cost per shot
|
||||
decay_pec = Decimal(str(weapon.decay))
|
||||
ammo = Decimal(str(weapon.ammo))
|
||||
cost_per_shot = (decay_pec / Decimal("100")) + (ammo * Decimal("0.0001"))
|
||||
|
||||
# Update UI
|
||||
self.weapon_btn.setText(weapon.name[:30])
|
||||
self.weapon_info.setText(f"Damage: {weapon.damage} | Range: {weapon.range}")
|
||||
self.weapon_decay_label.setText(f"{decay_pec} PEC")
|
||||
self.weapon_ammo_label.setText(f"{ammo}")
|
||||
self.weapon_cost_label.setText(f"{cost_per_shot:.4f} PED")
|
||||
|
||||
# Store for saving
|
||||
self._pending_weapon = {
|
||||
'name': weapon.name,
|
||||
'api_id': weapon.id,
|
||||
'damage': weapon.damage,
|
||||
'decay_pec': decay_pec,
|
||||
'ammo_pec': ammo,
|
||||
'cost_per_shot': cost_per_shot,
|
||||
}
|
||||
|
||||
self._update_summary()
|
||||
|
||||
def _select_armor(self):
|
||||
"""Open simplified armor selector."""
|
||||
from ui.armor_selection_dialog import ArmorSelectionDialog
|
||||
|
||||
dialog = ArmorSelectionDialog(self)
|
||||
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||
result = dialog.get_selected_armor()
|
||||
if result:
|
||||
self._set_armor(result)
|
||||
|
||||
def _set_armor(self, armor_data: dict):
|
||||
"""Set armor and calculate cost."""
|
||||
# armor_data has: name, decay_pec, protection_summary
|
||||
name = armor_data.get('name', 'Unknown')
|
||||
decay_pec = Decimal(str(armor_data.get('decay_pec', 0)))
|
||||
cost_per_hit = decay_pec / Decimal("100")
|
||||
|
||||
# Update UI
|
||||
self.armor_btn.setText(name[:30])
|
||||
prot_summary = armor_data.get('protection_summary', '')
|
||||
self.armor_info.setText(prot_summary[:50] if prot_summary else "No protection data")
|
||||
self.armor_cost_label.setText(f"{cost_per_hit:.4f} PED")
|
||||
|
||||
# Store for saving
|
||||
self._pending_armor = {
|
||||
'name': name,
|
||||
'api_id': armor_data.get('api_id'),
|
||||
'decay_pec': decay_pec,
|
||||
'cost_per_hit': cost_per_hit,
|
||||
}
|
||||
|
||||
self._update_summary()
|
||||
|
||||
def _select_healing(self):
|
||||
"""Open healing selector."""
|
||||
from ui.healing_selector import HealingSelectorDialog
|
||||
|
||||
dialog = HealingSelectorDialog(self)
|
||||
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||
healing = dialog.get_selected_healing()
|
||||
if healing:
|
||||
self._set_healing(healing)
|
||||
|
||||
def _set_healing(self, healing_data: dict):
|
||||
"""Set healing and calculate cost."""
|
||||
name = healing_data.get('name', 'Unknown')
|
||||
decay_pec = Decimal(str(healing_data.get('decay_pec', 0)))
|
||||
heal_amount = Decimal(str(healing_data.get('heal_amount', 0)))
|
||||
cost_per_heal = decay_pec / Decimal("100")
|
||||
|
||||
# Update UI
|
||||
self.healing_btn.setText(name[:30])
|
||||
self.healing_info.setText(f"Heal: {heal_amount} HP")
|
||||
self.healing_cost_label.setText(f"{cost_per_heal:.4f} PED")
|
||||
|
||||
# Store for saving
|
||||
self._pending_healing = {
|
||||
'name': name,
|
||||
'api_id': healing_data.get('api_id'),
|
||||
'decay_pec': decay_pec,
|
||||
'heal_amount': heal_amount,
|
||||
'cost_per_heal': cost_per_heal,
|
||||
}
|
||||
|
||||
self._update_summary()
|
||||
|
||||
def _update_summary(self):
|
||||
"""Update cost summary display."""
|
||||
shot = getattr(self, '_pending_weapon', {}).get('cost_per_shot', Decimal("0"))
|
||||
hit = getattr(self, '_pending_armor', {}).get('cost_per_hit', Decimal("0"))
|
||||
heal = getattr(self, '_pending_healing', {}).get('cost_per_heal', Decimal("0"))
|
||||
|
||||
self.summary_shot.setText(f"{shot:.4f} PED")
|
||||
self.summary_hit.setText(f"{hit:.4f} PED")
|
||||
self.summary_heal.setText(f"{heal:.4f} PED")
|
||||
|
||||
def _save_loadout(self):
|
||||
"""Save current configuration."""
|
||||
name = self.name_edit.text().strip()
|
||||
if not name:
|
||||
QMessageBox.warning(self, "Missing Name", "Please enter a loadout name")
|
||||
return
|
||||
|
||||
# Build config from pending data
|
||||
weapon = getattr(self, '_pending_weapon', {})
|
||||
armor = getattr(self, '_pending_armor', {})
|
||||
healing = getattr(self, '_pending_healing', {})
|
||||
|
||||
config = LoadoutConfig(
|
||||
name=name,
|
||||
weapon_cost_per_shot=weapon.get('cost_per_shot', Decimal("0")),
|
||||
armor_cost_per_hit=armor.get('cost_per_hit', Decimal("0")),
|
||||
healing_cost_per_heal=healing.get('cost_per_heal', Decimal("0")),
|
||||
weapon_name=weapon.get('name', 'None'),
|
||||
weapon_damage=weapon.get('damage', Decimal("0")),
|
||||
weapon_decay_pec=weapon.get('decay_pec', Decimal("0")),
|
||||
weapon_ammo_pec=weapon.get('ammo_pec', Decimal("0")),
|
||||
armor_name=armor.get('name', 'None'),
|
||||
armor_decay_pec=armor.get('decay_pec', Decimal("0")),
|
||||
healing_name=healing.get('name', 'None'),
|
||||
healing_decay_pec=healing.get('decay_pec', Decimal("0")),
|
||||
weapon_api_id=weapon.get('api_id'),
|
||||
armor_api_id=armor.get('api_id'),
|
||||
healing_api_id=healing.get('api_id'),
|
||||
)
|
||||
|
||||
# Save to file
|
||||
safe_name = "".join(c for c in name if c.isalnum() or c in "._- ").strip()
|
||||
if not safe_name:
|
||||
safe_name = "unnamed"
|
||||
|
||||
filepath = self.config_dir / f"{safe_name}.json"
|
||||
|
||||
try:
|
||||
with open(filepath, 'w') as f:
|
||||
json.dump(config.to_dict(), f, indent=2)
|
||||
|
||||
self.current_config = config
|
||||
self.loadout_saved.emit(config)
|
||||
self._load_saved_loadouts()
|
||||
|
||||
QMessageBox.information(self, "Saved", f"Loadout '{name}' saved!")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Error", f"Failed to save: {e}")
|
||||
|
||||
def _load_saved_loadouts(self):
|
||||
"""Load list of saved loadouts."""
|
||||
self.saved_list.clear()
|
||||
|
||||
try:
|
||||
for filepath in sorted(self.config_dir.glob("*.json")):
|
||||
try:
|
||||
with open(filepath, 'r') as f:
|
||||
data = json.load(f)
|
||||
config = LoadoutConfig.from_dict(data)
|
||||
|
||||
item = QListWidgetItem(f"📋 {config.name}")
|
||||
item.setData(Qt.ItemDataRole.UserRole, str(filepath))
|
||||
|
||||
# Tooltip with costs
|
||||
tooltip = (
|
||||
f"Weapon: {config.weapon_name}\n"
|
||||
f"Armor: {config.armor_name}\n"
|
||||
f"Cost/Shot: {config.weapon_cost_per_shot:.4f} PED\n"
|
||||
f"Cost/Hit: {config.armor_cost_per_hit:.4f} PED\n"
|
||||
f"Cost/Heal: {config.healing_cost_per_heal:.4f} PED"
|
||||
)
|
||||
item.setToolTip(tooltip)
|
||||
self.saved_list.addItem(item)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load {filepath}: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to list loadouts: {e}")
|
||||
|
||||
def _on_loadout_selected(self, item: QListWidgetItem):
|
||||
"""Load selected loadout into UI."""
|
||||
filepath = item.data(Qt.ItemDataRole.UserRole)
|
||||
if not filepath:
|
||||
return
|
||||
|
||||
try:
|
||||
with open(filepath, 'r') as f:
|
||||
data = json.load(f)
|
||||
config = LoadoutConfig.from_dict(data)
|
||||
|
||||
self._load_config_into_ui(config)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Error", f"Failed to load: {e}")
|
||||
|
||||
def _on_loadout_double_clicked(self, item: QListWidgetItem):
|
||||
"""Double-click to select and close."""
|
||||
self._on_loadout_selected(item)
|
||||
self.accept()
|
||||
|
||||
def _load_config_into_ui(self, config: LoadoutConfig):
|
||||
"""Load config values into UI fields."""
|
||||
self.name_edit.setText(config.name)
|
||||
|
||||
# Weapon
|
||||
if config.weapon_name != "None":
|
||||
self.weapon_btn.setText(config.weapon_name[:30])
|
||||
self.weapon_info.setText(f"Damage: {config.weapon_damage}")
|
||||
self.weapon_decay_label.setText(f"{config.weapon_decay_pec} PEC")
|
||||
self.weapon_ammo_label.setText(f"{config.weapon_ammo_pec}")
|
||||
self.weapon_cost_label.setText(f"{config.weapon_cost_per_shot:.4f} PED")
|
||||
self._pending_weapon = {
|
||||
'name': config.weapon_name,
|
||||
'api_id': config.weapon_api_id,
|
||||
'damage': config.weapon_damage,
|
||||
'decay_pec': config.weapon_decay_pec,
|
||||
'ammo_pec': config.weapon_ammo_pec,
|
||||
'cost_per_shot': config.weapon_cost_per_shot,
|
||||
}
|
||||
|
||||
# Armor
|
||||
if config.armor_name != "None":
|
||||
self.armor_btn.setText(config.armor_name[:30])
|
||||
self.armor_info.setText("Loaded from save")
|
||||
self.armor_cost_label.setText(f"{config.armor_cost_per_hit:.4f} PED")
|
||||
self._pending_armor = {
|
||||
'name': config.armor_name,
|
||||
'api_id': config.armor_api_id,
|
||||
'decay_pec': config.armor_decay_pec,
|
||||
'cost_per_hit': config.armor_cost_per_hit,
|
||||
}
|
||||
|
||||
# Healing
|
||||
if config.healing_name != "None":
|
||||
self.healing_btn.setText(config.healing_name[:30])
|
||||
self.healing_cost_label.setText(f"{config.healing_cost_per_heal:.4f} PED")
|
||||
self._pending_healing = {
|
||||
'name': config.healing_name,
|
||||
'api_id': config.healing_api_id,
|
||||
'decay_pec': config.healing_decay_pec,
|
||||
'cost_per_heal': config.healing_cost_per_heal,
|
||||
}
|
||||
|
||||
self._update_summary()
|
||||
self.current_config = config
|
||||
|
||||
def _new_loadout(self):
|
||||
"""Clear all fields for new loadout."""
|
||||
self.name_edit.clear()
|
||||
self.weapon_btn.setText("Select Weapon...")
|
||||
self.weapon_info.setText("None selected")
|
||||
self.weapon_decay_label.setText("0 PEC")
|
||||
self.weapon_ammo_label.setText("0")
|
||||
self.weapon_cost_label.setText("0.0000 PED")
|
||||
|
||||
self.armor_btn.setText("Select Armor...")
|
||||
self.armor_info.setText("None selected")
|
||||
self.armor_cost_label.setText("0.0000 PED")
|
||||
|
||||
self.healing_btn.setText("Select Healing...")
|
||||
self.healing_info.setText("None selected")
|
||||
self.healing_cost_label.setText("0.0000 PED")
|
||||
|
||||
self._pending_weapon = None
|
||||
self._pending_armor = None
|
||||
self._pending_healing = None
|
||||
self._update_summary()
|
||||
self.current_config = None
|
||||
|
||||
def _delete_loadout(self):
|
||||
"""Delete selected loadout."""
|
||||
item = self.saved_list.currentItem()
|
||||
if not item:
|
||||
QMessageBox.information(self, "No Selection", "Please select a loadout to delete")
|
||||
return
|
||||
|
||||
filepath = item.data(Qt.ItemDataRole.UserRole)
|
||||
name = item.text().replace("📋 ", "")
|
||||
|
||||
reply = QMessageBox.question(
|
||||
self, "Confirm Delete",
|
||||
f"Delete '{name}'?",
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
|
||||
)
|
||||
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
try:
|
||||
os.remove(filepath)
|
||||
self._load_saved_loadouts()
|
||||
self._new_loadout()
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Error", f"Failed to delete: {e}")
|
||||
|
||||
def get_config(self) -> Optional[LoadoutConfig]:
|
||||
"""Get current configuration."""
|
||||
return self.current_config
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Backward Compatibility
|
||||
# ============================================================================
|
||||
|
||||
# Keep old names for imports
|
||||
LoadoutManager = LoadoutManagerDialog
|
||||
|
|
@ -0,0 +1,262 @@
|
|||
"""
|
||||
Lemontropia Suite - Loadout Selection Dialog v2.0
|
||||
Simplified cost-focused selection.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from decimal import Decimal
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QPushButton, QListWidget, QListWidgetItem,
|
||||
QMessageBox, QWidget, QGridLayout
|
||||
)
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
|
||||
from ui.loadout_manager_simple import LoadoutConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LoadoutSelectionDialog(QDialog):
|
||||
"""Simplified dialog for selecting a loadout to start a session.
|
||||
|
||||
Emits cost data that can be used directly by MainWindow for tracking.
|
||||
"""
|
||||
|
||||
# Signal emits: {
|
||||
# 'id': 0, # 0 = file-based
|
||||
# 'name': 'Loadout Name',
|
||||
# 'source': 'file',
|
||||
# 'costs': {
|
||||
# 'cost_per_shot': Decimal,
|
||||
# 'cost_per_hit': Decimal,
|
||||
# 'cost_per_heal': Decimal,
|
||||
# },
|
||||
# 'display': {
|
||||
# 'weapon_name': str,
|
||||
# 'armor_name': str,
|
||||
# 'healing_name': str,
|
||||
# }
|
||||
# }
|
||||
loadout_selected = pyqtSignal(dict)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("Select Loadout for Session")
|
||||
self.setMinimumSize(500, 400)
|
||||
|
||||
self.config_dir = Path.home() / ".lemontropia" / "loadouts"
|
||||
self.selected_loadout = None
|
||||
|
||||
self._setup_ui()
|
||||
self._load_loadouts()
|
||||
|
||||
def _setup_ui(self):
|
||||
"""Setup simple UI."""
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setSpacing(10)
|
||||
|
||||
# Header
|
||||
header = QLabel("Select a loadout to track costs during your session:")
|
||||
header.setStyleSheet("font-size: 12px; color: #888;")
|
||||
layout.addWidget(header)
|
||||
|
||||
# Loadout list
|
||||
self.loadout_list = QListWidget()
|
||||
self.loadout_list.itemClicked.connect(self._on_select)
|
||||
self.loadout_list.itemDoubleClicked.connect(self._on_double_click)
|
||||
layout.addWidget(self.loadout_list)
|
||||
|
||||
# Preview panel
|
||||
self.preview = QWidget()
|
||||
preview_layout = QGridLayout(self.preview)
|
||||
|
||||
# Weapon
|
||||
preview_layout.addWidget(QLabel("⚔️ Weapon:"), 0, 0)
|
||||
self.preview_weapon = QLabel("-")
|
||||
preview_layout.addWidget(self.preview_weapon, 0, 1)
|
||||
|
||||
preview_layout.addWidget(QLabel(" Cost/Shot:"), 1, 0)
|
||||
self.preview_cost_shot = QLabel("-")
|
||||
self.preview_cost_shot.setStyleSheet("color: #7FFF7F;")
|
||||
preview_layout.addWidget(self.preview_cost_shot, 1, 1)
|
||||
|
||||
# Armor
|
||||
preview_layout.addWidget(QLabel("🛡️ Armor:"), 2, 0)
|
||||
self.preview_armor = QLabel("-")
|
||||
preview_layout.addWidget(self.preview_armor, 2, 1)
|
||||
|
||||
preview_layout.addWidget(QLabel(" Cost/Hit:"), 3, 0)
|
||||
self.preview_cost_hit = QLabel("-")
|
||||
self.preview_cost_hit.setStyleSheet("color: #7FFF7F;")
|
||||
preview_layout.addWidget(self.preview_cost_hit, 3, 1)
|
||||
|
||||
# Healing
|
||||
preview_layout.addWidget(QLabel("💚 Healing:"), 4, 0)
|
||||
self.preview_healing = QLabel("-")
|
||||
preview_layout.addWidget(self.preview_healing, 4, 1)
|
||||
|
||||
preview_layout.addWidget(QLabel(" Cost/Heal:"), 5, 0)
|
||||
self.preview_cost_heal = QLabel("-")
|
||||
self.preview_cost_heal.setStyleSheet("color: #7FFF7F;")
|
||||
preview_layout.addWidget(self.preview_cost_heal, 5, 1)
|
||||
|
||||
preview_layout.setColumnStretch(1, 1)
|
||||
layout.addWidget(self.preview)
|
||||
|
||||
# Buttons
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.addStretch()
|
||||
|
||||
self.skip_btn = QPushButton("Skip (No Cost Tracking)")
|
||||
self.skip_btn.clicked.connect(self._on_skip)
|
||||
self.skip_btn.setStyleSheet("color: #888;")
|
||||
button_layout.addWidget(self.skip_btn)
|
||||
|
||||
self.ok_btn = QPushButton("Start Session ▶")
|
||||
self.ok_btn.clicked.connect(self._on_accept)
|
||||
self.ok_btn.setEnabled(False)
|
||||
self.ok_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #2E7D32;
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:disabled {
|
||||
background-color: #333;
|
||||
color: #666;
|
||||
}
|
||||
""")
|
||||
button_layout.addWidget(self.ok_btn)
|
||||
|
||||
self.cancel_btn = QPushButton("Cancel")
|
||||
self.cancel_btn.clicked.connect(self.reject)
|
||||
button_layout.addWidget(self.cancel_btn)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
def _load_loadouts(self):
|
||||
"""Load all saved loadouts."""
|
||||
self.loadout_list.clear()
|
||||
self.loadouts = []
|
||||
|
||||
if not self.config_dir.exists():
|
||||
return
|
||||
|
||||
try:
|
||||
for filepath in sorted(self.config_dir.glob("*.json")):
|
||||
try:
|
||||
with open(filepath, 'r') as f:
|
||||
data = json.load(f)
|
||||
config = LoadoutConfig.from_dict(data)
|
||||
|
||||
# Build display text
|
||||
has_costs = (
|
||||
config.weapon_cost_per_shot > 0 or
|
||||
config.armor_cost_per_hit > 0 or
|
||||
config.healing_cost_per_heal > 0
|
||||
)
|
||||
|
||||
display = f"📋 {config.name}"
|
||||
if has_costs:
|
||||
display += f" (💰 {config.weapon_cost_per_shot:.3f}/shot)"
|
||||
|
||||
item = QListWidgetItem(display)
|
||||
item.setData(Qt.ItemDataRole.UserRole, config)
|
||||
|
||||
# Tooltip
|
||||
tooltip = (
|
||||
f"Weapon: {config.weapon_name}\n"
|
||||
f"Armor: {config.armor_name}\n"
|
||||
f"Healing: {config.healing_name}\n"
|
||||
f"\n"
|
||||
f"Cost/Shot: {config.weapon_cost_per_shot:.4f} PED\n"
|
||||
f"Cost/Hit: {config.armor_cost_per_hit:.4f} PED\n"
|
||||
f"Cost/Heal: {config.healing_cost_per_heal:.4f} PED"
|
||||
)
|
||||
item.setToolTip(tooltip)
|
||||
|
||||
self.loadout_list.addItem(item)
|
||||
self.loadouts.append(config)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load {filepath}: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to list loadouts: {e}")
|
||||
|
||||
if not self.loadouts:
|
||||
item = QListWidgetItem("(No saved loadouts)")
|
||||
item.setFlags(Qt.ItemFlag.NoItemFlags)
|
||||
self.loadout_list.addItem(item)
|
||||
|
||||
def _on_select(self, item: QListWidgetItem):
|
||||
"""Update preview when loadout selected."""
|
||||
config = item.data(Qt.ItemDataRole.UserRole)
|
||||
if not isinstance(config, LoadoutConfig):
|
||||
return
|
||||
|
||||
self.selected_loadout = config
|
||||
|
||||
# Update preview
|
||||
self.preview_weapon.setText(config.weapon_name if config.weapon_name != "None" else "Not set")
|
||||
self.preview_armor.setText(config.armor_name if config.armor_name != "None" else "Not set")
|
||||
self.preview_healing.setText(config.healing_name if config.healing_name != "None" else "Not set")
|
||||
|
||||
self.preview_cost_shot.setText(f"{config.weapon_cost_per_shot:.4f} PED")
|
||||
self.preview_cost_hit.setText(f"{config.armor_cost_per_hit:.4f} PED")
|
||||
self.preview_cost_heal.setText(f"{config.healing_cost_per_heal:.4f} PED")
|
||||
|
||||
self.ok_btn.setEnabled(True)
|
||||
|
||||
def _on_double_click(self, item: QListWidgetItem):
|
||||
"""Double-click to select immediately."""
|
||||
self._on_select(item)
|
||||
self._on_accept()
|
||||
|
||||
def _on_skip(self):
|
||||
"""Start session without cost tracking."""
|
||||
self.loadout_selected.emit({
|
||||
'id': 0,
|
||||
'name': 'No Loadout',
|
||||
'source': 'none',
|
||||
'costs': {
|
||||
'cost_per_shot': Decimal('0'),
|
||||
'cost_per_hit': Decimal('0'),
|
||||
'cost_per_heal': Decimal('0'),
|
||||
},
|
||||
'display': {
|
||||
'weapon_name': 'None',
|
||||
'armor_name': 'None',
|
||||
'healing_name': 'None',
|
||||
}
|
||||
})
|
||||
self.accept()
|
||||
|
||||
def _on_accept(self):
|
||||
"""Emit selected loadout and close."""
|
||||
if not self.selected_loadout:
|
||||
QMessageBox.warning(self, "No Selection", "Please select a loadout")
|
||||
return
|
||||
|
||||
config = self.selected_loadout
|
||||
|
||||
self.loadout_selected.emit({
|
||||
'id': 0, # File-based
|
||||
'name': config.name,
|
||||
'source': 'file',
|
||||
'costs': {
|
||||
'cost_per_shot': config.weapon_cost_per_shot,
|
||||
'cost_per_hit': config.armor_cost_per_hit,
|
||||
'cost_per_heal': config.healing_cost_per_heal,
|
||||
},
|
||||
'display': {
|
||||
'weapon_name': config.weapon_name,
|
||||
'armor_name': config.armor_name,
|
||||
'healing_name': config.healing_name,
|
||||
}
|
||||
})
|
||||
|
||||
self.accept()
|
||||
|
|
@ -936,23 +936,23 @@ class MainWindow(QMainWindow):
|
|||
# Show HUD and start session tracking
|
||||
self.hud.show()
|
||||
|
||||
# Get gear names from loadout selection if available, otherwise fall back to selected weapon
|
||||
weapon_name = getattr(self, '_session_weapon_name', None) or self._selected_weapon or "Unknown"
|
||||
armor_name = getattr(self, '_session_armor_name', None) or "None"
|
||||
healing_name = getattr(self, '_session_healing_name', None) or "None"
|
||||
# Get gear names and costs from simplified loadout structure
|
||||
session_display = getattr(self, '_session_display', {})
|
||||
session_costs = getattr(self, '_session_costs', {})
|
||||
|
||||
weapon_name = session_display.get('weapon_name', self._selected_weapon or "Unknown")
|
||||
armor_name = session_display.get('armor_name', "None")
|
||||
healing_name = session_display.get('healing_name', "None")
|
||||
loadout_name = "Loadout" if session_costs else "Default"
|
||||
|
||||
weapon_stats = self._selected_weapon_stats or {}
|
||||
weapon_dpp = Decimal(str(weapon_stats.get('dpp', 0)))
|
||||
weapon_cost_per_hour = Decimal(str(weapon_stats.get('cost_per_hour', 0)))
|
||||
|
||||
# Get loadout name from session selection
|
||||
loadout_info = getattr(self, '_session_loadout_info', None)
|
||||
loadout_name = loadout_info.get('name', 'Default') if loadout_info else "Default"
|
||||
|
||||
# Get cost data from session loadout
|
||||
cost_per_shot = getattr(self, '_session_cost_per_shot', Decimal('0'))
|
||||
cost_per_hit = getattr(self, '_session_cost_per_hit', Decimal('0'))
|
||||
cost_per_heal = getattr(self, '_session_cost_per_heal', Decimal('0'))
|
||||
# Get cost data from simplified structure
|
||||
cost_per_shot = session_costs.get('cost_per_shot', Decimal('0'))
|
||||
cost_per_hit = session_costs.get('cost_per_hit', Decimal('0'))
|
||||
cost_per_heal = session_costs.get('cost_per_heal', Decimal('0'))
|
||||
|
||||
self.hud.start_session(
|
||||
weapon=weapon_name,
|
||||
|
|
@ -966,12 +966,8 @@ class MainWindow(QMainWindow):
|
|||
cost_per_heal=cost_per_heal
|
||||
)
|
||||
|
||||
# Set up cost tracker if loadout selected (database-based only)
|
||||
if loadout_info and loadout_info.get('id') and loadout_info.get('source') == 'db':
|
||||
self._setup_session_cost_tracker(loadout_info)
|
||||
else:
|
||||
# For JSON-based loadouts, use manual cost tracking based on extracted values
|
||||
self.log_info("CostTracker", "Using manual cost tracking for file-based loadout")
|
||||
# Simple cost tracking - no database required
|
||||
self.log_info("CostTracker", "Cost tracking enabled with pre-calculated values")
|
||||
|
||||
self.log_info("HUD", f"HUD shown - Weapon: {weapon_name}, Armor: {armor_name}, Healing: {healing_name}, Loadout: {loadout_name}")
|
||||
|
||||
|
|
@ -1239,45 +1235,41 @@ class MainWindow(QMainWindow):
|
|||
"""Handle start session button - shows loadout selection first."""
|
||||
if self.current_project and self.session_state == SessionState.IDLE:
|
||||
# Show loadout selection dialog
|
||||
from ui.loadout_selection_dialog import LoadoutSelectionDialog
|
||||
from ui.loadout_selection_dialog_simple import LoadoutSelectionDialog
|
||||
dialog = LoadoutSelectionDialog(self)
|
||||
dialog.loadout_selected.connect(self._on_loadout_selected_for_session)
|
||||
dialog.rejected.connect(lambda: self.log_info("Session", "Session start cancelled - no loadout selected"))
|
||||
dialog.exec()
|
||||
|
||||
def _on_loadout_selected_for_session(self, loadout_info: dict):
|
||||
"""Handle loadout selection and start session."""
|
||||
loadout_id = loadout_info.get('id', 0)
|
||||
"""Handle loadout selection and start session - simplified cost-focused version."""
|
||||
loadout_name = loadout_info.get('name', 'No Loadout')
|
||||
loadout_data = loadout_info.get('data', {})
|
||||
costs = loadout_info.get('costs', {})
|
||||
display = loadout_info.get('display', {})
|
||||
|
||||
if loadout_id > 0:
|
||||
self.log_info("Session", f"Starting session with loadout: {loadout_name} (ID: {loadout_id})")
|
||||
# Store the selected loadout info for use in start_session
|
||||
self._session_loadout_info = loadout_info
|
||||
|
||||
# Extract gear names from loadout data
|
||||
self._session_weapon_name = loadout_data.get('weapon_name', 'Unknown')
|
||||
self._session_armor_name = loadout_data.get('armor_set_name', 'Unknown')
|
||||
self._session_healing_name = loadout_data.get('heal_name', 'Unknown')
|
||||
|
||||
# Extract cost data for session tracking (even for JSON-based loadouts)
|
||||
# Store cost data for session tracking
|
||||
from decimal import Decimal
|
||||
self._session_cost_per_shot = Decimal(str(loadout_data.get('weapon_decay_pec', 0))) / Decimal('100') + \
|
||||
Decimal(str(loadout_data.get('weapon_ammo_pec', 0))) * Decimal('0.0001')
|
||||
self._session_cost_per_hit = Decimal(str(loadout_data.get('armor_decay_pec', 0))) / Decimal('100')
|
||||
self._session_cost_per_heal = Decimal(str(loadout_data.get('heal_cost_pec', 0))) / Decimal('100')
|
||||
self._session_costs = {
|
||||
'cost_per_shot': costs.get('cost_per_shot', Decimal('0')),
|
||||
'cost_per_hit': costs.get('cost_per_hit', Decimal('0')),
|
||||
'cost_per_heal': costs.get('cost_per_heal', Decimal('0')),
|
||||
}
|
||||
|
||||
self.log_info("SessionCosts", f"Cost/Shot: {self._session_cost_per_shot:.4f}, Cost/Hit: {self._session_cost_per_hit:.4f}, Cost/Heal: {self._session_cost_per_heal:.4f}")
|
||||
# Store display data for HUD
|
||||
self._session_display = {
|
||||
'weapon_name': display.get('weapon_name', 'None'),
|
||||
'armor_name': display.get('armor_name', 'None'),
|
||||
'healing_name': display.get('healing_name', 'None'),
|
||||
}
|
||||
|
||||
if any(self._session_costs.values()):
|
||||
self.log_info("Session", f"Starting with loadout: {loadout_name}")
|
||||
self.log_info("SessionCosts",
|
||||
f"Shot: {self._session_costs['cost_per_shot']:.4f} PED, "
|
||||
f"Hit: {self._session_costs['cost_per_hit']:.4f} PED, "
|
||||
f"Heal: {self._session_costs['cost_per_heal']:.4f} PED")
|
||||
else:
|
||||
self.log_info("Session", "Starting session without loadout")
|
||||
self._session_loadout_info = None
|
||||
self._session_weapon_name = None
|
||||
self._session_armor_name = None
|
||||
self._session_healing_name = None
|
||||
self._session_cost_per_shot = None
|
||||
self._session_cost_per_hit = None
|
||||
self._session_cost_per_heal = None
|
||||
self.log_info("Session", f"Starting with loadout: {loadout_name} (no costs configured)")
|
||||
|
||||
# Now start the session
|
||||
if self.current_project:
|
||||
|
|
@ -1462,25 +1454,37 @@ class MainWindow(QMainWindow):
|
|||
|
||||
def on_loadout_manager(self):
|
||||
"""Open Loadout Manager dialog."""
|
||||
from ui.loadout_manager import LoadoutManagerDialog
|
||||
from ui.loadout_manager_simple import LoadoutManagerDialog
|
||||
dialog = LoadoutManagerDialog(self)
|
||||
dialog.loadout_saved.connect(self.on_loadout_selected)
|
||||
dialog.exec()
|
||||
|
||||
def on_loadout_selected(self, loadout):
|
||||
"""Handle loadout selection from Loadout Manager."""
|
||||
"""Handle loadout selection from Loadout Manager - simplified version."""
|
||||
self._selected_loadout = loadout
|
||||
self.log_info("Loadout", f"Selected loadout: {loadout.name}")
|
||||
|
||||
# Update selected gear from loadout
|
||||
# Update selected gear from loadout (simplified structure)
|
||||
if hasattr(loadout, 'weapon_name'):
|
||||
self._selected_weapon = loadout.weapon_name
|
||||
if hasattr(loadout, 'heal_cost_pec'):
|
||||
# Create medical tool stats from loadout heal cost
|
||||
self._selected_medical_tool = loadout.heal_name
|
||||
if hasattr(loadout, 'healing_name'):
|
||||
self._selected_medical_tool = loadout.healing_name
|
||||
self._selected_medical_tool_stats = {
|
||||
'decay': float(loadout.heal_cost_pec),
|
||||
'cost_per_heal': float(loadout.heal_cost_pec) / 100.0, # Convert PEC to PED
|
||||
'decay': float(loadout.healing_decay_pec),
|
||||
'cost_per_heal': float(loadout.healing_cost_per_heal),
|
||||
}
|
||||
# Store simplified costs for session
|
||||
if hasattr(loadout, 'weapon_cost_per_shot'):
|
||||
self._session_costs = {
|
||||
'cost_per_shot': loadout.weapon_cost_per_shot,
|
||||
'cost_per_hit': loadout.armor_cost_per_hit,
|
||||
'cost_per_heal': loadout.healing_cost_per_heal,
|
||||
}
|
||||
if hasattr(loadout, 'weapon_name'):
|
||||
self._session_display = {
|
||||
'weapon_name': loadout.weapon_name,
|
||||
'armor_name': loadout.armor_name,
|
||||
'healing_name': loadout.healing_name,
|
||||
}
|
||||
|
||||
def on_select_gear(self, gear_type: str = "weapon"):
|
||||
|
|
|
|||
Loading…
Reference in New Issue