Phase 4: dynamic vision, walls & doors
- src/lib/map/vision.ts (pure, tested): blockingSegments (walls + closed doors), segment intersection, computeVisibleCells (raycast viewer→cell-centre, sight radius). - BattleMap += dynamicVision + sightRadiusFeet (Dexie v12, backfilled). - MapEditor: new "Walls" tool (draw wall polylines; click a door to open/close), a "Vision" toggle (auto-enables fog and reveals from the party), sight-radius field, and "Reveal from party". Moving a PC token accumulates revealed cells via LoS when Vision is on. Doors/walls render for the GM (amber=closed, green=open). - Networked players need no new protocol: revealed cells already travel in the snapshot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,24 @@ test('maps: import a Universal VTT (.dd2vtt) map', async ({ page }) => {
|
||||
await expect(page.getByRole('button', { name: 'Export .uvtt' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('maps: walls tool + dynamic vision controls', async ({ page }) => {
|
||||
await page.getByRole('button', { name: '+ New campaign' }).first().click();
|
||||
await page.locator('input[data-autofocus]').fill('Vision');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('link', { name: 'Dashboard' }).click();
|
||||
await page.getByRole('link', { name: 'Maps' }).click();
|
||||
await page.locator('input[type="file"]').setInputFiles('e2e/fixtures/sample.dd2vtt');
|
||||
await expect(page.getByTestId('map-viewport')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'walls', exact: true }).click();
|
||||
await expect(page.getByRole('button', { name: /Reveal from party/ })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: /Clear walls/ })).toBeVisible();
|
||||
|
||||
const vision = page.locator('label', { hasText: 'Vision' }).locator('input[type=checkbox]');
|
||||
await vision.check();
|
||||
await expect(vision).toBeChecked();
|
||||
});
|
||||
|
||||
test('maps: zoom controls scale the canvas and fit resets', async ({ page }) => {
|
||||
await page.getByRole('button', { name: '+ New campaign' }).first().click();
|
||||
await page.locator('input[data-autofocus]').fill('Zoom');
|
||||
|
||||
Reference in New Issue
Block a user