Adds Task 1 of Playable Slice 0.2: pure entity/enum/helper definitions
for the Hunt/Encounter system (MonsterDefinition, LocationMonster,
Hunt, HuntEncounter, EncounterType, HuntStatus, RandomSource,
DangerRating). No DB migration, module wiring, or seed data yet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The DB-gated e2e suite covered health, seeded reads, and the arrivesAt
whitelist rejection, but never asserted that a valid POST /api/travel
actually transitions to TRAVELLING, that a concurrent second start is
rejected with 409/TRAVEL_ALREADY_ACTIVE, or that a travel completes and
moves the character. Added one test covering all three plus GET
/api/world/current-location reflecting the move, and restores the demo
character to its original location afterward so the suite stays safely
re-runnable.
HttpErrorResponse implements the Error interface structurally but does not
extend the Error class, so `error instanceof Error` was always false for
HTTP failures and every travel error (400/409/500/network) fell through to
the same generic fallback string, hiding the backend's specific
TravelDomainError code/message (e.g. TRAVEL_ALREADY_ACTIVE) from the user.
toErrorMessage now checks `error instanceof HttpErrorResponse` first and
maps known travel error codes to specific German messages, falling back to
the generic message for unknown codes and to `error.message` for genuine
non-HTTP errors.
startTravel()'s catch block also now resyncs current travel state from the
server (reusing the existing pollCurrentTravel/refreshCurrentTravel path)
so a failed start caused by a race with another tab/request doesn't leave
the store's local state stale.
The ledger claimed the map scene renders per-location Suedtor/Aschestrasse
artwork, but world-page.component.scss hardcodes a single static world-map
background image; per-location artwork is only used in the context-panel
thumbnail. Corrected the description to match the shipped behavior.
apps/web/public/images was shipping ~90 MB of source art (enemies, npc,
combat status icons, HUD icon originals/difficulty badges, unused
background paintings) that no component, template, or stylesheet
actually references, since Angular copies public/ verbatim into every
browser build. Moved everything not referenced under apps/web/src to a
new art/ directory at the repo root, preserving the original subfolder
layout; only the 11 files actually loaded by the app (runtime HUD/
background derivatives and their PNG fallbacks) remain under
apps/web/public/images. Documented the split in README.md.
Drives the full world/travel flow in Chromium via Playwright against the
live API and seeded Postgres, capturing the three required desktop
viewports and comparing them against the accepted design reference.
Fixes two evidenced defects found during verification:
- world-page scene sizing overflowed the viewport at all three required
desktop sizes, pushing the footer (and, at 1440x900/1366x768, the travel
button) below the fold; scene height now accounts for surrounding chrome
so nothing is clipped.
- the generic HTTP-failure fallback message was hardcoded in English while
the rest of the UI is German; now uses a matching German string.
Adds Playwright as a web devDependency for this and future browser
verification passes (no product-code dependency).
Document the migration/seed/API/web workflow in a root README, add an
API e2e smoke test that always checks /api/health and additionally
exercises the real DatabaseModule/seeded data plus the arrivesAt
validation rejection when DATABASE_URL is available, and add a
`test:e2e` root script.
Also fix `.env` loading so the documented root-level `.env` is actually
found: `main.ts` never loaded dotenv at all, and `data-source.ts`
loaded it relative to `process.cwd()`, which is `apps/api` (not the
repo root) whenever npm runs a `--workspace` script. Both now resolve
the repo-root `.env` explicitly. Also narrowed the CLI migrations glob
to numeric-prefixed files so it no longer tries to load the colocated
`*.migration.spec.ts` as a migration.
Verified end-to-end against a real PostgreSQL instance: migrate, seed
twice (idempotent), start the API, and exercise every documented route
including a rejected POST /api/travel body containing arrivesAt.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>