diff --git a/apps/api/src/combat/combat.service.spec.ts b/apps/api/src/combat/combat.service.spec.ts index 7de0813..d84ece9 100644 --- a/apps/api/src/combat/combat.service.spec.ts +++ b/apps/api/src/combat/combat.service.spec.ts @@ -343,6 +343,8 @@ describe('CombatService', () => { currentHp: 45, artworkPath: '/images/monsters/ash-rat.png', pendingIntent: null, + guardRemainingRounds: null, + enraged: false, }); expect(combat.events).toEqual([]); expect(dataSource.state.combats).toHaveLength(1); @@ -818,6 +820,34 @@ describe('CombatService', () => { }); describe('getCombat', () => { + it('reports an active guard and an enraged monster to the client', async () => { + const state = createState(); + const { service, dataSource } = createService({ state }); + const started = await service.startCombat(CHARACTER_ID, ENCOUNTER_ID); + + dataSource.state.combats[0].monsterState = { + ...dataSource.state.combats[0].monsterState, + activeGuard: { remainingRounds: 2, armorBonus: 10 }, + enraged: true, + }; + + const dto = await service.getCombat(CHARACTER_ID, started.id); + + expect(dto.monster.guardRemainingRounds).toBe(2); + expect(dto.monster.enraged).toBe(true); + }); + + it('reports no guard when the monster is not covering', async () => { + const state = createState(); + const { service, dataSource } = createService({ state }); + const started = await service.startCombat(CHARACTER_ID, ENCOUNTER_ID); + + const dto = await service.getCombat(CHARACTER_ID, started.id); + + expect(dto.monster.guardRemainingRounds).toBeNull(); + expect(dto.monster.enraged).toBe(false); + }); + it('returns the persisted state and ordered events after a refresh', async () => { const context = createService(); const started = await context.service.startCombat( diff --git a/apps/api/src/combat/combat.service.ts b/apps/api/src/combat/combat.service.ts index 1bcedbb..62abf45 100644 --- a/apps/api/src/combat/combat.service.ts +++ b/apps/api/src/combat/combat.service.ts @@ -68,6 +68,9 @@ export interface CombatMonsterDto { currentHp: number; artworkPath: string; pendingIntent: CombatIntent | null; + /** Rounds the monster's raised guard still covers, or null when open. */ + guardRemainingRounds: number | null; + enraged: boolean; } export interface CombatEventDto { @@ -459,6 +462,9 @@ export class CombatService { currentHp: combat.monsterCurrentHp, artworkPath: monster.artworkPath, pendingIntent: combat.monsterState.pendingAction ?? null, + guardRemainingRounds: + combat.monsterState.activeGuard?.remainingRounds ?? null, + enraged: combat.monsterState.enraged ?? false, }, events: events.map((event) => ({ round: event.round,