Phase 3: combat depth
- Condition durations + auto-expiry: timed conditions tick down on the affected combatant's turn and drop at 0 (engine tickConditions; UI rounds field + (Nr) chip) - Initiative: Roll-all (d20 + per-combatant bonus, re-sort anchored) + group/quantity add - Combat log: per-round event feed (turns, round changes, damage/heal, expiries, removals) + in-memory Undo of the last change - Encounter difficulty budget: 5e XP thresholds + PF2e level budget (pure lib + tests); live difficulty/XP-award readout from monster combatants vs the party - Combatants carry initBonus + cr/level; compendium add-to-combat passes them 18 new unit tests (durations, applyInitiatives, budget), new combat-depth e2e. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.evaluate(async () => {
|
||||
indexedDB.deleteDatabase('ttrpg-manager');
|
||||
localStorage.clear();
|
||||
});
|
||||
await page.reload();
|
||||
});
|
||||
|
||||
test('combat depth: timed condition auto-expires, log records events, roll-all works', async ({ page }) => {
|
||||
await page.getByRole('button', { name: '+ New campaign' }).first().click();
|
||||
await page.locator('input[data-autofocus]').fill('Depth');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
await page.getByRole('link', { name: 'Combat' }).click();
|
||||
await page.getByRole('button', { name: '+ New encounter' }).first().click();
|
||||
await page.locator('input[data-autofocus]').fill('Brawl');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
await page.getByLabel('Name').fill('Goblin');
|
||||
await page.getByRole('button', { name: 'Add', exact: true }).click();
|
||||
|
||||
const row = page.locator('li', { hasText: 'Goblin' });
|
||||
|
||||
// Add Prone with a 1-round duration
|
||||
await row.getByLabel('Condition duration in rounds').fill('1');
|
||||
await row.getByLabel('Add condition').selectOption('Prone');
|
||||
await expect(row.getByRole('button', { name: /Prone \(1r\)/ })).toBeVisible();
|
||||
|
||||
// Roll-all initiative
|
||||
await page.getByRole('button', { name: /Roll all/ }).click();
|
||||
|
||||
// Start combat, then advance a turn — Prone should expire on Goblin's next turn
|
||||
await page.getByRole('button', { name: 'Start combat' }).click();
|
||||
await expect(page.getByRole('button', { name: 'End' })).toBeVisible(); // combat is active
|
||||
await page.getByRole('button', { name: /Next turn/ }).click();
|
||||
await expect(row.getByRole('button', { name: /Prone/ })).toHaveCount(0);
|
||||
|
||||
// Combat log captured the events
|
||||
await expect(page.getByText('Combat Log')).toBeVisible();
|
||||
await expect(page.getByText(/Prone wore off/)).toBeVisible();
|
||||
});
|
||||
+1
-1
@@ -48,7 +48,7 @@ test('full core flow: campaign → character → dice → combat → compendium'
|
||||
await page.getByRole('button', { name: 'Add', exact: true }).click();
|
||||
await expect(page.getByText('Goblin')).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Start combat' }).click();
|
||||
await expect(page.getByText(/Round 1/)).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'End' })).toBeVisible(); // combat active
|
||||
|
||||
// --- Compendium ---
|
||||
await page.getByRole('link', { name: 'Compendium' }).click();
|
||||
|
||||
Reference in New Issue
Block a user