fix: Apply pact affinity cast speed bonus to all spell casts (AC-13)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m21s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m21s
Previously the pact affinity cast speed bonus was only applied to invocation spells. Per spec §6.2 and §10.2, it should apply to ALL spell casts (active, equipment, and invocation). Changes: - invocation-utils.ts: Added computeAttackSpeedMultFromPactAffinity() helper that combines prestige upgrade + discipline bonus and applies the diminishing returns formula - gameStore.ts: Compute total pact affinity (prestige + discipline) and apply cast speed bonus to attackSpeedMult passed to processCombatTick - combat-actions.ts: Use attackSpeedMult directly for active/equipment spell cast progress (bonus already baked in) - combat-invocation.ts: Use attackSpeedMult directly for invocation cast progress (bonus already baked in), removed redundant pactAffinity parameter
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# Circular Dependencies
|
||||
Generated: 2026-06-13T11:02:39.214Z
|
||||
Generated: 2026-06-13T11:42:23.525Z
|
||||
Found: 4 circular chain(s) — these MUST be fixed before modifying involved files.
|
||||
|
||||
1. 1) data/guardian-encounters.ts > data/guardian-procedural.ts
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"_meta": {
|
||||
"generated": "2026-06-13T11:02:37.078Z",
|
||||
"generated": "2026-06-13T11:42:21.085Z",
|
||||
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
|
||||
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
|
||||
},
|
||||
@@ -555,6 +555,8 @@
|
||||
"data/guardian-encounters.ts",
|
||||
"effects/discipline-effects.ts",
|
||||
"stores/combat-damage.ts",
|
||||
"stores/combat-invocation.ts",
|
||||
"stores/combat-melee.ts",
|
||||
"stores/combat-state.types.ts",
|
||||
"stores/dot-runtime.ts",
|
||||
"stores/golem-combat-actions.ts",
|
||||
@@ -581,6 +583,24 @@
|
||||
"stores/prestigeStore.ts",
|
||||
"utils/spire-utils.ts"
|
||||
],
|
||||
"stores/combat-invocation.ts": [
|
||||
"constants.ts",
|
||||
"data/guardian-encounters.ts",
|
||||
"effects/discipline-effects.ts",
|
||||
"stores/combat-damage.ts",
|
||||
"stores/combat-state.types.ts",
|
||||
"types.ts",
|
||||
"utils/index.ts",
|
||||
"utils/invocation-utils.ts"
|
||||
],
|
||||
"stores/combat-melee.ts": [
|
||||
"constants.ts",
|
||||
"data/guardian-encounters.ts",
|
||||
"stores/combat-damage.ts",
|
||||
"stores/combat-state.types.ts",
|
||||
"types.ts",
|
||||
"utils/index.ts"
|
||||
],
|
||||
"stores/combat-reset.ts": [
|
||||
"stores/combat-actions.ts",
|
||||
"stores/combat-state.types.ts",
|
||||
@@ -589,7 +609,8 @@
|
||||
"utils/spire-utils.ts"
|
||||
],
|
||||
"stores/combat-state.types.ts": [
|
||||
"types.ts"
|
||||
"types.ts",
|
||||
"utils/invocation-utils.ts"
|
||||
],
|
||||
"stores/combatStore.ts": [
|
||||
"data/guardian-encounters.ts",
|
||||
@@ -934,6 +955,13 @@
|
||||
"utils/result.ts",
|
||||
"utils/safe-persist.ts"
|
||||
],
|
||||
"utils/invocation-utils.ts": [
|
||||
"constants/spells.ts",
|
||||
"data/guardian-encounters.ts",
|
||||
"types.ts",
|
||||
"utils/combat-utils.ts",
|
||||
"utils/mana-utils.ts"
|
||||
],
|
||||
"utils/mana-utils.ts": [
|
||||
"constants.ts",
|
||||
"data/attunements.ts",
|
||||
|
||||
@@ -134,6 +134,7 @@ export function processCombatTick(
|
||||
// ─── Spell casting (only when a valid spell is configured) ────────────────
|
||||
if (spellDef) {
|
||||
const disciplineEffects = computeDisciplineEffects();
|
||||
// AC-13: Pact affinity cast speed bonus already applied to attackSpeedMult by gameStore
|
||||
const totalAttackSpeed = attackSpeedMult;
|
||||
const spellCastSpeed = spellDef.castSpeed || 1;
|
||||
const progressPerTick = HOURS_PER_TICK * spellCastSpeed * totalAttackSpeed;
|
||||
@@ -205,7 +206,7 @@ export function processCombatTick(
|
||||
|
||||
const isESpellAoe = !!eSpellDef.isAoe;
|
||||
const eSpellCastSpeed = eSpellDef.castSpeed || 1;
|
||||
const eProgressPerTick = HOURS_PER_TICK * eSpellCastSpeed * attackSpeedMult;
|
||||
const eProgressPerTick = HOURS_PER_TICK * eSpellCastSpeed * totalAttackSpeed;
|
||||
let eCastProgress = (eSpell.castProgress || 0) + eProgressPerTick;
|
||||
|
||||
let eSafetyCounter = 0;
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
selectInvocationSpell,
|
||||
deductInvocationSpellCost,
|
||||
computeChargeFillRate,
|
||||
computeCastSpeedBonus,
|
||||
computeCostMultiplier,
|
||||
computeDrainRateMultiplier,
|
||||
computeDrainPerTick,
|
||||
@@ -172,12 +171,9 @@ function processActiveInvocation(
|
||||
const drainPerTick = computeDrainPerTick(invSpellDef.cost.amount, drainMult);
|
||||
invocationCharge = Math.max(0, invocationCharge - drainPerTick);
|
||||
|
||||
// Accumulate cast progress with pact affinity bonus
|
||||
const pactAffinity = disciplineEffects.bonuses.pactAffinityBonus || 0;
|
||||
const castSpeedBonus = computeCastSpeedBonus(pactAffinity);
|
||||
const effectiveAttackSpeed = attackSpeedMult * (1 + castSpeedBonus);
|
||||
// Cast progress uses attackSpeedMult which already includes pact affinity bonus (AC-12/AC-13)
|
||||
const invCastSpeed = invSpellDef.castSpeed || 1;
|
||||
const invProgressPerTick = HOURS_PER_TICK * invCastSpeed * effectiveAttackSpeed;
|
||||
const invProgressPerTick = HOURS_PER_TICK * invCastSpeed * attackSpeedMult;
|
||||
const newCastProgress = activeInvocation.castProgress + invProgressPerTick;
|
||||
|
||||
if (newCastProgress >= 1 && floorHP > 0) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import type { TickContext, TickWrites } from './tick-pipeline';
|
||||
import type { GameCoordinatorState } from './gameStore.types';
|
||||
import type { EnemyState } from '../types';
|
||||
import { applyEnemyDefenses as applyEnemyDefensesFromPipeline } from './pipelines/combat-tick';
|
||||
import { computeAttackSpeedMultFromPactAffinity } from '../utils/invocation-utils';
|
||||
|
||||
// Track paused conversions already logged to avoid flooding the activity log every tick
|
||||
const loggedPausedConversions = new Set<string>();
|
||||
@@ -289,8 +290,13 @@ export const useGameStore = create<GameCoordinatorStore>()(
|
||||
}));
|
||||
useCombatStore.setState({ equipmentSpellStates });
|
||||
|
||||
// AC-12/AC-13: Pact affinity cast speed bonus (prestige + discipline)
|
||||
const attackSpeedMult = computeAttackSpeedMultFromPactAffinity(
|
||||
ctx.prestige.prestigeUpgrades.pactAffinity || 0,
|
||||
disciplineEffects.bonuses.pactAffinityBonus || 0,
|
||||
);
|
||||
const cr = useCombatStore.getState().processCombatTick(
|
||||
rawMana, elements, maxMana, 1,
|
||||
rawMana, elements, maxMana, attackSpeedMult,
|
||||
combatCbs.onFloorCleared,
|
||||
combatCbs.makeOnDamageDealt(() => rawMana, () => elements, defCtx, addLog),
|
||||
ctx.prestige.signedPacts,
|
||||
|
||||
@@ -198,6 +198,21 @@ export function computeDrainRateMultiplier(disciplineBonuses: DisciplineBonuses)
|
||||
return Math.max(MIN_DRAIN_MULTIPLIER, BASE_DRAIN_MULTIPLIER + reduction);
|
||||
}
|
||||
|
||||
// ─── Pact Affinity Attack Speed ───────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Compute the effective attack speed multiplier from pact affinity.
|
||||
* Combines prestige upgrade + discipline bonus, applies diminishing returns formula.
|
||||
* Used by gameStore to compute the attackSpeedMult passed to processCombatTick.
|
||||
*/
|
||||
export function computeAttackSpeedMultFromPactAffinity(
|
||||
pactAffinityUpgrade: number,
|
||||
pactAffinityBonus: number,
|
||||
): number {
|
||||
const total = pactAffinityUpgrade + pactAffinityBonus;
|
||||
return 1 * (1 + computeCastSpeedBonus(total));
|
||||
}
|
||||
|
||||
// ─── Drain Per Tick ────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user