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
+2
View File
@@ -35,6 +35,8 @@ export type {
EquipmentRarity,
EquipmentType,
} from "./interfaces/Equipment.js";
export type { EquipmentSet, EquipmentSetBonus } from "./interfaces/EquipmentSet.js";
export { computeSetBonuses } from "./interfaces/EquipmentSet.js";
export type { GameState } from "./interfaces/GameState.js";
export type { Player } from "./interfaces/Player.js";
export type { PrestigeData } from "./interfaces/Prestige.js";
+2
View File
@@ -23,4 +23,6 @@ export interface Boss {
prestigeRequirement: number;
/** Zone this boss belongs to */
zoneId: string;
/** One-time runestone bounty awarded on first-ever defeat */
bountyRunestones: number;
}
@@ -24,4 +24,6 @@ export interface Equipment {
equipped: boolean;
/** If set, this item can be purchased directly rather than obtained via boss drops */
cost?: { gold: number; essence: number; crystals: number };
/** Equipment set this item belongs to, if any */
setId?: string;
}
@@ -0,0 +1,44 @@
export interface EquipmentSetBonus {
goldMultiplier?: number;
combatMultiplier?: number;
clickMultiplier?: number;
}
export interface EquipmentSet {
id: string;
name: string;
description: string;
/** Equipment IDs that make up this set */
pieces: string[];
bonuses: {
2: EquipmentSetBonus;
3: EquipmentSetBonus;
};
}
/**
* Given a list of equipped item IDs and a set catalogue, returns the combined
* multiplicative bonuses granted by all active set bonuses.
*/
export const computeSetBonuses = (
equippedItemIds: string[],
sets: EquipmentSet[],
): { goldMultiplier: number; combatMultiplier: number; clickMultiplier: number } => {
let goldMultiplier = 1;
let combatMultiplier = 1;
let clickMultiplier = 1;
for (const set of sets) {
const count = set.pieces.filter((id) => equippedItemIds.includes(id)).length;
for (const threshold of [2, 3] as const) {
if (count >= threshold) {
const bonus = set.bonuses[threshold];
goldMultiplier *= bonus.goldMultiplier ?? 1;
combatMultiplier *= bonus.combatMultiplier ?? 1;
clickMultiplier *= bonus.clickMultiplier ?? 1;
}
}
}
return { goldMultiplier, combatMultiplier, clickMultiplier };
};
+1 -1
View File
@@ -5,5 +5,5 @@
"rootDir": ".",
"declaration": true
},
"exclude": ["test/**/*.ts"]
"exclude": ["test/**/*.ts", "prod/**"]
}