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:
parent
2183b79ce8
commit
bd6abd60c2
|
|
@ -181,6 +181,41 @@ class LoadoutConfig:
|
||||||
return Decimal("0")
|
return Decimal("0")
|
||||||
return total_damage / total_cost
|
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:
|
def calculate_weapon_cost_per_hour(self) -> Decimal:
|
||||||
"""Calculate weapon cost per hour."""
|
"""Calculate weapon cost per hour."""
|
||||||
cost_per_shot = self.get_total_decay_per_shot() + self.get_total_ammo_per_shot()
|
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 = QLineEdit()
|
||||||
self.loadout_name_edit.setPlaceholderText("Enter loadout name...")
|
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
|
# Weapon section
|
||||||
self.weapon_group = DarkGroupBox("🔫 Weapon Configuration")
|
self.weapon_group = DarkGroupBox("🔫 Weapon Configuration")
|
||||||
self.select_weapon_btn = QPushButton("🔍 Select from Entropia Nexus")
|
self.select_weapon_btn = QPushButton("🔍 Select from Entropia Nexus")
|
||||||
|
|
@ -1185,19 +1204,28 @@ class LoadoutManagerDialog(QDialog):
|
||||||
self.heal_cost_edit = DecimalLineEdit()
|
self.heal_cost_edit = DecimalLineEdit()
|
||||||
self.heal_amount_edit = DecimalLineEdit()
|
self.heal_amount_edit = DecimalLineEdit()
|
||||||
|
|
||||||
# Cost summary
|
# Cost summary - refined metrics (cost per action, not per hour)
|
||||||
self.summary_group = DarkGroupBox("📊 Cost Summary")
|
self.summary_group = DarkGroupBox("📊 Cost Analysis")
|
||||||
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;")
|
|
||||||
|
|
||||||
# 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 = 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
|
# Break-even calculator
|
||||||
self.mob_health_edit = DecimalLineEdit()
|
self.mob_health_edit = DecimalLineEdit()
|
||||||
|
|
@ -1263,17 +1291,6 @@ class LoadoutManagerDialog(QDialog):
|
||||||
name_layout.addWidget(self.loadout_name_edit, stretch=1)
|
name_layout.addWidget(self.loadout_name_edit, stretch=1)
|
||||||
right_layout.addLayout(name_layout)
|
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 configuration
|
||||||
weapon_layout = QFormLayout(self.weapon_group)
|
weapon_layout = QFormLayout(self.weapon_group)
|
||||||
|
|
||||||
|
|
@ -1421,23 +1438,41 @@ class LoadoutManagerDialog(QDialog):
|
||||||
|
|
||||||
right_layout.addWidget(self.accessories_group)
|
right_layout.addWidget(self.accessories_group)
|
||||||
|
|
||||||
# Cost summary
|
# Cost summary - refined layout with better metrics
|
||||||
summary_layout = QFormLayout(self.summary_group)
|
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
|
# Weapon section
|
||||||
summary_layout.addRow("Protection:", self.protection_summary_label)
|
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 = 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.mob_health_edit)
|
||||||
break_even_layout.addWidget(self.calc_break_even_btn)
|
break_even_layout.addWidget(self.calc_break_even_btn)
|
||||||
summary_layout.addRow("Break-Even:", break_even_layout)
|
summary_layout.addRow(" Calculate:", break_even_layout)
|
||||||
summary_layout.addRow("", self.break_even_label)
|
summary_layout.addRow(" Break-even:", self.break_even_label)
|
||||||
|
|
||||||
right_layout.addWidget(self.summary_group)
|
right_layout.addWidget(self.summary_group)
|
||||||
|
|
||||||
|
|
@ -1478,11 +1513,6 @@ class LoadoutManagerDialog(QDialog):
|
||||||
# Healing
|
# Healing
|
||||||
self.heal_combo.currentTextChanged.connect(self._on_heal_changed)
|
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
|
# Buttons
|
||||||
self.save_btn.clicked.connect(self._save_loadout)
|
self.save_btn.clicked.connect(self._save_loadout)
|
||||||
self.load_btn.clicked.connect(self._load_selected)
|
self.load_btn.clicked.connect(self._load_selected)
|
||||||
|
|
@ -1855,33 +1885,40 @@ class LoadoutManagerDialog(QDialog):
|
||||||
self._update_calculations()
|
self._update_calculations()
|
||||||
|
|
||||||
def _update_calculations(self):
|
def _update_calculations(self):
|
||||||
"""Update all cost and DPP calculations."""
|
"""Update all cost and performance calculations."""
|
||||||
try:
|
try:
|
||||||
config = self._get_current_config()
|
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()
|
dpp = config.calculate_dpp()
|
||||||
self.dpp_label.setText(f"{dpp:.4f}")
|
self.dpp_label.setText(f"{dpp:.4f}")
|
||||||
self.total_dpp_label.setText(f"{dpp:.4f}")
|
|
||||||
|
|
||||||
# Update cost breakdown
|
# Armor metrics (cost per hit)
|
||||||
weapon_cost = config.calculate_weapon_cost_per_hour()
|
cost_per_hit = config.get_armor_decay_per_hit()
|
||||||
armor_cost = config.calculate_armor_cost_per_hour()
|
self.cost_per_hit_label.setText(f"{cost_per_hit:.4f} PED")
|
||||||
heal_cost = config.calculate_heal_cost_per_hour()
|
|
||||||
total_cost = config.calculate_total_cost_per_hour()
|
|
||||||
|
|
||||||
self.weapon_cost_label.setText(f"{weapon_cost:.0f} PEC/hr")
|
# Protection summary
|
||||||
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 = config.get_total_protection()
|
protection = config.get_total_protection()
|
||||||
prot_text = format_protection(protection)
|
prot_text = format_protection(protection)
|
||||||
if prot_text == "None":
|
if prot_text == "None":
|
||||||
self.protection_summary_label.setText("No protection")
|
self.protection_summary_label.setText("No protection")
|
||||||
else:
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Calculation error: {e}")
|
logger.error(f"Calculation error: {e}")
|
||||||
|
|
@ -1972,17 +2009,14 @@ class LoadoutManagerDialog(QDialog):
|
||||||
heal_name=self.heal_combo.currentText(),
|
heal_name=self.heal_combo.currentText(),
|
||||||
heal_cost_pec=self.heal_cost_edit.get_decimal(),
|
heal_cost_pec=self.heal_cost_edit.get_decimal(),
|
||||||
heal_amount=self.heal_amount_edit.get_decimal(),
|
heal_amount=self.heal_amount_edit.get_decimal(),
|
||||||
shots_per_hour=self.shots_per_hour_spin.value(),
|
shots_per_hour=3600, # Default, no longer in UI
|
||||||
hits_per_hour=self.hits_per_hour_spin.value(),
|
hits_per_hour=720, # Default, no longer in UI
|
||||||
heals_per_hour=self.heals_per_hour_spin.value(),
|
heals_per_hour=60, # Default, no longer in UI
|
||||||
)
|
)
|
||||||
|
|
||||||
def _set_config(self, config: LoadoutConfig):
|
def _set_config(self, config: LoadoutConfig):
|
||||||
"""Set UI fields from configuration."""
|
"""Set UI fields from configuration."""
|
||||||
self.loadout_name_edit.setText(config.name)
|
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
|
# Weapon
|
||||||
self.weapon_name_label.setText(config.weapon_name)
|
self.weapon_name_label.setText(config.weapon_name)
|
||||||
|
|
@ -2166,9 +2200,6 @@ class LoadoutManagerDialog(QDialog):
|
||||||
self.heal_amount_edit.clear()
|
self.heal_amount_edit.clear()
|
||||||
|
|
||||||
# Reset values
|
# 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"))
|
self.mob_health_edit.set_decimal(Decimal("100"))
|
||||||
|
|
||||||
# Reset combos
|
# Reset combos
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue