feat(web): translate combat log, action buttons, and victory/defeat screen to English

Translates formatEvent() and monsterIntentLabel() combat-log templates,
default participant-name fallbacks (Du/Der Gegner -> You/The enemy), and
every static string in the combat page template: round header, action
buttons, outcome headings, rewards panel, post-combat buttons, log panel,
and loading/error/aria-label text.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACkMEDYiwtcfchqKkiUJNX
This commit is contained in:
Bastian Wagner
2026-08-21 21:40:04 +02:00
parent 5586e6c672
commit 0923fc0a23
3 changed files with 73 additions and 73 deletions

View File

@@ -1,4 +1,4 @@
<section class="combat" aria-label="Kampf"> <section class="combat" aria-label="Combat">
@if (combat(); as combat) { @if (combat(); as combat) {
<div class="combat__stage" [class.combat__stage--shaken]="stageShake()"> <div class="combat__stage" [class.combat__stage--shaken]="stageShake()">
<header class="combat__status"> <header class="combat__status">
@@ -9,7 +9,7 @@
<div <div
class="bar bar--player" class="bar bar--player"
role="progressbar" role="progressbar"
[attr.aria-label]="'Lebenspunkte ' + combat.player.name" [attr.aria-label]="'Health points ' + combat.player.name"
[attr.aria-valuenow]="combat.player.currentHp" [attr.aria-valuenow]="combat.player.currentHp"
[attr.aria-valuemin]="0" [attr.aria-valuemin]="0"
[attr.aria-valuemax]="combat.player.maxHp" [attr.aria-valuemax]="combat.player.maxHp"
@@ -20,7 +20,7 @@
</div> </div>
</div> </div>
<p class="combat__round" data-combat-round>Runde {{ combat.round }}</p> <p class="combat__round" data-combat-round>Round {{ combat.round }}</p>
<div class="fighter fighter--monster"> <div class="fighter fighter--monster">
<div class="fighter__meter"> <div class="fighter__meter">
@@ -31,7 +31,7 @@
<div <div
class="bar bar--monster" class="bar bar--monster"
role="progressbar" role="progressbar"
[attr.aria-label]="'Lebenspunkte ' + combat.monster.name" [attr.aria-label]="'Health points ' + combat.monster.name"
[attr.aria-valuenow]="combat.monster.currentHp" [attr.aria-valuenow]="combat.monster.currentHp"
[attr.aria-valuemin]="0" [attr.aria-valuemin]="0"
[attr.aria-valuemax]="combat.monster.maxHp" [attr.aria-valuemax]="combat.monster.maxHp"
@@ -80,7 +80,7 @@
(click)="performAction('ATTACK')" (click)="performAction('ATTACK')"
> >
<img class="action__icon" src="/images/hud/runtime/AttackIcon-96.png" alt="" /> <img class="action__icon" src="/images/hud/runtime/AttackIcon-96.png" alt="" />
<span class="action__label">Angriff</span> <span class="action__label">Attack</span>
<span class="action__key">1</span> <span class="action__key">1</span>
</button> </button>
<button <button
@@ -91,7 +91,7 @@
(click)="performAction('HEAVY_STRIKE')" (click)="performAction('HEAVY_STRIKE')"
> >
<img class="action__icon" src="/images/hud/runtime/AttackIcon-96.png" alt="" /> <img class="action__icon" src="/images/hud/runtime/AttackIcon-96.png" alt="" />
<span class="action__label">Schwerer Hieb</span> <span class="action__label">Heavy Strike</span>
<span class="action__key">2</span> <span class="action__key">2</span>
</button> </button>
<button <button
@@ -102,7 +102,7 @@
(click)="performAction('SHIELD_BASH')" (click)="performAction('SHIELD_BASH')"
> >
<img class="action__icon" src="/images/hud/runtime/CharacterIcon-128.png" alt="" /> <img class="action__icon" src="/images/hud/runtime/CharacterIcon-128.png" alt="" />
<span class="action__label">Schildstoß</span> <span class="action__label">Shield Bash</span>
<span class="action__key">3</span> <span class="action__key">3</span>
</button> </button>
<button <button
@@ -113,7 +113,7 @@
(click)="performAction('DEFEND')" (click)="performAction('DEFEND')"
> >
<img class="action__icon" src="/images/hud/runtime/CharacterIcon-128.png" alt="" /> <img class="action__icon" src="/images/hud/runtime/CharacterIcon-128.png" alt="" />
<span class="action__label">Verteidigen</span> <span class="action__label">Defend</span>
<span class="action__key">4</span> <span class="action__key">4</span>
</button> </button>
<button <button
@@ -124,7 +124,7 @@
(click)="performAction('POTION')" (click)="performAction('POTION')"
> >
<img class="action__icon" src="/images/items/small-healing-potion.png" alt="" /> <img class="action__icon" src="/images/items/small-healing-potion.png" alt="" />
<span class="action__label">Trank {{ combat.player.potionsRemaining }}/{{ combat.player.potionsMax }}</span> <span class="action__label">Potion {{ combat.player.potionsRemaining }}/{{ combat.player.potionsMax }}</span>
<span class="action__key">5</span> <span class="action__key">5</span>
</button> </button>
} }
@@ -132,24 +132,24 @@
@if (combat.status === 'WON') { @if (combat.status === 'WON') {
<div class="outcome outcome--won" data-combat-result="WON"> <div class="outcome outcome--won" data-combat-result="WON">
<h2 class="outcome__title">Sieg</h2> <h2 class="outcome__title">Victory</h2>
<p>{{ combat.monster.name }} wurde besiegt.</p> <p>{{ combat.monster.name }} has been defeated.</p>
@if (combat.rewards; as rewards) { @if (combat.rewards; as rewards) {
<section class="rewards" data-combat-rewards aria-label="Belohnungen"> <section class="rewards" data-combat-rewards aria-label="Rewards">
<h3 class="rewards__title">Belohnungen</h3> <h3 class="rewards__title">Rewards</h3>
@if (rewards.silver) { @if (rewards.silver) {
<dl class="rewards__currencies"> <dl class="rewards__currencies">
<div class="rewards__currency" data-reward-silver> <div class="rewards__currency" data-reward-silver>
<dt>Silber</dt> <dt>Silver</dt>
<dd>+{{ rewards.silver }}</dd> <dd>+{{ rewards.silver }}</dd>
</div> </div>
</dl> </dl>
} }
@if (rewards.items.length) { @if (rewards.items.length) {
<h3 class="rewards__title">Beute</h3> <h3 class="rewards__title">Loot</h3>
<ul class="rewards__loot"> <ul class="rewards__loot">
@for (reward of rewards.items; track reward.characterItemId) { @for (reward of rewards.items; track reward.characterItemId) {
<li> <li>
@@ -158,14 +158,14 @@
} }
</ul> </ul>
} @else { } @else {
<p class="outcome__hint" data-reward-empty>Keine besondere Beute gefunden.</p> <p class="outcome__hint" data-reward-empty>No notable loot found.</p>
} }
</section> </section>
} }
<div class="outcome__buttons"> <div class="outcome__buttons">
<button type="button" class="outcome__button" data-combat-to-hunt (click)="goToHunt()"> <button type="button" class="outcome__button" data-combat-to-hunt (click)="goToHunt()">
Weiter jagen Keep Hunting
</button> </button>
<button <button
type="button" type="button"
@@ -173,7 +173,7 @@
data-combat-to-location data-combat-to-location
(click)="goToLocation()" (click)="goToLocation()"
> >
Zum Ort To Location
</button> </button>
<button <button
type="button" type="button"
@@ -181,17 +181,17 @@
data-combat-to-inventory data-combat-to-inventory
(click)="goToInventory()" (click)="goToInventory()"
> >
Inventar öffnen Open Inventory
</button> </button>
</div> </div>
</div> </div>
} @else if (combat.status === 'LOST') { } @else if (combat.status === 'LOST') {
<div class="outcome outcome--lost" data-combat-result="LOST"> <div class="outcome outcome--lost" data-combat-result="LOST">
<h2 class="outcome__title">Niederlage</h2> <h2 class="outcome__title">Defeat</h2>
<p>{{ combat.player.name }} wurde im Kampf besiegt.</p> <p>{{ combat.player.name }} has been defeated.</p>
<div class="outcome__buttons"> <div class="outcome__buttons">
<button type="button" class="outcome__button" data-combat-to-hunt (click)="goToHunt()"> <button type="button" class="outcome__button" data-combat-to-hunt (click)="goToHunt()">
Weiter jagen Keep Hunting
</button> </button>
<button <button
type="button" type="button"
@@ -199,18 +199,18 @@
data-combat-to-location data-combat-to-location
(click)="goToLocation()" (click)="goToLocation()"
> >
Zum Ort To Location
</button> </button>
</div> </div>
</div> </div>
} }
</div> </div>
<aside class="combat__log" aria-label="Kampfprotokoll"> <aside class="combat__log" aria-label="Combat Log">
<h2 class="combat__log-title">Kampflog</h2> <h2 class="combat__log-title">Combat Log</h2>
<div class="combat__log-body"> <div class="combat__log-body">
@for (round of logRounds(); track round.round) { @for (round of logRounds(); track round.round) {
<p class="combat__log-round">Runde {{ round.round }}</p> <p class="combat__log-round">Round {{ round.round }}</p>
@for (event of round.events; track event.sequence) { @for (event of round.events; track event.sequence) {
<p class="combat__log-entry" [class.combat__log-entry--player]="event.source === 'PLAYER'"> <p class="combat__log-entry" [class.combat__log-entry--player]="event.source === 'PLAYER'">
{{ formatEvent(event) }} {{ formatEvent(event) }}
@@ -220,13 +220,13 @@
</div> </div>
</aside> </aside>
} @else if (combatStore.loading()) { } @else if (combatStore.loading()) {
<p class="combat__notice" role="status">Kampf wird geladen</p> <p class="combat__notice" role="status">Loading combat</p>
} }
@if (combatStore.error(); as error) { @if (combatStore.error(); as error) {
<section class="combat__notice combat__notice--error" role="alert"> <section class="combat__notice combat__notice--error" role="alert">
<p>{{ error }}</p> <p>{{ error }}</p>
<button type="button" data-combat-retry (click)="retry()">Erneut versuchen</button> <button type="button" data-combat-retry (click)="retry()">Retry</button>
</section> </section>
} }
</section> </section>

View File

@@ -28,7 +28,7 @@ const activeCombat: Combat = {
rewards: null, 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 { function countOccurrences(haystack: string | null, needle: string): number {
return haystack ? haystack.split(needle).length - 1 : 0; return haystack ? haystack.split(needle).length - 1 : 0;
@@ -91,7 +91,7 @@ describe('CombatPageComponent', () => {
expect(combatStore.loadCombat).toHaveBeenCalledWith('combat-1'); 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 fixture = await setup(activeCombat);
const element = fixture.nativeElement as HTMLElement; const element = fixture.nativeElement as HTMLElement;
@@ -99,22 +99,22 @@ describe('CombatPageComponent', () => {
expect(element.textContent).toContain('95 / 100'); expect(element.textContent).toContain('95 / 100');
expect(element.textContent).toContain('Aschenratte'); expect(element.textContent).toContain('Aschenratte');
expect(element.textContent).toContain('31 / 45'); 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(); 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 fixture = await setup(activeCombat);
const element = fixture.nativeElement as HTMLElement; const element = fixture.nativeElement as HTMLElement;
expect(element.querySelector('[data-combat-attack]')?.textContent).toContain('Angriff'); expect(element.querySelector('[data-combat-attack]')?.textContent).toContain('Attack');
expect(element.querySelector('[data-combat-heavy-strike]')?.textContent).toContain('Schwerer Hieb'); expect(element.querySelector('[data-combat-heavy-strike]')?.textContent).toContain('Heavy Strike');
expect(element.querySelector('[data-combat-shield-bash]')?.textContent).toContain('Schildstoß'); expect(element.querySelector('[data-combat-shield-bash]')?.textContent).toContain('Shield Bash');
expect(element.querySelector('[data-combat-defend]')?.textContent).toContain('Verteidigen'); expect(element.querySelector('[data-combat-defend]')?.textContent).toContain('Defend');
expect(element.querySelector('[data-combat-potion]')?.textContent).toContain('Trank 2/2'); 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 fixture = await setup(activeCombat);
const element = fixture.nativeElement as HTMLElement; const element = fixture.nativeElement as HTMLElement;
@@ -123,7 +123,7 @@ describe('CombatPageComponent', () => {
expect(combatStore.performAction).toHaveBeenCalledWith('HEAVY_STRIKE'); 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 fixture = await setup(activeCombat);
const element = fixture.nativeElement as HTMLElement; const element = fixture.nativeElement as HTMLElement;
@@ -132,7 +132,7 @@ describe('CombatPageComponent', () => {
expect(combatStore.performAction).toHaveBeenCalledWith('SHIELD_BASH'); 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 fixture = await setup(activeCombat);
const element = fixture.nativeElement as HTMLElement; const element = fixture.nativeElement as HTMLElement;
@@ -141,7 +141,7 @@ describe('CombatPageComponent', () => {
expect(combatStore.performAction).toHaveBeenCalledWith('DEFEND'); 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 fixture = await setup(activeCombat);
const element = fixture.nativeElement as HTMLElement; const element = fixture.nativeElement as HTMLElement;
@@ -169,7 +169,7 @@ describe('CombatPageComponent', () => {
const element = fixture.nativeElement as HTMLElement; const element = fixture.nativeElement as HTMLElement;
expect(element.querySelector('[data-combat-telegraph]')?.textContent).toContain( 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; 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 drinks a potion and heals 35 HP.');
expect(element.textContent).toContain('Aric Duskwalker geht in die Verteidigung.'); expect(element.textContent).toContain('Aric Duskwalker braces to defend.');
expect(element.textContent).toContain('Aschenratte bereitet Schweren Hieb vor.'); expect(element.textContent).toContain('Aschenratte is winding up a Heavy Strike.');
expect(element.textContent).toContain('Aric Duskwalker unterbricht den vorbereiteten Angriff von Aschenratte.'); 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 fixture = await setup(activeCombat);
const element = fixture.nativeElement as HTMLElement; const element = fixture.nativeElement as HTMLElement;
expect(element.textContent).toContain('Aric Duskwalker trifft Aschenratte für 14 Schaden.'); expect(element.textContent).toContain('Aric Duskwalker hits Aschenratte for 14 damage.');
expect(element.textContent).toContain('Aschenratte trifft Aric Duskwalker für 5 Schaden.'); 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 fixture = await setup(activeCombat);
const element = fixture.nativeElement as HTMLElement; const element = fixture.nativeElement as HTMLElement;
@@ -303,7 +303,7 @@ describe('CombatPageComponent', () => {
await vi.advanceTimersByTimeAsync(1260); await vi.advanceTimersByTimeAsync(1260);
fixture.detectChanges(); 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); expect(monster?.classList.contains('sprite--lunge')).toBe(false);
}); });
@@ -334,7 +334,7 @@ describe('CombatPageComponent', () => {
await vi.advanceTimersByTimeAsync(540); await vi.advanceTimersByTimeAsync(540);
fixture.detectChanges(); 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(element.querySelector('[data-combat-telegraph]')).toBeNull();
expect(monster?.classList.contains('sprite--lunge')).toBe(false); 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. // Before the checkpoint: the pre-heal HP and potion count still show.
expect(element.textContent).toContain('70 / 100'); 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, // 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 // so the HP bar and potion count update here -- well before the monster's
@@ -373,8 +373,8 @@ describe('CombatPageComponent', () => {
await vi.advanceTimersByTimeAsync(540); await vi.advanceTimersByTimeAsync(540);
fixture.detectChanges(); fixture.detectChanges();
expect(element.textContent).toContain('85 / 100'); expect(element.textContent).toContain('85 / 100');
expect(element.querySelector('[data-combat-potion]')?.textContent).toContain('Trank 1/2'); expect(element.querySelector('[data-combat-potion]')?.textContent).toContain('Potion 1/2');
expect(element.textContent).toContain('Aric Duskwalker trinkt einen Trank und heilt 15 Lebenspunkte.'); expect(element.textContent).toContain('Aric Duskwalker drinks a potion and heals 15 HP.');
// The monster's reply is still held back at this point. // The monster's reply is still held back at this point.
expect(countOccurrences(element.textContent, monsterHitLine)).toBe(1); expect(countOccurrences(element.textContent, monsterHitLine)).toBe(1);
@@ -444,7 +444,7 @@ describe('CombatPageComponent', () => {
expect(worldStore.refreshCharacter).toHaveBeenCalledOnce(); 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); const fixture = await setup(activeCombat);
combatStore.actionPending.set(true); combatStore.actionPending.set(true);
fixture.detectChanges(); fixture.detectChanges();
@@ -453,21 +453,21 @@ describe('CombatPageComponent', () => {
expect(element.querySelector<HTMLButtonElement>('[data-combat-attack]')?.disabled).toBe(true); expect(element.querySelector<HTMLButtonElement>('[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 fixture = await setup({ ...activeCombat, status: 'WON' });
const element = fixture.nativeElement as HTMLElement; const element = fixture.nativeElement as HTMLElement;
expect(element.querySelector('[data-combat-result="WON"]')).toBeTruthy(); 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(); 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 fixture = await setup({ ...activeCombat, status: 'LOST' });
const element = fixture.nativeElement as HTMLElement; const element = fixture.nativeElement as HTMLElement;
expect(element.querySelector('[data-combat-result="LOST"]')).toBeTruthy(); 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(); 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 () => { it('hides the silver tile entirely when the kill granted none', async () => {
// Every monster seeded in this slice rolls silverMin/silverMax = 0 (design // 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. // after every single fight in the shipped content.
const fixture = await setup({ const fixture = await setup({
...wonWithRewards, ...wonWithRewards,
@@ -589,7 +589,7 @@ describe('CombatPageComponent', () => {
const element = fixture.nativeElement as HTMLElement; const element = fixture.nativeElement as HTMLElement;
expect(element.querySelector('[data-reward-empty]')?.textContent).toContain( expect(element.querySelector('[data-reward-empty]')?.textContent).toContain(
'Keine besondere Beute gefunden.', 'No notable loot found.',
); );
expect(element.querySelector('[role="alert"]')).toBeNull(); expect(element.querySelector('[role="alert"]')).toBeNull();
}); });

View File

@@ -196,7 +196,7 @@ export class CombatPageComponent implements OnInit {
} }
// The location is the screen a fight resolves back into. It sits beside // 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. // one-click rhythm.
protected goToLocation(): void { protected goToLocation(): void {
void this.router.navigate(['/location']); void this.router.navigate(['/location']);
@@ -233,7 +233,7 @@ export class CombatPageComponent implements OnInit {
if (!combat || combat.monster.pendingIntent !== 'HEAVY_ATTACK') { if (!combat || combat.monster.pendingIntent !== 'HEAVY_ATTACK') {
return null; return null;
} }
return `${combat.monster.name} bereitet Schweren Hieb vor.`; return `${combat.monster.name} is winding up a Heavy Strike.`;
} }
protected logRounds(): CombatLogRound[] { protected logRounds(): CombatLogRound[] {
@@ -254,36 +254,36 @@ export class CombatPageComponent implements OnInit {
protected formatEvent(event: CombatEvent): string { protected formatEvent(event: CombatEvent): string {
const combat = this.displayed(); const combat = this.displayed();
const playerName = combat?.player.name ?? 'Du'; const playerName = combat?.player.name ?? 'You';
const monsterName = combat?.monster.name ?? 'Der Gegner'; const monsterName = combat?.monster.name ?? 'The enemy';
if (event.type === 'DAMAGE') { if (event.type === 'DAMAGE') {
const attacker = event.source === 'PLAYER' ? playerName : monsterName; const attacker = event.source === 'PLAYER' ? playerName : monsterName;
const defender = event.target === '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') { 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') { if (event.type === 'DEFEND') {
return `${playerName} geht in die Verteidigung.`; return `${playerName} braces to defend.`;
} }
if (event.type === 'TELEGRAPH') { if (event.type === 'TELEGRAPH') {
return `${monsterName} bereitet Schweren Hieb vor.`; return `${monsterName} is winding up a Heavy Strike.`;
} }
if (event.type === 'INTERRUPT') { if (event.type === 'INTERRUPT') {
return `${playerName} unterbricht den vorbereiteten Angriff von ${monsterName}.`; return `${playerName} interrupts ${monsterName}'s attack.`;
} }
if (event.type === 'COMBAT_WON') { 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<void> { private wait(ms: number): Promise<void> {