feat(web): model HP regen fields and map CHARACTER_TOO_WOUNDED

- Add hpRegenPerSecond and hpRegenSince fields to CharacterResponse
- Update inventory-page.component.spec.ts fixture with new fields
- Add CHARACTER_TOO_WOUNDED error mapping to combat.store
- Add test for CHARACTER_TOO_WOUNDED error mapping
- Update app.spec.ts and world.store.spec.ts fixtures for compilation

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Bastian Wagner
2026-08-21 15:59:32 +02:00
parent caf63b34a4
commit c27b6c5026
6 changed files with 25 additions and 0 deletions

View File

@@ -158,6 +158,8 @@ describe('App', () => {
currentHp: 52, currentHp: 52,
maxHp: 80, maxHp: 80,
attack: 12, attack: 12,
hpRegenPerSecond: 1,
hpRegenSince: null,
currentLocation: { id: 'location-id', key: 'south-gate', name: 'Südtor von Graufurt' }, currentLocation: { id: 'location-id', key: 'south-gate', name: 'Südtor von Graufurt' },
}); });
const fixture = TestBed.createComponent(AppShellComponent); const fixture = TestBed.createComponent(AppShellComponent);

View File

@@ -13,6 +13,8 @@ export interface CharacterResponse {
currentHp: number; currentHp: number;
maxHp: number; maxHp: number;
attack: number; attack: number;
hpRegenPerSecond: number;
hpRegenSince: string | null;
currentLocation: LocationSummary; currentLocation: LocationSummary;
} }

View File

@@ -84,6 +84,22 @@ describe('CombatStore', () => {
expect(store.errorCode()).toBe('COMBAT_ALREADY_ACTIVE'); expect(store.errorCode()).toBe('COMBAT_ALREADY_ACTIVE');
}); });
it('maps CHARACTER_TOO_WOUNDED to its German message', async () => {
api.startCombat.mockReturnValue(
throwError(
() =>
new HttpErrorResponse({
status: 409,
error: { statusCode: 409, code: 'CHARACTER_TOO_WOUNDED', message: 'Too wounded.' },
}),
),
);
await store.startCombat('encounter-1');
expect(store.error()).toBe('Du bist zu schwer verwundet, um zu kämpfen. Warte, bis du dich erholt hast.');
});
it('loads the running combat and clears the error that sent us looking for it', async () => { it('loads the running combat and clears the error that sent us looking for it', async () => {
api.startCombat.mockReturnValue( api.startCombat.mockReturnValue(
throwError( throwError(

View File

@@ -13,6 +13,7 @@ const COMBAT_ERROR_MESSAGES: Readonly<Record<string, string>> = {
HUNT_ENCOUNTER_ALREADY_CONSUMED: 'Diese Begegnung wurde bereits genutzt.', HUNT_ENCOUNTER_ALREADY_CONSUMED: 'Diese Begegnung wurde bereits genutzt.',
INVALID_HUNT_ENCOUNTER: 'Diese Begegnung ist nicht mehr gültig.', INVALID_HUNT_ENCOUNTER: 'Diese Begegnung ist nicht mehr gültig.',
CHARACTER_TRAVELLING: 'Du kannst nicht kämpfen, während du unterwegs bist.', CHARACTER_TRAVELLING: 'Du kannst nicht kämpfen, während du unterwegs bist.',
CHARACTER_TOO_WOUNDED: 'Du bist zu schwer verwundet, um zu kämpfen. Warte, bis du dich erholt hast.',
COMBAT_ALREADY_ACTIVE: 'Du befindest dich bereits in einem Kampf.', COMBAT_ALREADY_ACTIVE: 'Du befindest dich bereits in einem Kampf.',
COMBAT_NOT_FOUND: 'Dieser Kampf wurde nicht gefunden.', COMBAT_NOT_FOUND: 'Dieser Kampf wurde nicht gefunden.',
COMBAT_ALREADY_FINISHED: 'Dieser Kampf ist bereits beendet.', COMBAT_ALREADY_FINISHED: 'Dieser Kampf ist bereits beendet.',

View File

@@ -71,6 +71,8 @@ const character: CharacterResponse = {
currentHp: 100, currentHp: 100,
maxHp: 100, maxHp: 100,
attack: 6, attack: 6,
hpRegenPerSecond: 1,
hpRegenSince: null,
currentLocation: { id: 'loc-1', key: 'south-gate', name: 'Südtor' }, currentLocation: { id: 'loc-1', key: 'south-gate', name: 'Südtor' },
}; };

View File

@@ -20,6 +20,8 @@ const character: CharacterResponse = {
currentHp: 100, currentHp: 100,
maxHp: 100, maxHp: 100,
attack: 6, attack: 6,
hpRegenPerSecond: 1,
hpRegenSince: null,
currentLocation: { id: 'origin-id', key: 'south-gate', name: 'Südtor' }, currentLocation: { id: 'origin-id', key: 'south-gate', name: 'Südtor' },
}; };