diff --git a/apps/web/src/app/features/combat/combat-page/combat-page.component.html b/apps/web/src/app/features/combat/combat-page/combat-page.component.html index 7453aff..12aee40 100644 --- a/apps/web/src/app/features/combat/combat-page/combat-page.component.html +++ b/apps/web/src/app/features/combat/combat-page/combat-page.component.html @@ -1,4 +1,4 @@ -
+
@if (combat(); as combat) {
@@ -9,7 +9,7 @@
-

Runde {{ combat.round }}

+

Round {{ combat.round }}

@@ -31,7 +31,7 @@
- Angriff + Attack 1 } @@ -132,24 +132,24 @@ @if (combat.status === 'WON') {
-

Sieg

-

{{ combat.monster.name }} wurde besiegt.

+

Victory

+

{{ combat.monster.name }} has been defeated.

@if (combat.rewards; as rewards) { -
-

Belohnungen

+
+

Rewards

@if (rewards.silver) {
-
Silber
+
Silver
+{{ rewards.silver }}
} @if (rewards.items.length) { -

Beute

+

Loot

    @for (reward of rewards.items; track reward.characterItemId) {
  • @@ -158,14 +158,14 @@ }
} @else { -

Keine besondere Beute gefunden.

+

No notable loot found.

}
}
} @else if (combat.status === 'LOST') {
-

Niederlage

-

{{ combat.player.name }} wurde im Kampf besiegt.

+

Defeat

+

{{ combat.player.name }} has been defeated.

}
-
diff --git a/apps/web/src/app/features/combat/combat-page/combat-page.component.spec.ts b/apps/web/src/app/features/combat/combat-page/combat-page.component.spec.ts index 3752029..3786b3e 100644 --- a/apps/web/src/app/features/combat/combat-page/combat-page.component.spec.ts +++ b/apps/web/src/app/features/combat/combat-page/combat-page.component.spec.ts @@ -28,7 +28,7 @@ const activeCombat: Combat = { rewards: null, }; -const monsterHitLine = 'Aschenratte trifft Aric Duskwalker für 5 Schaden.'; +const monsterHitLine = 'Aschenratte hits Aric Duskwalker for 5 damage.'; function countOccurrences(haystack: string | null, needle: string): number { return haystack ? haystack.split(needle).length - 1 : 0; @@ -91,7 +91,7 @@ describe('CombatPageComponent', () => { expect(combatStore.loadCombat).toHaveBeenCalledWith('combat-1'); }); - it('shows the player, monster, HP bars, round, and the Angriff action', async () => { + it('shows the player, monster, HP bars, round, and the Attack action', async () => { const fixture = await setup(activeCombat); const element = fixture.nativeElement as HTMLElement; @@ -99,22 +99,22 @@ describe('CombatPageComponent', () => { expect(element.textContent).toContain('95 / 100'); expect(element.textContent).toContain('Aschenratte'); expect(element.textContent).toContain('31 / 45'); - expect(element.querySelector('[data-combat-round]')?.textContent).toContain('Runde 2'); + expect(element.querySelector('[data-combat-round]')?.textContent).toContain('Round 2'); expect(element.querySelector('[data-combat-attack]')).toBeTruthy(); }); - it('shows all five combat actions with their German labels', async () => { + it('shows all five combat actions with their English labels', async () => { const fixture = await setup(activeCombat); const element = fixture.nativeElement as HTMLElement; - expect(element.querySelector('[data-combat-attack]')?.textContent).toContain('Angriff'); - expect(element.querySelector('[data-combat-heavy-strike]')?.textContent).toContain('Schwerer Hieb'); - expect(element.querySelector('[data-combat-shield-bash]')?.textContent).toContain('Schildstoß'); - expect(element.querySelector('[data-combat-defend]')?.textContent).toContain('Verteidigen'); - expect(element.querySelector('[data-combat-potion]')?.textContent).toContain('Trank 2/2'); + expect(element.querySelector('[data-combat-attack]')?.textContent).toContain('Attack'); + expect(element.querySelector('[data-combat-heavy-strike]')?.textContent).toContain('Heavy Strike'); + expect(element.querySelector('[data-combat-shield-bash]')?.textContent).toContain('Shield Bash'); + expect(element.querySelector('[data-combat-defend]')?.textContent).toContain('Defend'); + expect(element.querySelector('[data-combat-potion]')?.textContent).toContain('Potion 2/2'); }); - it('sends HEAVY_STRIKE when Schwerer Hieb is clicked', async () => { + it('sends HEAVY_STRIKE when Heavy Strike is clicked', async () => { const fixture = await setup(activeCombat); const element = fixture.nativeElement as HTMLElement; @@ -123,7 +123,7 @@ describe('CombatPageComponent', () => { expect(combatStore.performAction).toHaveBeenCalledWith('HEAVY_STRIKE'); }); - it('sends SHIELD_BASH when Schildstoß is clicked', async () => { + it('sends SHIELD_BASH when Shield Bash is clicked', async () => { const fixture = await setup(activeCombat); const element = fixture.nativeElement as HTMLElement; @@ -132,7 +132,7 @@ describe('CombatPageComponent', () => { expect(combatStore.performAction).toHaveBeenCalledWith('SHIELD_BASH'); }); - it('sends DEFEND when Verteidigen is clicked', async () => { + it('sends DEFEND when Defend is clicked', async () => { const fixture = await setup(activeCombat); const element = fixture.nativeElement as HTMLElement; @@ -141,7 +141,7 @@ describe('CombatPageComponent', () => { expect(combatStore.performAction).toHaveBeenCalledWith('DEFEND'); }); - it('sends POTION when Trank is clicked', async () => { + it('sends POTION when Potion is clicked', async () => { const fixture = await setup(activeCombat); const element = fixture.nativeElement as HTMLElement; @@ -169,7 +169,7 @@ describe('CombatPageComponent', () => { const element = fixture.nativeElement as HTMLElement; expect(element.querySelector('[data-combat-telegraph]')?.textContent).toContain( - 'Aschenratte bereitet Schweren Hieb vor.', + 'Aschenratte is winding up a Heavy Strike.', ); }); @@ -192,21 +192,21 @@ describe('CombatPageComponent', () => { }); const element = fixture.nativeElement as HTMLElement; - expect(element.textContent).toContain('Aric Duskwalker trinkt einen Trank und heilt 35 Lebenspunkte.'); - expect(element.textContent).toContain('Aric Duskwalker geht in die Verteidigung.'); - expect(element.textContent).toContain('Aschenratte bereitet Schweren Hieb vor.'); - expect(element.textContent).toContain('Aric Duskwalker unterbricht den vorbereiteten Angriff von Aschenratte.'); + expect(element.textContent).toContain('Aric Duskwalker drinks a potion and heals 35 HP.'); + expect(element.textContent).toContain('Aric Duskwalker braces to defend.'); + expect(element.textContent).toContain('Aschenratte is winding up a Heavy Strike.'); + expect(element.textContent).toContain("Aric Duskwalker interrupts Aschenratte's attack."); }); - it('renders the structured events as readable German combat-log entries', async () => { + it('renders the structured events as readable English combat-log entries', async () => { const fixture = await setup(activeCombat); const element = fixture.nativeElement as HTMLElement; - expect(element.textContent).toContain('Aric Duskwalker trifft Aschenratte für 14 Schaden.'); - expect(element.textContent).toContain('Aschenratte trifft Aric Duskwalker für 5 Schaden.'); + expect(element.textContent).toContain('Aric Duskwalker hits Aschenratte for 14 damage.'); + expect(element.textContent).toContain('Aschenratte hits Aric Duskwalker for 5 damage.'); }); - it('calls combatStore.performAction("ATTACK") when Angriff is clicked', async () => { + it('calls combatStore.performAction("ATTACK") when Attack is clicked', async () => { const fixture = await setup(activeCombat); const element = fixture.nativeElement as HTMLElement; @@ -303,7 +303,7 @@ describe('CombatPageComponent', () => { await vi.advanceTimersByTimeAsync(1260); fixture.detectChanges(); - expect(element.querySelector('[data-combat-telegraph]')?.textContent).toContain('bereitet Schweren Hieb vor'); + expect(element.querySelector('[data-combat-telegraph]')?.textContent).toContain('is winding up a Heavy Strike'); expect(monster?.classList.contains('sprite--lunge')).toBe(false); }); @@ -334,7 +334,7 @@ describe('CombatPageComponent', () => { await vi.advanceTimersByTimeAsync(540); fixture.detectChanges(); - expect(element.textContent).toContain('Aric Duskwalker unterbricht den vorbereiteten Angriff von Aschenratte.'); + expect(element.textContent).toContain("Aric Duskwalker interrupts Aschenratte's attack."); expect(element.querySelector('[data-combat-telegraph]')).toBeNull(); expect(monster?.classList.contains('sprite--lunge')).toBe(false); }); @@ -365,7 +365,7 @@ describe('CombatPageComponent', () => { // Before the checkpoint: the pre-heal HP and potion count still show. expect(element.textContent).toContain('70 / 100'); - expect(element.querySelector('[data-combat-potion]')?.textContent).toContain('Trank 2/2'); + expect(element.querySelector('[data-combat-potion]')?.textContent).toContain('Potion 2/2'); // First checkpoint: the heal already landed from the player's own action, // so the HP bar and potion count update here -- well before the monster's @@ -373,8 +373,8 @@ describe('CombatPageComponent', () => { await vi.advanceTimersByTimeAsync(540); fixture.detectChanges(); expect(element.textContent).toContain('85 / 100'); - expect(element.querySelector('[data-combat-potion]')?.textContent).toContain('Trank 1/2'); - expect(element.textContent).toContain('Aric Duskwalker trinkt einen Trank und heilt 15 Lebenspunkte.'); + expect(element.querySelector('[data-combat-potion]')?.textContent).toContain('Potion 1/2'); + expect(element.textContent).toContain('Aric Duskwalker drinks a potion and heals 15 HP.'); // The monster's reply is still held back at this point. expect(countOccurrences(element.textContent, monsterHitLine)).toBe(1); @@ -444,7 +444,7 @@ describe('CombatPageComponent', () => { expect(worldStore.refreshCharacter).toHaveBeenCalledOnce(); }); - it('disables Angriff while an action is pending', async () => { + it('disables Attack while an action is pending', async () => { const fixture = await setup(activeCombat); combatStore.actionPending.set(true); fixture.detectChanges(); @@ -453,21 +453,21 @@ describe('CombatPageComponent', () => { expect(element.querySelector('[data-combat-attack]')?.disabled).toBe(true); }); - it('shows the victory state and hides Angriff when the combat is WON', async () => { + it('shows the victory state and hides Attack when the combat is WON', async () => { const fixture = await setup({ ...activeCombat, status: 'WON' }); const element = fixture.nativeElement as HTMLElement; expect(element.querySelector('[data-combat-result="WON"]')).toBeTruthy(); - expect(element.textContent).toContain('Sieg'); + expect(element.textContent).toContain('Victory'); expect(element.querySelector('[data-combat-attack]')).toBeNull(); }); - it('shows the defeat state and hides Angriff when the combat is LOST', async () => { + it('shows the defeat state and hides Attack when the combat is LOST', async () => { const fixture = await setup({ ...activeCombat, status: 'LOST' }); const element = fixture.nativeElement as HTMLElement; expect(element.querySelector('[data-combat-result="LOST"]')).toBeTruthy(); - expect(element.textContent).toContain('Niederlage'); + expect(element.textContent).toContain('Defeat'); expect(element.querySelector('[data-combat-attack]')).toBeNull(); }); @@ -542,7 +542,7 @@ describe('CombatPageComponent', () => { it('hides the silver tile entirely when the kill granted none', async () => { // Every monster seeded in this slice rolls silverMin/silverMax = 0 (design - // R7), so without this guard the victory screen advertises "Silber +0" + // R7), so without this guard the victory screen advertises "Silver +0" // after every single fight in the shipped content. const fixture = await setup({ ...wonWithRewards, @@ -589,7 +589,7 @@ describe('CombatPageComponent', () => { const element = fixture.nativeElement as HTMLElement; expect(element.querySelector('[data-reward-empty]')?.textContent).toContain( - 'Keine besondere Beute gefunden.', + 'No notable loot found.', ); expect(element.querySelector('[role="alert"]')).toBeNull(); }); diff --git a/apps/web/src/app/features/combat/combat-page/combat-page.component.ts b/apps/web/src/app/features/combat/combat-page/combat-page.component.ts index a30fe7c..0f8b118 100644 --- a/apps/web/src/app/features/combat/combat-page/combat-page.component.ts +++ b/apps/web/src/app/features/combat/combat-page/combat-page.component.ts @@ -196,7 +196,7 @@ export class CombatPageComponent implements OnInit { } // The location is the screen a fight resolves back into. It sits beside - // "Weiter jagen" rather than replacing it, so the hunt loop keeps its + // "Keep Hunting" rather than replacing it, so the hunt loop keeps its // one-click rhythm. protected goToLocation(): void { void this.router.navigate(['/location']); @@ -233,7 +233,7 @@ export class CombatPageComponent implements OnInit { if (!combat || combat.monster.pendingIntent !== 'HEAVY_ATTACK') { return null; } - return `${combat.monster.name} bereitet Schweren Hieb vor.`; + return `${combat.monster.name} is winding up a Heavy Strike.`; } protected logRounds(): CombatLogRound[] { @@ -254,36 +254,36 @@ export class CombatPageComponent implements OnInit { protected formatEvent(event: CombatEvent): string { const combat = this.displayed(); - const playerName = combat?.player.name ?? 'Du'; - const monsterName = combat?.monster.name ?? 'Der Gegner'; + const playerName = combat?.player.name ?? 'You'; + const monsterName = combat?.monster.name ?? 'The enemy'; if (event.type === 'DAMAGE') { const attacker = event.source === 'PLAYER' ? playerName : monsterName; const defender = event.target === 'PLAYER' ? playerName : monsterName; - return `${attacker} trifft ${defender} für ${event.amount} Schaden.`; + return `${attacker} hits ${defender} for ${event.amount} damage.`; } if (event.type === 'HEAL') { - return `${playerName} trinkt einen Trank und heilt ${event.amount} Lebenspunkte.`; + return `${playerName} drinks a potion and heals ${event.amount} HP.`; } if (event.type === 'DEFEND') { - return `${playerName} geht in die Verteidigung.`; + return `${playerName} braces to defend.`; } if (event.type === 'TELEGRAPH') { - return `${monsterName} bereitet Schweren Hieb vor.`; + return `${monsterName} is winding up a Heavy Strike.`; } if (event.type === 'INTERRUPT') { - return `${playerName} unterbricht den vorbereiteten Angriff von ${monsterName}.`; + return `${playerName} interrupts ${monsterName}'s attack.`; } if (event.type === 'COMBAT_WON') { - return `${monsterName} wurde besiegt.`; + return `${monsterName} has been defeated.`; } - return `${playerName} wurde im Kampf besiegt.`; + return `${playerName} has been defeated.`; } private wait(ms: number): Promise {