generated from nhcarrigan/template
Compare commits
3 Commits
d1559c327f
...
8a332dc9ce
| Author | SHA1 | Date | |
|---|---|---|---|
|
8a332dc9ce
|
|||
|
56d963dc90
|
|||
|
77c7ee02a6
|
@@ -628,7 +628,7 @@ export const defaultBosses: Array<Boss> = [
|
||||
name: "The Prism Golem",
|
||||
prestigeRequirement: 3,
|
||||
status: "locked",
|
||||
upgradeRewards: [],
|
||||
upgradeRewards: [ "crystal_sage_1" ],
|
||||
zoneId: "crystalline_spire",
|
||||
},
|
||||
{
|
||||
@@ -664,7 +664,7 @@ export const defaultBosses: Array<Boss> = [
|
||||
name: "The Faceted",
|
||||
prestigeRequirement: 4,
|
||||
status: "locked",
|
||||
upgradeRewards: [],
|
||||
upgradeRewards: [ "void_sentinel_1" ],
|
||||
zoneId: "crystalline_spire",
|
||||
},
|
||||
{
|
||||
@@ -682,7 +682,7 @@ export const defaultBosses: Array<Boss> = [
|
||||
name: "The Diamond Colossus",
|
||||
prestigeRequirement: 4,
|
||||
status: "locked",
|
||||
upgradeRewards: [],
|
||||
upgradeRewards: [ "eternal_champion_1" ],
|
||||
zoneId: "crystalline_spire",
|
||||
},
|
||||
{
|
||||
@@ -700,7 +700,7 @@ export const defaultBosses: Array<Boss> = [
|
||||
name: "The Crystal Sovereign",
|
||||
prestigeRequirement: 4,
|
||||
status: "locked",
|
||||
upgradeRewards: [],
|
||||
upgradeRewards: [ "cosmos_knight_1" ],
|
||||
zoneId: "crystalline_spire",
|
||||
},
|
||||
// ── Void Sanctum ──────────────────────────────────────────────────────────
|
||||
@@ -719,7 +719,7 @@ export const defaultBosses: Array<Boss> = [
|
||||
name: "The Void Herald",
|
||||
prestigeRequirement: 4,
|
||||
status: "locked",
|
||||
upgradeRewards: [],
|
||||
upgradeRewards: [ "seraph_knight_1" ],
|
||||
zoneId: "void_sanctum",
|
||||
},
|
||||
{
|
||||
@@ -755,7 +755,7 @@ export const defaultBosses: Array<Boss> = [
|
||||
name: "The Unmaker",
|
||||
prestigeRequirement: 5,
|
||||
status: "locked",
|
||||
upgradeRewards: [],
|
||||
upgradeRewards: [ "abyss_diver_1" ],
|
||||
zoneId: "void_sanctum",
|
||||
},
|
||||
{
|
||||
@@ -791,7 +791,7 @@ export const defaultBosses: Array<Boss> = [
|
||||
name: "The Void Emperor",
|
||||
prestigeRequirement: 5,
|
||||
status: "locked",
|
||||
upgradeRewards: [],
|
||||
upgradeRewards: [ "infernal_warden_1" ],
|
||||
zoneId: "void_sanctum",
|
||||
},
|
||||
// ── Eternal Throne ────────────────────────────────────────────────────────
|
||||
@@ -810,7 +810,7 @@ export const defaultBosses: Array<Boss> = [
|
||||
name: "The Throne Warden",
|
||||
prestigeRequirement: 5,
|
||||
status: "locked",
|
||||
upgradeRewards: [],
|
||||
upgradeRewards: [ "infinity_ranger_1" ],
|
||||
zoneId: "eternal_throne",
|
||||
},
|
||||
{
|
||||
@@ -846,7 +846,7 @@ export const defaultBosses: Array<Boss> = [
|
||||
name: "The Undying",
|
||||
prestigeRequirement: 5,
|
||||
status: "locked",
|
||||
upgradeRewards: [],
|
||||
upgradeRewards: [ "reality_warden_1" ],
|
||||
zoneId: "eternal_throne",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
/* eslint-disable complexity -- Complex component with many render paths */
|
||||
import { type JSX, useState } from "react";
|
||||
import { useGame } from "../../context/gameContext.js";
|
||||
import { computeEffectiveAdventurerStats } from "../../engine/tick.js";
|
||||
import { cdnImage } from "../../utils/cdn.js";
|
||||
import { LockToggle } from "../ui/lockToggle.js";
|
||||
import type { Adventurer } from "@elysium/types";
|
||||
@@ -76,12 +77,19 @@ const computeMaxAffordable = (adventurer: Adventurer, gold: number): number => {
|
||||
return quantity;
|
||||
};
|
||||
|
||||
interface EffectiveAdventurerStats {
|
||||
readonly combatPower: number;
|
||||
readonly essencePerSecond: number;
|
||||
readonly goldPerSecond: number;
|
||||
}
|
||||
|
||||
interface AdventurerCardProperties {
|
||||
readonly adventurer: Adventurer;
|
||||
readonly currentGold: number;
|
||||
readonly batchSize: BatchSize;
|
||||
readonly unlockHint: string | undefined;
|
||||
readonly formatNumber: (n: number)=> string;
|
||||
readonly effectiveStats: EffectiveAdventurerStats;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,6 +100,7 @@ interface AdventurerCardProperties {
|
||||
* @param props.batchSize - The selected batch size.
|
||||
* @param props.unlockHint - Optional quest name that unlocks this adventurer.
|
||||
* @param props.formatNumber - The number formatting utility function.
|
||||
* @param props.effectiveStats - The post-multiplier per-unit stats.
|
||||
* @returns The JSX element.
|
||||
*/
|
||||
const AdventurerCard = ({
|
||||
@@ -100,6 +109,7 @@ const AdventurerCard = ({
|
||||
batchSize,
|
||||
unlockHint,
|
||||
formatNumber,
|
||||
effectiveStats,
|
||||
}: AdventurerCardProperties): JSX.Element => {
|
||||
const { buyAdventurer } = useGame();
|
||||
|
||||
@@ -134,17 +144,17 @@ const AdventurerCard = ({
|
||||
<div className="adventurer-info">
|
||||
<h3>{adventurer.name}</h3>
|
||||
<p>
|
||||
{formatNumber(adventurer.goldPerSecond)}
|
||||
{formatNumber(effectiveStats.goldPerSecond)}
|
||||
{" gold/s each"}
|
||||
</p>
|
||||
{adventurer.essencePerSecond > 0
|
||||
&& <p>
|
||||
{formatNumber(adventurer.essencePerSecond)}
|
||||
{formatNumber(effectiveStats.essencePerSecond)}
|
||||
{" essence/s each"}
|
||||
</p>
|
||||
}
|
||||
<p>
|
||||
{formatNumber(adventurer.combatPower)}
|
||||
{formatNumber(effectiveStats.combatPower)}
|
||||
{" combat power each"}
|
||||
</p>
|
||||
</div>
|
||||
@@ -280,6 +290,10 @@ const AdventurerPanel = (): JSX.Element => {
|
||||
adventurer={adventurer}
|
||||
batchSize={batchSize}
|
||||
currentGold={state.resources.gold}
|
||||
effectiveStats={computeEffectiveAdventurerStats(
|
||||
state,
|
||||
adventurer.id,
|
||||
)}
|
||||
formatNumber={formatNumber}
|
||||
key={adventurer.id}
|
||||
unlockHint={adventurerUnlockHints.get(adventurer.id)}
|
||||
|
||||
+120
-1
@@ -243,10 +243,129 @@ export const computeEssencePerSecond = (state: GameState): number => {
|
||||
return essencePerSecond;
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes the effective per-unit stats for a single adventurer type,
|
||||
* applying all active multipliers (upgrades, prestige, equipment, echo,
|
||||
* crafted, companion). The returned values represent what a single
|
||||
* adventurer of this type currently contributes per second, matching the
|
||||
* per-unit contribution used by computeGoldPerSecond and
|
||||
* computeEssencePerSecond.
|
||||
* @param state - The current game state.
|
||||
* @param adventurerId - The ID of the adventurer to compute stats for.
|
||||
* @returns Effective per-unit goldPerSecond, essencePerSecond, and combatPower.
|
||||
*/
|
||||
export const computeEffectiveAdventurerStats = (
|
||||
state: GameState,
|
||||
adventurerId: string,
|
||||
): { combatPower: number; essencePerSecond: number; goldPerSecond: number } => {
|
||||
const adventurer = state.adventurers.find((a) => {
|
||||
return a.id === adventurerId;
|
||||
});
|
||||
|
||||
/* V8 ignore next 3 -- @preserve */
|
||||
if (adventurer === undefined) {
|
||||
return { combatPower: 0, essencePerSecond: 0, goldPerSecond: 0 };
|
||||
}
|
||||
|
||||
const upgradeMultiplier = state.upgrades.
|
||||
filter((upgrade) => {
|
||||
const isGlobal = upgrade.target === "global";
|
||||
const isThisAdventurer
|
||||
= upgrade.target === "adventurer"
|
||||
&& upgrade.adventurerId === adventurerId;
|
||||
return upgrade.purchased && (isGlobal || isThisAdventurer);
|
||||
}).
|
||||
reduce((mult, upgrade) => {
|
||||
return mult * upgrade.multiplier;
|
||||
}, 1);
|
||||
|
||||
const equippedItems = state.equipment.filter((item) => {
|
||||
return item.equipped;
|
||||
});
|
||||
const equipmentGoldMultiplier = equippedItems.reduce((mult, item) => {
|
||||
return mult * (item.bonus.goldMultiplier ?? 1);
|
||||
}, 1);
|
||||
const equipmentCombatMultiplier = equippedItems.reduce((mult, item) => {
|
||||
return mult * (item.bonus.combatMultiplier ?? 1);
|
||||
}, 1);
|
||||
const equippedItemIds = equippedItems.map((item) => {
|
||||
return item.id;
|
||||
});
|
||||
const setBonuses = computeSetBonuses(equippedItemIds, EQUIPMENT_SETS);
|
||||
|
||||
const runestonesIncome = state.prestige.runestonesIncomeMultiplier ?? 1;
|
||||
const runestonesEssence = state.prestige.runestonesEssenceMultiplier ?? 1;
|
||||
// eslint-disable-next-line stylistic/no-mixed-operators -- prestige count * factor is clear
|
||||
const prestigeCombatMultiplier = 1 + state.prestige.count * 0.1;
|
||||
const echoIncome = state.transcendence?.echoIncomeMultiplier ?? 1;
|
||||
const echoCombatMultiplier = state.transcendence?.echoCombatMultiplier ?? 1;
|
||||
const craftedGoldMultiplier
|
||||
= state.exploration?.craftedGoldMultiplier ?? 1;
|
||||
const craftedEssenceMultiplier
|
||||
= state.exploration?.craftedEssenceMultiplier ?? 1;
|
||||
const craftedCombatMultiplier
|
||||
= state.exploration?.craftedCombatMultiplier ?? 1;
|
||||
|
||||
const companionBonus = getActiveCompanionBonus(
|
||||
state.companions?.activeCompanionId,
|
||||
state.companions?.unlockedCompanionIds ?? [],
|
||||
);
|
||||
const companionGoldMult
|
||||
= companionBonus?.type === "passiveGold"
|
||||
? 1 + companionBonus.value
|
||||
: 1;
|
||||
const companionEssenceMult
|
||||
= companionBonus?.type === "essenceIncome"
|
||||
? 1 + companionBonus.value
|
||||
: 1;
|
||||
const companionCombatMult
|
||||
= companionBonus?.type === "bossDamage"
|
||||
? 1 + companionBonus.value
|
||||
: 1;
|
||||
|
||||
const goldPerSecond
|
||||
= adventurer.goldPerSecond
|
||||
* upgradeMultiplier
|
||||
* state.prestige.productionMultiplier
|
||||
* runestonesIncome
|
||||
* echoIncome
|
||||
* equipmentGoldMultiplier
|
||||
* setBonuses.goldMultiplier
|
||||
* craftedGoldMultiplier
|
||||
* companionGoldMult;
|
||||
|
||||
const essencePerSecond
|
||||
= adventurer.essencePerSecond
|
||||
* upgradeMultiplier
|
||||
* state.prestige.productionMultiplier
|
||||
* runestonesEssence
|
||||
* craftedEssenceMultiplier
|
||||
* companionEssenceMult;
|
||||
|
||||
const combatPower
|
||||
= adventurer.combatPower
|
||||
* upgradeMultiplier
|
||||
* prestigeCombatMultiplier
|
||||
* equipmentCombatMultiplier
|
||||
* setBonuses.combatMultiplier
|
||||
* echoCombatMultiplier
|
||||
* craftedCombatMultiplier
|
||||
* companionCombatMult;
|
||||
|
||||
return { combatPower, essencePerSecond, goldPerSecond };
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes the party's total combat power, applying all active multipliers
|
||||
* (upgrades, prestige, equipment, set bonuses, echo, crafted, companion).
|
||||
* This mirrors the server-side calculatePartyStats in boss.ts.
|
||||
* This mirrors the server-side calculatePartyStats in boss.ts and is the
|
||||
* single source of truth for all combat-power checks in the client:
|
||||
* - Displayed as "Combat Power" in the resource bar
|
||||
* - Displayed as "Party DPS" in the boss panel
|
||||
* - Used to gate quest availability
|
||||
* Note: the active companion's bossDamage bonus is intentionally included
|
||||
* here, as it applies to the full combat power calculation (boss fights and
|
||||
* quest gating alike), matching the server-side behaviour.
|
||||
* @param state - The current game state.
|
||||
* @returns The total party combat power.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user