feat(combat): rebuild the combat screen around the reference layout
Move both health bars to a status row at the top of the scene with a circular portrait beside each, and place full-body sprites for the player and the monster standing on the location background instead of square portraits. The Angriff action now uses the ornate HUD frame art, with the keybind in the frame's own tab. Sprite and icon derivatives are keyed by monster key so both seeded monsters resolve; the Aschenratte body art still carries its original backdrop until a cut-out version replaces it at the same path.
This commit is contained in:
@@ -1,93 +1,117 @@
|
||||
<section class="combat-page" aria-label="Kampf">
|
||||
<section class="combat" aria-label="Kampf">
|
||||
@if (combatStore.combat(); as combat) {
|
||||
<div class="combat-page__scene">
|
||||
<div class="combat-page__combatant combat-page__combatant--player">
|
||||
<img
|
||||
class="combat-page__portrait"
|
||||
src="/images/hud/runtime/CharacterIcon-128.png"
|
||||
[alt]="combat.player.name"
|
||||
/>
|
||||
<div class="combat-page__info">
|
||||
<span class="combat-page__name">{{ combat.player.name }}</span>
|
||||
<div class="combat-page__hp" aria-label="Lebenspunkte">
|
||||
<span>{{ combat.player.currentHp }} / {{ combat.player.maxHp }}</span>
|
||||
<span class="combat-page__hp-track" aria-hidden="true">
|
||||
<span class="combat-page__hp-value" [style.inline-size.%]="playerHpPercent()"></span>
|
||||
</span>
|
||||
<div class="combat__stage">
|
||||
<header class="combat__status">
|
||||
<div class="fighter fighter--player">
|
||||
<img class="fighter__icon" [src]="playerIcon" alt="" />
|
||||
<div class="fighter__meter">
|
||||
<p class="fighter__name">{{ combat.player.name }}</p>
|
||||
<div
|
||||
class="bar bar--player"
|
||||
role="progressbar"
|
||||
[attr.aria-label]="'Lebenspunkte ' + combat.player.name"
|
||||
[attr.aria-valuenow]="combat.player.currentHp"
|
||||
[attr.aria-valuemin]="0"
|
||||
[attr.aria-valuemax]="combat.player.maxHp"
|
||||
>
|
||||
<span class="bar__fill" [style.inline-size.%]="playerHpPercent()"></span>
|
||||
<span class="bar__text">{{ combat.player.currentHp }} / {{ combat.player.maxHp }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="combat-page__round" data-combat-round>Runde {{ combat.round }}</div>
|
||||
<p class="combat__round" data-combat-round>Runde {{ combat.round }}</p>
|
||||
|
||||
<div class="combat-page__combatant combat-page__combatant--monster">
|
||||
<picture>
|
||||
@if (runtimeMonsterArtwork(combat.monster.artworkPath); as runtimeArtwork) {
|
||||
<source [srcset]="runtimeArtwork" type="image/jpeg" />
|
||||
}
|
||||
<div class="fighter fighter--monster">
|
||||
<div class="fighter__meter">
|
||||
<p class="fighter__name">
|
||||
{{ combat.monster.name }}
|
||||
<span class="fighter__level">Stufe {{ combat.monster.level }}</span>
|
||||
</p>
|
||||
<div
|
||||
class="bar bar--monster"
|
||||
role="progressbar"
|
||||
[attr.aria-label]="'Lebenspunkte ' + combat.monster.name"
|
||||
[attr.aria-valuenow]="combat.monster.currentHp"
|
||||
[attr.aria-valuemin]="0"
|
||||
[attr.aria-valuemax]="combat.monster.maxHp"
|
||||
>
|
||||
<span class="bar__fill" [style.inline-size.%]="monsterHpPercent()"></span>
|
||||
<span class="bar__text">{{ combat.monster.currentHp }} / {{ combat.monster.maxHp }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<img
|
||||
class="combat-page__portrait"
|
||||
[src]="combat.monster.artworkPath"
|
||||
[alt]="combat.monster.name"
|
||||
class="fighter__icon"
|
||||
[src]="monsterIcon(combat.monster.key, combat.monster.artworkPath)"
|
||||
alt=""
|
||||
/>
|
||||
</picture>
|
||||
<div class="combat-page__info">
|
||||
<span class="combat-page__name">{{ combat.monster.name }}</span>
|
||||
<span class="combat-page__level">Stufe {{ combat.monster.level }}</span>
|
||||
<div class="combat-page__hp" aria-label="Lebenspunkte des Gegners">
|
||||
<span>{{ combat.monster.currentHp }} / {{ combat.monster.maxHp }}</span>
|
||||
<span class="combat-page__hp-track" aria-hidden="true">
|
||||
<span
|
||||
class="combat-page__hp-value combat-page__hp-value--monster"
|
||||
[style.inline-size.%]="monsterHpPercent()"
|
||||
></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="combat__field">
|
||||
<img class="sprite sprite--player" [src]="playerSprite" [alt]="combat.player.name" />
|
||||
<img
|
||||
class="sprite sprite--monster"
|
||||
[src]="monsterSprite(combat.monster.key, combat.monster.artworkPath)"
|
||||
[alt]="combat.monster.name"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="combat-page__actions">
|
||||
@if (combat.status === 'ACTIVE') {
|
||||
<button
|
||||
type="button"
|
||||
class="combat-page__attack"
|
||||
data-combat-attack
|
||||
[disabled]="combatStore.actionPending()"
|
||||
(click)="attack()"
|
||||
>
|
||||
Angriff
|
||||
</button>
|
||||
} @else if (combat.status === 'WON') {
|
||||
<section class="combat-page__result combat-page__result--won" data-combat-result="WON">
|
||||
<h2>Sieg</h2>
|
||||
<p>{{ combat.monster.name }} wurde besiegt.</p>
|
||||
<p class="combat-page__result-hint">Belohnungen werden im nächsten Schritt verarbeitet.</p>
|
||||
<button type="button" data-combat-to-hunt (click)="goToHunt()">Zur Jagd</button>
|
||||
</section>
|
||||
} @else {
|
||||
<section class="combat-page__result combat-page__result--lost" data-combat-result="LOST">
|
||||
<h2>Niederlage</h2>
|
||||
<p>{{ combat.player.name }} wurde im Kampf besiegt.</p>
|
||||
<button type="button" data-combat-to-hunt (click)="goToHunt()">Zur Jagd</button>
|
||||
</section>
|
||||
}
|
||||
</div>
|
||||
|
||||
<aside class="combat-page__log" aria-label="Kampfprotokoll">
|
||||
@for (round of logRounds(); track round.round) {
|
||||
<p class="combat-page__log-round">Runde {{ round.round }}</p>
|
||||
@for (event of round.events; track event.sequence) {
|
||||
<p class="combat-page__log-entry">{{ formatEvent(event) }}</p>
|
||||
<footer class="combat__actions">
|
||||
@if (combat.status === 'ACTIVE') {
|
||||
<button
|
||||
type="button"
|
||||
class="action"
|
||||
data-combat-attack
|
||||
[disabled]="combatStore.actionPending()"
|
||||
(click)="attack()"
|
||||
>
|
||||
<img class="action__icon" src="/images/hud/runtime/AttackIcon-96.png" alt="" />
|
||||
<span class="action__label">Angriff</span>
|
||||
<span class="action__key">1</span>
|
||||
</button>
|
||||
}
|
||||
</footer>
|
||||
|
||||
@if (combat.status === 'WON') {
|
||||
<div class="outcome outcome--won" data-combat-result="WON">
|
||||
<h2 class="outcome__title">Sieg</h2>
|
||||
<p>{{ combat.monster.name }} wurde besiegt.</p>
|
||||
<p class="outcome__hint">Belohnungen werden im nächsten Schritt verarbeitet.</p>
|
||||
<button type="button" class="outcome__button" data-combat-to-hunt (click)="goToHunt()">
|
||||
Zur Jagd
|
||||
</button>
|
||||
</div>
|
||||
} @else if (combat.status === 'LOST') {
|
||||
<div class="outcome outcome--lost" data-combat-result="LOST">
|
||||
<h2 class="outcome__title">Niederlage</h2>
|
||||
<p>{{ combat.player.name }} wurde im Kampf besiegt.</p>
|
||||
<button type="button" class="outcome__button" data-combat-to-hunt (click)="goToHunt()">
|
||||
Zur Jagd
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<aside class="combat__log" aria-label="Kampfprotokoll">
|
||||
<h2 class="combat__log-title">Kampflog</h2>
|
||||
<div class="combat__log-body">
|
||||
@for (round of logRounds(); track round.round) {
|
||||
<p class="combat__log-round">Runde {{ round.round }}</p>
|
||||
@for (event of round.events; track event.sequence) {
|
||||
<p class="combat__log-entry" [class.combat__log-entry--player]="event.source === 'PLAYER'">
|
||||
{{ formatEvent(event) }}
|
||||
</p>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</aside>
|
||||
} @else if (combatStore.loading()) {
|
||||
<p class="combat-page__loading" role="status">Kampf wird geladen…</p>
|
||||
<p class="combat__notice" role="status">Kampf wird geladen…</p>
|
||||
}
|
||||
|
||||
@if (combatStore.error(); as error) {
|
||||
<section class="combat-page__error" role="alert">
|
||||
<section class="combat__notice combat__notice--error" role="alert">
|
||||
<p>{{ error }}</p>
|
||||
<button type="button" data-combat-retry (click)="retry()">Erneut versuchen</button>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user