feat: add hunt page, combat placeholder, and Jagd navigation
Wires up Slice 0.2 end-to-end: HuntPageComponent renders the hunting-unavailable/ready/loading/encounters-found states off HuntingStore and WorldStore, Angreifen hands the HuntEncounter id to a new inert CombatPlaceholderPageComponent via /combat/new, the Jagd nav entry is enabled with router-driven active state (matching Karte's), and the context panel now lists possible encounters for hunting-enabled locations. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,12 @@
|
||||
<dt>Jagd</dt>
|
||||
<dd>{{ location.huntingEnabled ? 'Jagd möglich' : 'Keine Jagd' }}</dd>
|
||||
</div>
|
||||
@if (location.huntingEnabled && location.possibleMonsters.length) {
|
||||
<div>
|
||||
<dt>Mögliche Begegnungen</dt>
|
||||
<dd>{{ location.possibleMonsters.join(', ') }}</dd>
|
||||
</div>
|
||||
}
|
||||
</dl>
|
||||
} @else {
|
||||
<span class="context-panel__eyebrow">GEBIETSINFO</span>
|
||||
|
||||
@@ -3,6 +3,33 @@ import { TestBed } from '@angular/core/testing';
|
||||
import { WorldStore } from '../../features/world/world.store';
|
||||
import { ContextPanelComponent } from './context-panel.component';
|
||||
|
||||
const southGate = {
|
||||
id: 'south-gate-id',
|
||||
key: 'south-gate',
|
||||
name: 'Südtor von Graufurt',
|
||||
description: 'Der letzte sichere Schritt vor den Aschenfeldern.',
|
||||
regionKey: 'ashen-fields',
|
||||
minRecommendedLevel: 1,
|
||||
maxRecommendedLevel: 1,
|
||||
dangerLevel: 0,
|
||||
isSafe: true,
|
||||
huntingEnabled: false,
|
||||
artworkPath: '/images/backgrounds/Suedtor.png',
|
||||
connections: [],
|
||||
possibleMonsters: [],
|
||||
};
|
||||
|
||||
const burnedRoad = {
|
||||
...southGate,
|
||||
id: 'burned-road-id',
|
||||
key: 'burned-road',
|
||||
name: 'Verbrannte Straße',
|
||||
isSafe: false,
|
||||
huntingEnabled: true,
|
||||
artworkPath: '/images/backgrounds/Aschestrasse.png',
|
||||
possibleMonsters: ['Aschenratte', 'Straßenräuber'],
|
||||
};
|
||||
|
||||
describe('ContextPanelComponent', () => {
|
||||
it('uses a runtime location derivative while preserving the API artwork path as image fallback', async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
@@ -11,20 +38,7 @@ describe('ContextPanelComponent', () => {
|
||||
{
|
||||
provide: WorldStore,
|
||||
useValue: {
|
||||
currentLocation: signal({
|
||||
id: 'south-gate-id',
|
||||
key: 'south-gate',
|
||||
name: 'Südtor von Graufurt',
|
||||
description: 'Der letzte sichere Schritt vor den Aschenfeldern.',
|
||||
regionKey: 'ashen-fields',
|
||||
minRecommendedLevel: 1,
|
||||
maxRecommendedLevel: 1,
|
||||
dangerLevel: 0,
|
||||
isSafe: true,
|
||||
huntingEnabled: false,
|
||||
artworkPath: '/images/backgrounds/Suedtor.png',
|
||||
connections: [],
|
||||
}),
|
||||
currentLocation: signal(southGate),
|
||||
selectedConnection: signal(null),
|
||||
},
|
||||
},
|
||||
@@ -42,4 +56,48 @@ describe('ContextPanelComponent', () => {
|
||||
'/images/backgrounds/Suedtor.png',
|
||||
);
|
||||
});
|
||||
|
||||
it('hides "Mögliche Begegnungen" when the location has no hunting', async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ContextPanelComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: WorldStore,
|
||||
useValue: {
|
||||
currentLocation: signal(southGate),
|
||||
selectedConnection: signal(null),
|
||||
},
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
const fixture = TestBed.createComponent(ContextPanelComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
const element = fixture.nativeElement as HTMLElement;
|
||||
expect(element.textContent).not.toContain('Mögliche Begegnungen');
|
||||
});
|
||||
|
||||
it('lists possible monsters as plain text when hunting is enabled', async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ContextPanelComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: WorldStore,
|
||||
useValue: {
|
||||
currentLocation: signal(burnedRoad),
|
||||
selectedConnection: signal(null),
|
||||
},
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
const fixture = TestBed.createComponent(ContextPanelComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
const element = fixture.nativeElement as HTMLElement;
|
||||
expect(element.textContent).toContain('Mögliche Begegnungen');
|
||||
expect(element.textContent).toContain('Aschenratte, Straßenräuber');
|
||||
expect(element.textContent).not.toMatch(/\d+\s?%/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<nav class="side-navigation" aria-label="Spielnavigation">
|
||||
<button
|
||||
class="side-navigation__item side-navigation__item--active"
|
||||
class="side-navigation__item"
|
||||
type="button"
|
||||
routerLink="/world"
|
||||
routerLinkActive="side-navigation__item--active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
ariaCurrentWhenActive="page"
|
||||
data-navigation="world"
|
||||
aria-current="page"
|
||||
aria-label="Karte"
|
||||
>
|
||||
<img src="/images/hud/runtime/MapsIcon-128.png" alt="" />
|
||||
@@ -14,9 +16,12 @@
|
||||
<button
|
||||
class="side-navigation__item"
|
||||
type="button"
|
||||
routerLink="/hunt"
|
||||
routerLinkActive="side-navigation__item--active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
ariaCurrentWhenActive="page"
|
||||
data-navigation="hunt"
|
||||
disabled
|
||||
aria-label="Jagd ist noch nicht verfügbar"
|
||||
aria-label="Jagd"
|
||||
>
|
||||
<img src="/images/hud/runtime/HuntIcon-128.png" alt="" />
|
||||
<span>Jagd</span>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-side-navigation',
|
||||
imports: [RouterLink],
|
||||
imports: [RouterLink, RouterLinkActive],
|
||||
templateUrl: './side-navigation.component.html',
|
||||
styleUrl: './side-navigation.component.scss',
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user