263 lines
9.1 KiB
Python
263 lines
9.1 KiB
Python
"""
|
|
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()
|