feat: add equipment set bonuses and boss bounty runestones

- Define EquipmentSet type + computeSetBonuses utility in packages/types
- Add setId field to Equipment type and assign sets to 27 equipment items
- Create 9 named equipment sets (Iron Vanguard → Eternal Throne) with 2pc/3pc bonuses
- Apply set combat multiplier in boss route
- Apply set gold/click multipliers in tick engine and click handler
- Include set bonuses in anti-cheat delta validation
- Show active set bonus strip + set badge per card in EquipmentPanel
- Add boss first-kill bounty runestones (scaling 1–10 per boss tier)
- Update AboutPanel and IDEAS.md
This commit is contained in:
2026-03-06 23:56:45 -08:00
committed by Naomi Carrigan
parent 48bf74e713
commit 078ae50e69
19 changed files with 488 additions and 20 deletions
+11 -4
View File
@@ -1,4 +1,6 @@
import type { Achievement, Equipment, GameState } from "@elysium/types";
import { computeSetBonuses } from "@elysium/types";
import { EQUIPMENT_SETS } from "../data/equipmentSets.js";
import { updateChallengeProgress } from "../utils/dailyChallenges.js";
/**
@@ -57,6 +59,7 @@ export const applyTick = (state: GameState, deltaSeconds: number): GameState =>
(mult, e) => mult * (e.bonus.goldMultiplier ?? 1),
1,
);
const setGoldMultiplier = computeSetBonuses(equippedItems.map((e) => e.id), EQUIPMENT_SETS).goldMultiplier;
const runestonesIncome = state.prestige.runestonesIncomeMultiplier ?? 1;
const runestonesEssence = state.prestige.runestonesEssenceMultiplier ?? 1;
@@ -88,6 +91,7 @@ export const applyTick = (state: GameState, deltaSeconds: number): GameState =>
prestige *
runestonesIncome *
equipmentGoldMultiplier *
setGoldMultiplier *
deltaSeconds;
essenceGained +=
@@ -228,7 +232,7 @@ export const applyTick = (state: GameState, deltaSeconds: number): GameState =>
essence: newEssence,
crystals: capResource(state.resources.crystals + questCrystals + challengeCrystals),
},
dailyChallenges: updatedDailyChallenges,
...(updatedDailyChallenges !== undefined ? { dailyChallenges: updatedDailyChallenges } : {}),
player: {
...state.player,
totalGoldEarned: newTotalGoldEarned,
@@ -271,9 +275,11 @@ export const calculateClickPower = (state: GameState): number => {
.filter((u) => u.purchased && u.target === "click")
.reduce((mult, upgrade) => mult * upgrade.multiplier, 1);
const equipmentClickMultiplier = (state.equipment ?? [])
.filter((e) => e.equipped && e.bonus.clickMultiplier != null)
const equippedItems = (state.equipment ?? []).filter((e) => e.equipped);
const equipmentClickMultiplier = equippedItems
.filter((e) => e.bonus.clickMultiplier != null)
.reduce((mult, e) => mult * (e.bonus.clickMultiplier ?? 1), 1);
const setClickMultiplier = computeSetBonuses(equippedItems.map((e) => e.id), EQUIPMENT_SETS).clickMultiplier;
const runestonesClick = state.prestige.runestonesClickMultiplier ?? 1;
@@ -282,6 +288,7 @@ export const calculateClickPower = (state: GameState): number => {
clickMultiplier *
state.prestige.productionMultiplier *
runestonesClick *
equipmentClickMultiplier
equipmentClickMultiplier *
setClickMultiplier
);
};