fix: correct equipment balance and sort items by stat power #69

Merged
naomi merged 2 commits from fix/equipment-duplicate-combat-multiplier into main 2026-03-19 08:51:08 -07:00
2 changed files with 26 additions and 9 deletions
+9 -9
View File
@@ -101,7 +101,7 @@ export const defaultEquipment: Array<Equipment> = [
type: "weapon", type: "weapon",
}, },
{ {
bonus: { combatMultiplier: 2.75 }, bonus: { combatMultiplier: 3.25 },
cost: { crystals: 500, essence: 2000, gold: 0 }, cost: { crystals: 500, essence: 2000, gold: 0 },
description: description:
"A blade made of compressed nothingness. It does not cut — it simply unmakes.", "A blade made of compressed nothingness. It does not cut — it simply unmakes.",
@@ -204,7 +204,7 @@ export const defaultEquipment: Array<Equipment> = [
type: "armour", type: "armour",
}, },
{ {
bonus: { goldMultiplier: 2.25 }, bonus: { goldMultiplier: 2.75 },
description: description:
"Woven from threads of pure starlight harvested by the Astral Wraith. Income flows like cosmic energy.", "Woven from threads of pure starlight harvested by the Astral Wraith. Income flows like cosmic energy.",
equipped: false, equipped: false,
@@ -305,7 +305,7 @@ export const defaultEquipment: Array<Equipment> = [
type: "trinket", type: "trinket",
}, },
{ {
bonus: { clickMultiplier: 2, goldMultiplier: 1.25 }, bonus: { clickMultiplier: 2.25, goldMultiplier: 1.25 },
description: description:
"The legendary stone that grants mastery over gold and combat alike.", "The legendary stone that grants mastery over gold and combat alike.",
equipped: false, equipped: false,
@@ -316,7 +316,7 @@ export const defaultEquipment: Array<Equipment> = [
type: "trinket", type: "trinket",
}, },
{ {
bonus: { clickMultiplier: 2.25, goldMultiplier: 1.15 }, bonus: { clickMultiplier: 2.25, goldMultiplier: 1.25 },
description: description:
"A flame that has never been extinguished, sealed in crystal by the Phoenix Lord. It burns with the power of rebirth.", "A flame that has never been extinguished, sealed in crystal by the Phoenix Lord. It burns with the power of rebirth.",
equipped: false, equipped: false,
@@ -697,7 +697,7 @@ export const defaultEquipment: Array<Equipment> = [
}, },
// ── Purchasable endgame sinks ───────────────────────────────────────────── // ── Purchasable endgame sinks ─────────────────────────────────────────────
{ {
bonus: { clickMultiplier: 2.5 }, bonus: { clickMultiplier: 3 },
cost: { crystals: 0, essence: 20_000_000, gold: 0 }, cost: { crystals: 0, essence: 20_000_000, gold: 0 },
description: description:
"A lens of compressed celestial light that sharpens every strike with divine precision.", "A lens of compressed celestial light that sharpens every strike with divine precision.",
@@ -709,7 +709,7 @@ export const defaultEquipment: Array<Equipment> = [
type: "trinket", type: "trinket",
}, },
{ {
bonus: { goldMultiplier: 3 }, bonus: { goldMultiplier: 3.75 },
cost: { crystals: 0, essence: 50_000_000, gold: 0 }, cost: { crystals: 0, essence: 50_000_000, gold: 0 },
description: description:
"A book written in the language of the deep — reading it aligns your guild's operations with abyssal efficiency.", "A book written in the language of the deep — reading it aligns your guild's operations with abyssal efficiency.",
@@ -721,7 +721,7 @@ export const defaultEquipment: Array<Equipment> = [
type: "armour", type: "armour",
}, },
{ {
bonus: { combatMultiplier: 4 }, bonus: { combatMultiplier: 7 },
cost: { crystals: 0, essence: 100_000_000, gold: 0 }, cost: { crystals: 0, essence: 100_000_000, gold: 0 },
description: description:
"A weapon that channels void energy — the absence of resistance makes every strike devastating.", "A weapon that channels void energy — the absence of resistance makes every strike devastating.",
@@ -733,7 +733,7 @@ export const defaultEquipment: Array<Equipment> = [
type: "weapon", type: "weapon",
}, },
{ {
bonus: { clickMultiplier: 3.5, goldMultiplier: 1.5 }, bonus: { clickMultiplier: 4, goldMultiplier: 1.5 },
cost: { crystals: 5_000_000, essence: 0, gold: 0 }, cost: { crystals: 5_000_000, essence: 0, gold: 0 },
description: description:
"A gem forged in the heart of the Infernal Court — it burns with productivity and righteous fury in equal measure.", "A gem forged in the heart of the Infernal Court — it burns with productivity and righteous fury in equal measure.",
@@ -745,7 +745,7 @@ export const defaultEquipment: Array<Equipment> = [
type: "trinket", type: "trinket",
}, },
{ {
bonus: { goldMultiplier: 4 }, bonus: { goldMultiplier: 4.75 },
cost: { crystals: 20_000_000, essence: 0, gold: 0 }, cost: { crystals: 20_000_000, essence: 0, gold: 0 },
description: description:
"Armour structured around a crystalline lattice of optimal income calculations. Every gold piece finds you faster.", "Armour structured around a crystalline lattice of optimal income calculations. Every gold piece finds you faster.",
@@ -7,6 +7,7 @@
/* eslint-disable react/no-multi-comp -- Sub-component is tightly coupled to the panel */ /* eslint-disable react/no-multi-comp -- Sub-component is tightly coupled to the panel */
/* eslint-disable max-lines-per-function -- Complex component with many render paths */ /* eslint-disable max-lines-per-function -- Complex component with many render paths */
/* eslint-disable complexity -- Complex component with many conditional render paths */ /* eslint-disable complexity -- Complex component with many conditional render paths */
/* eslint-disable max-lines -- Equipment panel with set bonus display and sort logic */
import { type JSX, useState } from "react"; import { type JSX, useState } from "react";
import { useGame } from "../../context/gameContext.js"; import { useGame } from "../../context/gameContext.js";
import { EQUIPMENT_SETS } from "../../data/equipmentSets.js"; import { EQUIPMENT_SETS } from "../../data/equipmentSets.js";
@@ -188,6 +189,20 @@ const EquipmentCard = ({
); );
}; };
/**
* Computes a combined power score for sorting — sum of all bonus multipliers.
* Using the sum (rather than a single stat) keeps hybrid items in sensible order.
* @param item - The equipment piece whose bonus multipliers are summed.
* @returns The combined bonus value.
*/
const equipmentPower = (item: Equipment): number => {
return (
(item.bonus.combatMultiplier ?? 1)
+ (item.bonus.goldMultiplier ?? 1)
+ (item.bonus.clickMultiplier ?? 1)
);
};
const slotOrder: Array<EquipmentType> = [ "weapon", "armour", "trinket" ]; const slotOrder: Array<EquipmentType> = [ "weapon", "armour", "trinket" ];
const slotLabel: Record<EquipmentType, string> = { const slotLabel: Record<EquipmentType, string> = {
armour: "🛡️ Armour", armour: "🛡️ Armour",
@@ -320,6 +335,8 @@ const EquipmentPanel = (): JSX.Element => {
{slotOrder.map((slotType) => { {slotOrder.map((slotType) => {
const items = equipment.filter((item) => { const items = equipment.filter((item) => {
return item.type === slotType && (showLocked || item.owned); return item.type === slotType && (showLocked || item.owned);
}).sort((a, b) => {
return equipmentPower(a) - equipmentPower(b);
}); });
return ( return (
<div className="equipment-slot-section" key={slotType}> <div className="equipment-slot-section" key={slotType}>