Merge branch 'master' into worktree-local-location-view
Brings in the encounter-status feature (cleared/resumed hunt encounters) and its own independent Verwilderter Straßenhund / Verkohlter Plünderer assets. Both branches added the same two monsters at the same time; resolved by keeping master's asset set as canonical (images/combat/icons/, images/combat/sprites/) rather than maintaining a parallel copy under images/monsters/icons/ — dropped that directory and pointed the seed's MonsterDefinition.iconPath, the local-view test fixtures and the frontend icon lookup at the existing combat/icons paths instead. Kept this branch's COMBAT_MONSTER_SCALE entries for the two monsters, since master never added them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,7 @@ const threeEncounterHunt: HuntResult = {
|
||||
id: 'encounter-1',
|
||||
monster: { key: 'ash-rat', name: 'Aschenratte', level: 1, artworkPath: '/images/enemies/AshRat.png' },
|
||||
dangerRating: 'WEAK',
|
||||
status: 'AVAILABLE',
|
||||
},
|
||||
{
|
||||
id: 'encounter-2',
|
||||
@@ -35,11 +36,13 @@ const threeEncounterHunt: HuntResult = {
|
||||
artworkPath: '/images/enemies/RoadBandit.png',
|
||||
},
|
||||
dangerRating: 'MATCH',
|
||||
status: 'AVAILABLE',
|
||||
},
|
||||
{
|
||||
id: 'encounter-3',
|
||||
monster: { key: 'ash-rat', name: 'Aschenratte', level: 1, artworkPath: '/images/enemies/AshRat.png' },
|
||||
dangerRating: 'WEAK',
|
||||
status: 'AVAILABLE',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -73,6 +76,7 @@ describe('HuntPageComponent', () => {
|
||||
encounters: () => HuntResult['encounters'];
|
||||
startHunt: ReturnType<typeof vi.fn>;
|
||||
refreshHunt: ReturnType<typeof vi.fn>;
|
||||
loadActiveHunt: ReturnType<typeof vi.fn>;
|
||||
selectEncounter: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
let combatStore: {
|
||||
@@ -95,6 +99,7 @@ describe('HuntPageComponent', () => {
|
||||
encounters: () => currentHunt()?.encounters ?? [],
|
||||
startHunt: vi.fn(() => Promise.resolve()),
|
||||
refreshHunt: vi.fn(() => Promise.resolve()),
|
||||
loadActiveHunt: vi.fn(() => Promise.resolve()),
|
||||
selectEncounter: vi.fn(),
|
||||
};
|
||||
combatStore = {
|
||||
@@ -281,6 +286,75 @@ describe('HuntPageComponent', () => {
|
||||
expect(huntingStore.startHunt).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('adopts the resumable hunt on page entry so cleared encounters stay marked', async () => {
|
||||
await setup(burnedRoad);
|
||||
|
||||
expect(huntingStore.loadActiveHunt).toHaveBeenCalledOnce();
|
||||
expect(huntingStore.startHunt).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('marks a defeated encounter and takes away its attack action', async () => {
|
||||
const clearedHunt: HuntResult = {
|
||||
...threeEncounterHunt,
|
||||
encounters: [
|
||||
{ ...threeEncounterHunt.encounters[0], status: 'DEFEATED' },
|
||||
threeEncounterHunt.encounters[1],
|
||||
threeEncounterHunt.encounters[2],
|
||||
],
|
||||
};
|
||||
const fixture = await setup(burnedRoad, clearedHunt);
|
||||
const element = fixture.nativeElement as HTMLElement;
|
||||
|
||||
const cards = element.querySelectorAll('app-encounter-card');
|
||||
expect(cards[0].querySelector('.encounter-card__defeated-mark')).not.toBeNull();
|
||||
expect(cards[1].querySelector('.encounter-card__defeated-mark')).toBeNull();
|
||||
|
||||
const attackButtons = Array.from(
|
||||
element.querySelectorAll<HTMLButtonElement>('.encounter-card__attack'),
|
||||
);
|
||||
expect(attackButtons[0].disabled).toBe(true);
|
||||
expect(attackButtons[1].disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('keeps an in-progress encounter unmarked but locked', async () => {
|
||||
const fightingHunt: HuntResult = {
|
||||
...threeEncounterHunt,
|
||||
encounters: [
|
||||
{ ...threeEncounterHunt.encounters[0], status: 'IN_PROGRESS' },
|
||||
threeEncounterHunt.encounters[1],
|
||||
threeEncounterHunt.encounters[2],
|
||||
],
|
||||
};
|
||||
const fixture = await setup(burnedRoad, fightingHunt);
|
||||
const element = fixture.nativeElement as HTMLElement;
|
||||
|
||||
const cards = element.querySelectorAll('app-encounter-card');
|
||||
expect(cards[0].querySelector('.encounter-card__defeated-mark')).toBeNull();
|
||||
|
||||
const attackButtons = Array.from(
|
||||
element.querySelectorAll<HTMLButtonElement>('.encounter-card__attack'),
|
||||
);
|
||||
expect(attackButtons[0].disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('does not start a combat from a defeated encounter card', async () => {
|
||||
const clearedHunt: HuntResult = {
|
||||
...threeEncounterHunt,
|
||||
encounters: [
|
||||
{ ...threeEncounterHunt.encounters[0], status: 'DEFEATED' },
|
||||
threeEncounterHunt.encounters[1],
|
||||
threeEncounterHunt.encounters[2],
|
||||
],
|
||||
};
|
||||
const fixture = await setup(burnedRoad, clearedHunt);
|
||||
const element = fixture.nativeElement as HTMLElement;
|
||||
|
||||
element.querySelectorAll<HTMLButtonElement>('.encounter-card__attack')[0].click();
|
||||
await Promise.resolve();
|
||||
|
||||
expect(combatStore.startCombat).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('loads the world state on init when no location has been loaded yet (direct navigation/hard refresh)', async () => {
|
||||
await setup(null);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user