Files
ashen-realms/apps/web/src/app/features/combat/combat-page/combat-page.component.html

275 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<section class="combat" aria-label="Combat">
@if (combat(); as combat) {
<div class="combat__stage" [class.combat__stage--shaken]="stageShake()">
<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]="'Health points ' + 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>
@if (combat.player.statusEffects.length) {
<ul class="statuses" data-combat-statuses aria-label="Active effects">
@for (effect of combat.player.statusEffects; track effect.type) {
<li
class="status"
[class]="'status--' + effect.type.toLowerCase()"
[attr.data-combat-status]="effect.type"
>
<span class="status__glyph" aria-hidden="true"></span>
<span class="status__label">
{{ statusEffectLabel(effect.type) }} · {{ effect.remainingRounds }}
</span>
</li>
}
</ul>
}
</div>
</div>
<p class="combat__round" data-combat-round>Round {{ combat.round }}</p>
<div class="fighter fighter--monster">
<div class="fighter__meter">
<p class="fighter__name">
{{ combat.monster.name }}
<span class="fighter__level">Level {{ combat.monster.level }}</span>
</p>
<div
class="bar bar--monster"
role="progressbar"
[attr.aria-label]="'Health points ' + 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="fighter__icon"
[src]="monsterIcon(combat.monster.key, combat.monster.artworkPath)"
alt=""
/>
</div>
</header>
@if (monsterIntentLabel(); as intent) {
<p class="combat__telegraph" data-combat-telegraph role="status">{{ intent }}</p>
}
@if (monsterGuardLabel(); as guard) {
<p class="combat__telegraph combat__guard" data-combat-guard role="status">{{ guard }}</p>
}
@if (monsterIsEnraged()) {
<p class="combat__telegraph combat__enraged" data-combat-enraged role="status">
{{ combat.monster.name }} is enraged.
</p>
}
<div class="combat__field">
<div
class="sprite sprite--player"
[class.sprite--attacking]="phase() === 'attacking'"
[class.sprite--hit]="phase() === 'hit'"
role="img"
[attr.aria-label]="combat.player.name"
></div>
<img
class="sprite sprite--monster"
[class.sprite--flinch]="monsterPhase() === 'flinch'"
[class.sprite--lunge]="monsterPhase() === 'lunge'"
[style.--sprite-scale]="monsterSpriteScale(combat.monster.key)"
[src]="monsterSprite(combat.monster.key, combat.monster.artworkPath)"
[alt]="combat.monster.name"
/>
</div>
<footer class="combat__actions">
@if (combat.status === 'ACTIVE') {
<button
type="button"
class="action"
data-combat-attack
[disabled]="busy()"
(click)="performAction('ATTACK')"
>
<img class="action__icon" src="/images/hud/runtime/AttackIcon-96.png" alt="" />
<span class="action__label">Attack</span>
<span class="action__key">1</span>
</button>
<button
type="button"
class="action"
data-combat-heavy-strike
[disabled]="busy()"
(click)="performAction('HEAVY_STRIKE')"
>
<img class="action__icon" src="/images/hud/runtime/AttackIcon-96.png" alt="" />
<span class="action__label">Heavy Strike</span>
<span class="action__key">2</span>
</button>
<button
type="button"
class="action"
data-combat-shield-bash
[disabled]="busy()"
(click)="performAction('SHIELD_BASH')"
>
<img class="action__icon" src="/images/hud/runtime/CharacterIcon-128.png" alt="" />
<span class="action__label">Shield Bash</span>
<span class="action__key">3</span>
</button>
<button
type="button"
class="action"
data-combat-defend
[disabled]="busy()"
(click)="performAction('DEFEND')"
>
<img class="action__icon" src="/images/hud/runtime/CharacterIcon-128.png" alt="" />
<span class="action__label">Defend</span>
<span class="action__key">4</span>
</button>
<button
type="button"
class="action"
data-combat-potion
[disabled]="busy() || combat.player.potionsRemaining <= 0"
(click)="performAction('POTION')"
>
<img class="action__icon" src="/images/items/small-healing-potion.png" alt="" />
<span class="action__label">Potion {{ combat.player.potionsRemaining }}/{{ combat.player.potionsMax }}</span>
<span class="action__key">5</span>
</button>
}
</footer>
@if (combat.status === 'WON') {
<div class="outcome outcome--won" data-combat-result="WON">
<h2 class="outcome__title">Victory</h2>
<p>{{ combat.monster.name }} has been defeated.</p>
@if (combat.rewards; as rewards) {
<section class="rewards" data-combat-rewards aria-label="Rewards">
@if (lootGroups(); as groups) {
@if (groups.length) {
@for (group of groups; track group.key) {
<h3 class="rewards__title" [attr.data-reward-group]="group.key">
{{ group.title }}
</h3>
<ul class="rewards__loot">
@for (reward of group.items; track reward.characterItemId) {
<li>
<app-item-card [item]="reward.item" [quantity]="reward.quantity" />
</li>
}
</ul>
}
} @else {
<p class="outcome__hint" data-reward-empty>No notable loot found.</p>
}
}
@if (leftBehind(); as refused) {
@if (refused.length) {
<h3 class="rewards__title rewards__title--refused">Left Behind</h3>
<ul class="rewards__refused" data-reward-left-behind>
@for (entry of refused; track entry.key) {
<li [attr.data-reward-refused]="entry.key">
{{ entry.name }} ×{{ entry.quantity }} — no room left
</li>
}
</ul>
}
}
@if (rewards.capacities.length) {
<app-loot-capacity-strip
class="rewards__capacities"
[capacities]="rewards.capacities"
/>
}
</section>
}
<div class="outcome__buttons">
<button type="button" class="outcome__button" data-combat-to-hunt (click)="goToHunt()">
Keep Hunting
</button>
<button
type="button"
class="outcome__button outcome__button--secondary"
data-combat-to-location
(click)="goToLocation()"
>
To Location
</button>
<button
type="button"
class="outcome__button outcome__button--secondary"
data-combat-to-inventory
(click)="goToInventory()"
>
Open Inventory
</button>
</div>
</div>
} @else if (combat.status === 'LOST') {
<div class="outcome outcome--lost" data-combat-result="LOST">
<h2 class="outcome__title">Defeat</h2>
<p>{{ combat.player.name }} has been defeated.</p>
<div class="outcome__buttons">
<button type="button" class="outcome__button" data-combat-to-hunt (click)="goToHunt()">
Keep Hunting
</button>
<button
type="button"
class="outcome__button outcome__button--secondary"
data-combat-to-location
(click)="goToLocation()"
>
To Location
</button>
</div>
</div>
}
</div>
<aside class="combat__log" aria-label="Combat Log">
<h2 class="combat__log-title">Combat Log</h2>
<div class="combat__log-body">
@for (round of logRounds(); track round.round) {
<p class="combat__log-round">Round {{ 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__notice" role="status">Loading combat…</p>
}
@if (combatStore.error(); as error) {
<section class="combat__notice combat__notice--error" role="alert">
<p>{{ error }}</p>
<button type="button" data-combat-retry (click)="retry()">Retry</button>
</section>
}
</section>