Lemontropia-Suite/ui/weapon_selector.py

225 lines
7.9 KiB
Python

"""
Lemontropia Suite - Simple Weapon Selector
Quick weapon selection for cost configuration.
"""
import logging
from decimal import Decimal, InvalidOperation
from PyQt6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QLabel,
QPushButton, QLineEdit, QListWidget, QListWidgetItem,
QMessageBox, QFormLayout, QGroupBox
)
from PyQt6.QtCore import Qt, pyqtSignal
from core.nexus_full_api import get_nexus_api
logger = logging.getLogger(__name__)
class WeaponSelectorDialog(QDialog):
"""Simple dialog to select a weapon for cost tracking."""
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Select Weapon")
self.setMinimumSize(500, 400)
self.selected_weapon = None
self._weapons = []
self._setup_ui()
self._load_weapons()
def _setup_ui(self):
"""Setup simple UI."""
layout = QVBoxLayout(self)
layout.setSpacing(10)
# Search
search_layout = QHBoxLayout()
search_layout.addWidget(QLabel("Search:"))
self.search_edit = QLineEdit()
self.search_edit.setPlaceholderText("Type weapon name...")
self.search_edit.textChanged.connect(self._on_search)
search_layout.addWidget(self.search_edit)
layout.addLayout(search_layout)
# Weapon list
self.weapon_list = QListWidget()
self.weapon_list.itemClicked.connect(self._on_select)
self.weapon_list.itemDoubleClicked.connect(self._on_double_click)
layout.addWidget(self.weapon_list)
# Preview
preview_group = QGroupBox("Selected Weapon")
preview_layout = QFormLayout(preview_group)
self.preview_name = QLabel("None")
preview_layout.addRow("Name:", self.preview_name)
self.preview_damage = QLabel("-")
preview_layout.addRow("Damage:", self.preview_damage)
self.preview_decay = QLabel("-")
preview_layout.addRow("Decay:", self.preview_decay)
self.preview_ammo = QLabel("-")
preview_layout.addRow("Ammo:", self.preview_ammo)
self.preview_cost = QLabel("-")
self.preview_cost.setStyleSheet("font-weight: bold; color: #7FFF7F;")
preview_layout.addRow("Cost/Shot:", self.preview_cost)
layout.addWidget(preview_group)
# Buttons
button_layout = QHBoxLayout()
button_layout.addStretch()
self.ok_btn = QPushButton("Select")
self.ok_btn.clicked.connect(self.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)
cancel_btn = QPushButton("Cancel")
cancel_btn.clicked.connect(self.reject)
button_layout.addWidget(cancel_btn)
layout.addLayout(button_layout)
def _load_weapons(self):
"""Load weapons from API."""
try:
nexus = get_nexus_api()
all_weapons = nexus.get_all_weapons()
# Filter out weapons with invalid/null decay or ammo
self._weapons = []
for w in all_weapons:
try:
# Validate that decay and ammo are valid numbers
decay_val = w.decay if w.decay is not None else 0
ammo_val = w.ammo_burn if w.ammo_burn is not None else 0
# Try to convert to Decimal to validate
Decimal(str(decay_val))
Decimal(str(ammo_val))
self._weapons.append(w)
except (InvalidOperation, ValueError, TypeError):
# Skip weapons with invalid data
continue
# Sort by name
self._weapons.sort(key=lambda w: w.name.lower())
self._populate_list(self._weapons)
except Exception as e:
logger.error(f"Failed to load weapons: {e}")
QMessageBox.warning(self, "Error", f"Failed to load weapons: {e}")
def _populate_list(self, weapons):
"""Populate list with weapons."""
self.weapon_list.clear()
for weapon in weapons:
try:
# Get values with safe defaults
decay_raw = weapon.decay if weapon.decay is not None else 0
ammo_raw = weapon.ammo_burn if weapon.ammo_burn is not None else 0
# Calculate cost per shot
decay_pec = Decimal(str(decay_raw))
ammo = Decimal(str(ammo_raw))
cost_per_shot = (decay_pec / Decimal("100")) + (ammo * Decimal("0.0001"))
item = QListWidgetItem(f"{weapon.name} (💰 {cost_per_shot:.4f} PED)")
item.setData(Qt.ItemDataRole.UserRole, weapon)
# Tooltip
tooltip = (
f"Damage: {weapon.damage}\n"
f"Decay: {decay_raw} PEC\n"
f"Ammo: {ammo_raw}\n"
f"Range: {weapon.range_val}\n"
f"DPP: {weapon.dpp:.2f}" if weapon.dpp else "DPP: -"
)
item.setToolTip(tooltip)
self.weapon_list.addItem(item)
except Exception as e:
# Skip weapons that fail to process
logger.debug(f"Skipping weapon {getattr(weapon, 'name', 'unknown')}: {e}")
continue
def _on_search(self, text):
"""Filter weapons by search text."""
if not text:
self._populate_list(self._weapons)
return
text_lower = text.lower()
filtered = [w for w in self._weapons if text_lower in w.name.lower()]
self._populate_list(filtered)
def _on_select(self, item):
"""Update preview when weapon selected."""
weapon = item.data(Qt.ItemDataRole.UserRole)
if not weapon:
return
self.selected_weapon = weapon
# Get values with safe defaults
decay_raw = weapon.decay if weapon.decay is not None else 0
ammo_raw = weapon.ammo_burn if weapon.ammo_burn is not None else 0
# Calculate cost per shot
decay_pec = Decimal(str(decay_raw))
ammo = Decimal(str(ammo_raw))
cost_per_shot = (decay_pec / Decimal("100")) + (ammo * Decimal("0.0001"))
# Update preview
self.preview_name.setText(weapon.name)
self.preview_damage.setText(str(weapon.damage) if weapon.damage is not None else "-")
self.preview_decay.setText(f"{decay_raw} PEC")
self.preview_ammo.setText(str(ammo_raw))
self.preview_cost.setText(f"{cost_per_shot:.4f} PED")
self.ok_btn.setEnabled(True)
# 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 preview
self.preview_name.setText(weapon.name)
self.preview_damage.setText(str(weapon.damage))
self.preview_decay.setText(f"{weapon.decay} PEC")
self.preview_ammo.setText(str(weapon.ammo))
self.preview_cost.setText(f"{cost_per_shot:.4f} PED")
self.ok_btn.setEnabled(True)
def _on_double_click(self, item):
"""Double-click to select immediately."""
self._on_select(item)
self.accept()
def get_selected_weapon(self):
"""Get the selected weapon."""
return self.selected_weapon