""" Weapon Attachment Selector for Lemontropia Suite Browse and search weapon attachments (scopes, sights, amplifiers, absorbers) """ from decimal import Decimal from PyQt6.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QTreeWidget, QTreeWidgetItem, QHeaderView, QLabel, QDialogButtonBox, QProgressBar, QGroupBox, QFormLayout, QComboBox, QTabWidget, QWidget ) from PyQt6.QtCore import Qt, QThread, pyqtSignal from PyQt6.QtGui import QColor from typing import Optional, List from core.nexus_full_api import get_nexus_api, NexusAttachment class AttachmentLoaderThread(QThread): """Background thread for loading attachments from API.""" attachments_loaded = pyqtSignal(list) error_occurred = pyqtSignal(str) def run(self): try: api = get_nexus_api() attachments = api.get_all_attachments() self.attachments_loaded.emit(attachments) except Exception as e: self.error_occurred.emit(str(e)) class AttachmentSelectorDialog(QDialog): """Dialog for selecting weapon attachments from Entropia Nexus API.""" attachment_selected = pyqtSignal(NexusAttachment) def __init__(self, parent=None, attachment_type: str = ""): super().__init__(parent) self.preferred_type = attachment_type.lower() type_names = { "amplifier": "Amplifier", "scope": "Scope", "sight": "Sight", "absorber": "Absorber" } title_type = type_names.get(self.preferred_type, "Attachment") self.setWindowTitle(f"Select {title_type} - Entropia Nexus") self.setMinimumSize(900, 600) self.all_attachments: List[NexusAttachment] = [] self.selected_attachment: Optional[NexusAttachment] = None self._setup_ui() self._load_data() def _setup_ui(self): layout = QVBoxLayout(self) layout.setSpacing(10) # Status self.status_label = QLabel("Loading attachments from Entropia Nexus...") layout.addWidget(self.status_label) self.progress = QProgressBar() self.progress.setRange(0, 0) layout.addWidget(self.progress) # Type filter tabs self.tabs = QTabWidget() # Create tabs for each attachment type self.tab_all = self._create_attachment_tab("All Attachments") self.tab_amps = self._create_attachment_tab("Amplifiers") self.tab_scopes = self._create_attachment_tab("Scopes") self.tab_sights = self._create_attachment_tab("Sights") self.tab_absorbers = self._create_attachment_tab("Absorbers") self.tabs.addTab(self.tab_all["widget"], "All") self.tabs.addTab(self.tab_amps["widget"], "⚡ Amplifiers") self.tabs.addTab(self.tab_scopes["widget"], "🔭 Scopes") self.tabs.addTab(self.tab_sights["widget"], "🎯 Sights") self.tabs.addTab(self.tab_absorbers["widget"], "🛡️ Absorbers") # Set preferred tab if self.preferred_type == "amplifier": self.tabs.setCurrentIndex(1) elif self.preferred_type == "scope": self.tabs.setCurrentIndex(2) elif self.preferred_type == "sight": self.tabs.setCurrentIndex(3) elif self.preferred_type == "absorber": self.tabs.setCurrentIndex(4) self.tabs.currentChanged.connect(self._on_tab_changed) layout.addWidget(self.tabs) # Preview panel self.preview_group = QGroupBox("Attachment Preview") preview_layout = QFormLayout(self.preview_group) self.preview_name = QLabel("-") self.preview_type = QLabel("-") self.preview_damage = QLabel("-") self.preview_range = QLabel("-") self.preview_decay = QLabel("-") self.preview_efficiency = QLabel("-") preview_layout.addRow("Name:", self.preview_name) preview_layout.addRow("Type:", self.preview_type) preview_layout.addRow("Damage Bonus:", self.preview_damage) preview_layout.addRow("Range Bonus:", self.preview_range) preview_layout.addRow("Decay:", self.preview_decay) preview_layout.addRow("Efficiency:", self.preview_efficiency) layout.addWidget(self.preview_group) # Buttons buttons = QDialogButtonBox( QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel ) buttons.accepted.connect(self._on_accept) buttons.rejected.connect(self.reject) self.ok_button = buttons.button(QDialogButtonBox.StandardButton.Ok) self.ok_button.setEnabled(False) layout.addWidget(buttons) def _create_attachment_tab(self, title: str) -> dict: """Create a tab with search and tree for attachments.""" widget = QWidget() layout = QVBoxLayout(widget) # Search search_layout = QHBoxLayout() search_layout.addWidget(QLabel("Search:")) search_input = QLineEdit() search_input.setPlaceholderText(f"Search {title.lower()}...") search_layout.addWidget(search_input) clear_btn = QPushButton("Clear") search_layout.addWidget(clear_btn) layout.addLayout(search_layout) # Tree tree = QTreeWidget() tree.setHeaderLabels(["Name", "Type", "Dmg+", "Rng+", "Decay", "Efficiency"]) header = tree.header() header.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) tree.itemSelectionChanged.connect(self._on_selection_changed) tree.itemDoubleClicked.connect(self._on_double_click) layout.addWidget(tree) return { "widget": widget, "search": search_input, "clear": clear_btn, "tree": tree } def _load_data(self): """Load attachments in background thread.""" self.loader = AttachmentLoaderThread() self.loader.attachments_loaded.connect(self._on_attachments_loaded) self.loader.error_occurred.connect(self._on_load_error) self.loader.start() def _on_attachments_loaded(self, attachments: List[NexusAttachment]): """Handle loaded attachments.""" self.all_attachments = attachments self.status_label.setText(f"Loaded {len(attachments)} attachments from Entropia Nexus") self.progress.setRange(0, 100) self.progress.setValue(100) # Populate all tabs self._populate_tab(self.tab_all, self.all_attachments) self._populate_tab(self.tab_amps, [a for a in attachments if a.attachment_type == "amplifier"]) self._populate_tab(self.tab_scopes, [a for a in attachments if a.attachment_type == "scope"]) self._populate_tab(self.tab_sights, [a for a in attachments if a.attachment_type == "sight"]) self._populate_tab(self.tab_absorbers, [a for a in attachments if a.attachment_type == "absorber"]) # Connect search signals for tab in [self.tab_all, self.tab_amps, self.tab_scopes, self.tab_sights, self.tab_absorbers]: tab["search"].textChanged.connect(lambda text, t=tab: self._on_search_changed(t, text)) tab["clear"].clicked.connect(lambda t=tab: t["search"].clear()) def _on_load_error(self, error: str): """Handle load error.""" self.status_label.setText(f"Error loading attachments: {error}") self.progress.setRange(0, 100) self.progress.setValue(0) def _populate_tab(self, tab: dict, attachments: List[NexusAttachment]): """Populate a tab's tree with attachments.""" tree = tab["tree"] tree.clear() # Sort by damage bonus (amplifiers) or range (scopes) attachments = sorted(attachments, key=lambda a: a.damage_bonus + a.range_bonus, reverse=True) for att in attachments: item = QTreeWidgetItem() item.setText(0, att.name) item.setText(1, att.attachment_type.title()) item.setText(2, f"+{att.damage_bonus}") item.setText(3, f"+{att.range_bonus}") item.setText(4, f"{att.decay:.2f}") item.setText(5, f"{att.efficiency_bonus:.1f}%") # Color code by type if att.attachment_type == "amplifier": item.setForeground(0, QColor("#ff9800")) # Orange elif att.attachment_type == "scope": item.setForeground(0, QColor("#2196f3")) # Blue elif att.attachment_type == "sight": item.setForeground(0, QColor("#4caf50")) # Green elif att.attachment_type == "absorber": item.setForeground(0, QColor("#9c27b0")) # Purple item.setData(0, Qt.ItemDataRole.UserRole, att) tree.addTopLevelItem(item) def _on_search_changed(self, tab: dict, text: str): """Handle search text change in a tab.""" tree = tab["tree"] for i in range(tree.topLevelItemCount()): item = tree.topLevelItem(i) attachment = item.data(0, Qt.ItemDataRole.UserRole) if text.lower() in attachment.name.lower(): item.setHidden(False) else: item.setHidden(True) def _on_tab_changed(self, index: int): """Handle tab change.""" # Clear selection when changing tabs self.selected_attachment = None self.ok_button.setEnabled(False) self._clear_preview() def _on_selection_changed(self): """Handle selection change in current tab.""" current_tab = self._get_current_tab() if current_tab: items = current_tab["tree"].selectedItems() if items: self.selected_attachment = items[0].data(0, Qt.ItemDataRole.UserRole) self.ok_button.setEnabled(True) self._update_preview(self.selected_attachment) else: self.selected_attachment = None self.ok_button.setEnabled(False) self._clear_preview() def _get_current_tab(self) -> Optional[dict]: """Get the currently active tab data.""" index = self.tabs.currentIndex() tabs = [self.tab_all, self.tab_amps, self.tab_scopes, self.tab_sights, self.tab_absorbers] if 0 <= index < len(tabs): return tabs[index] return None def _update_preview(self, attachment: NexusAttachment): """Update preview panel.""" self.preview_name.setText(attachment.name) self.preview_type.setText(attachment.attachment_type.title()) self.preview_damage.setText(f"+{attachment.damage_bonus}") self.preview_range.setText(f"+{attachment.range_bonus}") self.preview_decay.setText(f"{attachment.decay:.2f} PEC") self.preview_efficiency.setText(f"{attachment.efficiency_bonus:.1f}%") def _clear_preview(self): """Clear preview panel.""" self.preview_name.setText("-") self.preview_type.setText("-") self.preview_damage.setText("-") self.preview_range.setText("-") self.preview_decay.setText("-") self.preview_efficiency.setText("-") def _on_double_click(self, item, column): """Handle double click.""" self._on_accept() def _on_accept(self): """Handle OK button.""" if self.selected_attachment: self.attachment_selected.emit(self.selected_attachment) self.accept()