feat: implement DoT/debuff runtime system (spec §6, AC-12, AC-13)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m18s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m18s
- Add ActiveEffect, EffectType types to game.ts; activeEffects + effectiveArmor on EnemyState - Add SpellOnHitEffect + onHitEffect field to SpellDefinition - Wire onHitEffect to fire (burn), death (curse), lightning (armor_corrode), frost (freeze), soul (bypassArmor burn) - Add applyOnHitEffect() — applies on-hit effect on successful spell hit (spec §6.2) - Add processDoTPhase() — ticks all active effects after weapon/golem attacks (spec §6.3) - Add bypassArmor/bypassBarrier support in applyEnemyDefenses() (AC-13) - Export standalone applyEnemyDefenses from combat-tick.ts for DoT pipeline - Split DoT runtime into separate dot-runtime.ts (135 lines) to keep combat-actions.ts under 400 lines - Update all enemy generation sites with activeEffects/effectiveArmor defaults - Fix test helpers for new required fields All 921 tests pass (45 test files)
This commit is contained in:
@@ -5,8 +5,10 @@
|
||||
import { SPELLS_DEF, HOURS_PER_TICK } from '../constants';
|
||||
import { getGuardianForFloor } from '../data/guardian-encounters';
|
||||
import type { CombatStore, CombatState } from './combat-state.types';
|
||||
import type { SpellState } from '../types';
|
||||
import type { SpellState, EnemyState } from '../types';
|
||||
import { applyOnHitEffect, processDoTPhase } from './dot-runtime';
|
||||
import type { ActiveGolem } from '../types';
|
||||
import type { SpellOnHitEffect } from '../types/spells';
|
||||
import { getFloorMaxHP, getFloorElement, getMultiElementBonus, calcDamage, canAffordSpellCost, deductSpellCost } from '../utils';
|
||||
import { computeDisciplineEffects } from '../effects/discipline-effects';
|
||||
import { processGolemMaintenance, processGolemAttacks } from './golem-combat-actions';
|
||||
@@ -65,6 +67,14 @@ export function processCombatTick(
|
||||
signedPacts: number[],
|
||||
golemancyState: { activeGolems: ActiveGolem[] },
|
||||
golemApplyDamageToRoom: (dmg: number) => { floorHP: number; floorMaxHP: number; roomCleared: boolean },
|
||||
applyEnemyDefenses: (
|
||||
dmg: number,
|
||||
enemy: EnemyState | null,
|
||||
roomType: string,
|
||||
addLog: (msg: string) => void,
|
||||
bypassArmor?: boolean,
|
||||
bypassBarrier?: boolean,
|
||||
) => number,
|
||||
): CombatTickResult {
|
||||
const state = get();
|
||||
const logMessages: string[] = [];
|
||||
@@ -177,6 +187,9 @@ export function processCombatTick(
|
||||
castProgress -= 1;
|
||||
safetyCounter++;
|
||||
|
||||
// Apply on-hit effect (DoT/debuff) to enemy (spec §6.2)
|
||||
applyOnHitEffect(get, set, spellId, logMessages);
|
||||
|
||||
// Check if room/floor is cleared
|
||||
if (floorHP <= 0) {
|
||||
const guardian = getGuardianForFloor(currentFloor);
|
||||
@@ -302,6 +315,24 @@ export function processCombatTick(
|
||||
currentFloor = postGolemState.currentFloor;
|
||||
}
|
||||
|
||||
// ─── DoT/Debuff tick processing (spec §6.3) ──────────────────────────
|
||||
// Process after all weapon/golem attacks
|
||||
if (floorHP > 0) {
|
||||
const doTDamage = processDoTPhase(get, set, applyEnemyDefenses, logMessages);
|
||||
floorHP = Math.max(0, floorHP - doTDamage);
|
||||
|
||||
// Check if DoT cleared the room
|
||||
if (floorHP <= 0) {
|
||||
const guardian = getGuardianForFloor(currentFloor);
|
||||
onFloorCleared(currentFloor, !!guardian);
|
||||
get().advanceRoomOrFloor();
|
||||
const newState = get();
|
||||
currentFloor = newState.currentFloor;
|
||||
floorMaxHP = newState.floorMaxHP;
|
||||
floorHP = newState.floorHP;
|
||||
}
|
||||
}
|
||||
|
||||
const newMaxFloorReached = Math.max(state.maxFloorReached, currentFloor);
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user