refactor(ui): replace per-hour metrics with per-action cost metrics

Removed confusing per-hour calculations and replaced with practical
per-action metrics that players actually need:

New Metrics Displayed:
- ⚔️ Weapon: Cost/Shot (PED), DPS, DPP
- 🛡️ Armor: Cost/Hit (PED), Protection
- 💚 Healing: Cost/Heal (PED), HP/PEC

Removed:
- Activity Settings section (Shots/Hour, Hits/Hour, Heals/Hour)
- Per-hour cost calculations

The Break-Even calculator remains based on mob HP.

These metrics represent actual costs per action taken:
- Each shot fired costs X PED
- Each hit taken costs Y PED
- Each heal used costs Z PED

Much more intuitive for tracking hunt profitability.
This commit is contained in:
LemonNexus 2026-02-09 15:02:56 +00:00
parent 2183b79ce8
commit bd6abd60c2
1 changed files with 109 additions and 78 deletions

View File

@ -181,6 +181,41 @@ class LoadoutConfig:
return Decimal("0")
return total_damage / total_cost
def calculate_dps(self) -> Decimal:
"""Calculate Damage Per Second (DPS) with all attachments."""
# Standard EU fire rate is typically 1 shot per second without attachments
# A scope may increase this, but we'll use a base of 1.0 for simplicity
shots_per_second = Decimal("1.0")
if self.weapon_scope:
# Scopes can increase fire rate, but let's be conservative
shots_per_second = Decimal("1.2")
return self.get_total_damage() * shots_per_second
def get_armor_decay_per_hit(self) -> Decimal:
"""Calculate armor decay cost per hit taken (in PED)."""
decay_per_hit = Decimal("0")
if self.equipped_armor:
decay_per_hit = self.equipped_armor.get_total_decay_per_hit()
else:
# Legacy fallback
decay_per_hit = self.armor_decay_pec
# Add plate decay costs
for slot, plate_config in self.armor_plates.items():
decay_per_hit += plate_config.decay_pec
return decay_per_hit / Decimal("100") # Convert PEC to PED
def get_heal_cost_per_use(self) -> Decimal:
"""Calculate healing cost per use (in PED)."""
return self.heal_cost_pec / Decimal("100")
def get_hp_per_pec(self) -> Decimal:
"""Calculate HP healed per PEC spent."""
if self.heal_cost_pec == 0:
return Decimal("0")
return self.heal_amount / self.heal_cost_pec
def calculate_weapon_cost_per_hour(self) -> Decimal:
"""Calculate weapon cost per hour."""
cost_per_shot = self.get_total_decay_per_shot() + self.get_total_ammo_per_shot()
@ -1114,22 +1149,6 @@ class LoadoutManagerDialog(QDialog):
self.loadout_name_edit = QLineEdit()
self.loadout_name_edit.setPlaceholderText("Enter loadout name...")
# Activity settings
self.shots_per_hour_spin = QSpinBox()
self.shots_per_hour_spin.setRange(1, 20000)
self.shots_per_hour_spin.setValue(3600)
self.shots_per_hour_spin.setSuffix(" /hr")
self.hits_per_hour_spin = QSpinBox()
self.hits_per_hour_spin.setRange(0, 5000)
self.hits_per_hour_spin.setValue(720)
self.hits_per_hour_spin.setSuffix(" /hr")
self.heals_per_hour_spin = QSpinBox()
self.heals_per_hour_spin.setRange(0, 500)
self.heals_per_hour_spin.setValue(60)
self.heals_per_hour_spin.setSuffix(" /hr")
# Weapon section
self.weapon_group = DarkGroupBox("🔫 Weapon Configuration")
self.select_weapon_btn = QPushButton("🔍 Select from Entropia Nexus")
@ -1185,19 +1204,28 @@ class LoadoutManagerDialog(QDialog):
self.heal_cost_edit = DecimalLineEdit()
self.heal_amount_edit = DecimalLineEdit()
# Cost summary
self.summary_group = DarkGroupBox("📊 Cost Summary")
self.weapon_cost_label = QLabel("0.00 PEC/hr")
self.armor_cost_label = QLabel("0.00 PEC/hr")
self.heal_cost_label = QLabel("0.00 PEC/hr")
self.total_cost_label = QLabel("0.00 PED/hr")
self.total_cost_label.setStyleSheet("color: #ff9800; font-weight: bold; font-size: 18px;")
self.total_dpp_label = QLabel("0.0000")
self.total_dpp_label.setStyleSheet("color: #4caf50; font-weight: bold; font-size: 18px;")
# Cost summary - refined metrics (cost per action, not per hour)
self.summary_group = DarkGroupBox("📊 Cost Analysis")
# Protection summary
# Weapon metrics
self.cost_per_shot_label = QLabel("0.0000 PED")
self.dps_label = QLabel("0.00 DPS")
self.dpp_label = QLabel("0.0000 DPP")
self.dpp_label.setStyleSheet("color: #4caf50; font-weight: bold;")
# Armor metrics
self.cost_per_hit_label = QLabel("0.0000 PED")
self.protection_summary_label = QLabel("No protection")
self.protection_summary_label.setStyleSheet("color: #4a90d9; font-size: 12px;")
self.protection_summary_label.setStyleSheet("color: #4a90d9;")
# Healing metrics
self.cost_per_heal_label = QLabel("0.0000 PED")
self.hp_per_pec_label = QLabel("0.00 HP/PEC")
self.hp_per_pec_label.setStyleSheet("color: #4caf50;")
# Total cost display
self.total_cost_label = QLabel("0.0000 PED")
self.total_cost_label.setStyleSheet("color: #ff9800; font-weight: bold; font-size: 16px;")
# Break-even calculator
self.mob_health_edit = DecimalLineEdit()
@ -1263,17 +1291,6 @@ class LoadoutManagerDialog(QDialog):
name_layout.addWidget(self.loadout_name_edit, stretch=1)
right_layout.addLayout(name_layout)
# Activity settings
activity_group = DarkGroupBox("⚙️ Activity Settings")
activity_layout = QGridLayout(activity_group)
activity_layout.addWidget(QLabel("Shots/Hour:"), 0, 0)
activity_layout.addWidget(self.shots_per_hour_spin, 0, 1)
activity_layout.addWidget(QLabel("Hits Taken/Hour:"), 0, 2)
activity_layout.addWidget(self.hits_per_hour_spin, 0, 3)
activity_layout.addWidget(QLabel("Heals/Hour:"), 0, 4)
activity_layout.addWidget(self.heals_per_hour_spin, 0, 5)
right_layout.addWidget(activity_group)
# Weapon configuration
weapon_layout = QFormLayout(self.weapon_group)
@ -1421,23 +1438,41 @@ class LoadoutManagerDialog(QDialog):
right_layout.addWidget(self.accessories_group)
# Cost summary
# Cost summary - refined layout with better metrics
summary_layout = QFormLayout(self.summary_group)
summary_layout.addRow("Weapon Cost:", self.weapon_cost_label)
summary_layout.addRow("Armor Cost:", self.armor_cost_label)
summary_layout.addRow("Healing Cost:", self.heal_cost_label)
summary_layout.addRow("Total DPP:", self.total_dpp_label)
summary_layout.addRow("Total Cost:", self.total_cost_label)
# Protection summary
summary_layout.addRow("Protection:", self.protection_summary_label)
# Weapon section
weapon_group = QLabel("<b>⚔️ Weapon</b>")
weapon_group.setStyleSheet("color: #e0e0e0; margin-top: 5px;")
summary_layout.addRow(weapon_group)
summary_layout.addRow(" Cost/Shot:", self.cost_per_shot_label)
summary_layout.addRow(" DPS:", self.dps_label)
summary_layout.addRow(" DPP:", self.dpp_label)
# Armor section
armor_group = QLabel("<b>🛡️ Armor</b>")
armor_group.setStyleSheet("color: #e0e0e0; margin-top: 5px;")
summary_layout.addRow(armor_group)
summary_layout.addRow(" Cost/Hit:", self.cost_per_hit_label)
summary_layout.addRow(" Protection:", self.protection_summary_label)
# Healing section
heal_group = QLabel("<b>💚 Healing</b>")
heal_group.setStyleSheet("color: #e0e0e0; margin-top: 5px;")
summary_layout.addRow(heal_group)
summary_layout.addRow(" Cost/Heal:", self.cost_per_heal_label)
summary_layout.addRow(" HP/PEC:", self.hp_per_pec_label)
# Break-even calculator
break_even_group = QLabel("<b>📈 Break-Even</b>")
break_even_group.setStyleSheet("color: #e0e0e0; margin-top: 10px;")
summary_layout.addRow(break_even_group)
break_even_layout = QHBoxLayout()
break_even_layout.addWidget(QLabel("Mob Health:"))
break_even_layout.addWidget(QLabel("Mob HP:"))
break_even_layout.addWidget(self.mob_health_edit)
break_even_layout.addWidget(self.calc_break_even_btn)
summary_layout.addRow("Break-Even:", break_even_layout)
summary_layout.addRow("", self.break_even_label)
summary_layout.addRow(" Calculate:", break_even_layout)
summary_layout.addRow(" Break-even:", self.break_even_label)
right_layout.addWidget(self.summary_group)
@ -1478,11 +1513,6 @@ class LoadoutManagerDialog(QDialog):
# Healing
self.heal_combo.currentTextChanged.connect(self._on_heal_changed)
# Activity settings
self.shots_per_hour_spin.valueChanged.connect(self._update_calculations)
self.hits_per_hour_spin.valueChanged.connect(self._update_calculations)
self.heals_per_hour_spin.valueChanged.connect(self._update_calculations)
# Buttons
self.save_btn.clicked.connect(self._save_loadout)
self.load_btn.clicked.connect(self._load_selected)
@ -1855,33 +1885,40 @@ class LoadoutManagerDialog(QDialog):
self._update_calculations()
def _update_calculations(self):
"""Update all cost and DPP calculations."""
"""Update all cost and performance calculations."""
try:
config = self._get_current_config()
# Update DPP
# Weapon metrics (per shot, not per hour)
cost_per_shot = config.get_total_decay_per_shot() + config.get_total_ammo_per_shot()
self.cost_per_shot_label.setText(f"{cost_per_shot:.4f} PED")
# DPS calculation
dps = config.calculate_dps()
self.dps_label.setText(f"{dps:.2f}")
# DPP (Damage Per Pec)
dpp = config.calculate_dpp()
self.dpp_label.setText(f"{dpp:.4f}")
self.total_dpp_label.setText(f"{dpp:.4f}")
# Update cost breakdown
weapon_cost = config.calculate_weapon_cost_per_hour()
armor_cost = config.calculate_armor_cost_per_hour()
heal_cost = config.calculate_heal_cost_per_hour()
total_cost = config.calculate_total_cost_per_hour()
# Armor metrics (cost per hit)
cost_per_hit = config.get_armor_decay_per_hit()
self.cost_per_hit_label.setText(f"{cost_per_hit:.4f} PED")
self.weapon_cost_label.setText(f"{weapon_cost:.0f} PEC/hr")
self.armor_cost_label.setText(f"{armor_cost:.0f} PEC/hr")
self.heal_cost_label.setText(f"{heal_cost:.0f} PEC/hr")
self.total_cost_label.setText(f"{total_cost:.2f} PED/hr")
# Update protection summary
# Protection summary
protection = config.get_total_protection()
prot_text = format_protection(protection)
if prot_text == "None":
self.protection_summary_label.setText("No protection")
else:
self.protection_summary_label.setText(f"Total: {protection.get_total()} | {prot_text}")
self.protection_summary_label.setText(f"Total: {protection.get_total():.1f} | {prot_text}")
# Healing metrics
cost_per_heal = config.get_heal_cost_per_use()
self.cost_per_heal_label.setText(f"{cost_per_heal:.4f} PED")
hp_per_pec = config.get_hp_per_pec()
self.hp_per_pec_label.setText(f"{hp_per_pec:.2f}")
except Exception as e:
logger.error(f"Calculation error: {e}")
@ -1972,17 +2009,14 @@ class LoadoutManagerDialog(QDialog):
heal_name=self.heal_combo.currentText(),
heal_cost_pec=self.heal_cost_edit.get_decimal(),
heal_amount=self.heal_amount_edit.get_decimal(),
shots_per_hour=self.shots_per_hour_spin.value(),
hits_per_hour=self.hits_per_hour_spin.value(),
heals_per_hour=self.heals_per_hour_spin.value(),
shots_per_hour=3600, # Default, no longer in UI
hits_per_hour=720, # Default, no longer in UI
heals_per_hour=60, # Default, no longer in UI
)
def _set_config(self, config: LoadoutConfig):
"""Set UI fields from configuration."""
self.loadout_name_edit.setText(config.name)
self.shots_per_hour_spin.setValue(config.shots_per_hour)
self.hits_per_hour_spin.setValue(config.hits_per_hour)
self.heals_per_hour_spin.setValue(config.heals_per_hour)
# Weapon
self.weapon_name_label.setText(config.weapon_name)
@ -2166,9 +2200,6 @@ class LoadoutManagerDialog(QDialog):
self.heal_amount_edit.clear()
# Reset values
self.shots_per_hour_spin.setValue(3600)
self.hits_per_hour_spin.setValue(720)
self.heals_per_hour_spin.setValue(60)
self.mob_health_edit.set_decimal(Decimal("100"))
# Reset combos