Commit Graph

27 Commits

Author SHA1 Message Date
Bastian Wagner
dfa62fd152 fix: translate the last five German fixture strings found by the final review's re-review
Fixes 5 residual findings from the re-review of Playable Slice 0.6.6's
whole-branch review: two twice-hit spec files still carried invented
or seeded German fixture values (reputation-display faction name,
inventory-detail-panel item description and item name), a world store
fixture still used a German location description, and a dev comment
in reputation-content.ts referenced a faction name that no longer
exists in the code. All are literal string substitutions using
already-translated canon English text; no keys, ids, or logic changed.
2026-08-22 09:17:23 +02:00
Bastian Wagner
8418a93329 fix: correct final whole-branch review findings for 0.6.6 English content
- TRAVELLING spelling: fix TRAVELING -> TRAVELLING in travel-panel.component.html
  to match UK spelling used everywhere else (world/hunting/combat stores + API
  error messages)
- Grenzwacht -> Border Watch: fix stale German faction display name in
  reputation-display, reputation.service/controller, turn-in.service specs,
  and the vertical-slice seed spec test title
- Suedtor von Graufurt -> Graufurt South Gate: fix stale ASCII-transliterated
  German location name in hunting.service.spec.ts
- Aschenfelder(n) -> Ashen Fields: fix stale German location name/description
  in top-bar.component.spec.ts and context-panel.component.spec.ts (key
  identifiers left untouched)
- Fix 7 test titles still describing translated error messages as "German"
  across world/hunting/inventory/combat store specs
- README: update demo location names from German to their current English
  names (Graufurt South Gate, Burned Road)
- item-rarity.enum.ts: fix now-false comment claiming rarity labels are
  German; the frontend RARITY_LABELS map is English

Pure literal-string/comment substitutions; no keys, ids, or logic changed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACkMEDYiwtcfchqKkiUJNX
2026-08-22 09:03:13 +02:00
Bastian Wagner
1322285b0f fix: close out remaining German player-facing text across web and API tests
Repo-wide grep sweep (apps/web/src, apps/api/src) for leftover German
content strings missed by Tasks 1-9, mostly in spec fixtures/assertions
that mirror already-translated seed content (monster/item names, POI
titles and action labels, location names/descriptions) plus a few real
source-file gaps:

- inventory-detail-panel.component.ts: STAT_LABELS (Waffenschaden,
  Angriff, Leben, Rüstung) were never translated by Task 6; now match
  the identical English labels already used in inventory-page.component.html.
- app-shell.component.html: aria-label="Spielinhalt" -> "Game content"
  (this file was outside every prior task's file list).
- location-interaction-panel.component.spec.ts: dead NPC-quote fixture
  translated to match the real wounded-scout POI text.

Code comments referencing German source-spec section titles or
not-yet-seeded faction names, and inline calculation-documentation
comments, are left as-is per the source spec's scope (dev-facing
comments may stay German).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACkMEDYiwtcfchqKkiUJNX
2026-08-21 22:59:34 +02:00
Bastian Wagner
b8ee56ca1a feat(content): translate seeded items, locations, and reputation faction to English
Converts all German player-facing content in the API's seed files to
English per the English Game Content Foundation glossary: item
name/description, monster name, location name/description, all
local-location POI/action content, and the border-guard reputation
faction. Technical keys are unchanged.
2026-08-21 20:00:04 +02:00
Bastian Wagner
cedd87f648 Merge branch 'master' into worktree-playable-slice-0.6.5-renown-reputation
Reconciles Slice 0.6.5 (Renown & Reputation Foundation) against master's
persistent-HP-and-regeneration slice, which landed independently and
touches several of the same files (Character entity, CombatService,
EquipmentService, the inventory detail panel).

Conflict resolutions:
- CharacterStatsService/EquipmentService constructor wiring: kept
  master's CharacterVitalsService injection, which this branch's
  version of the same files didn't have yet.
- CombatService.performAction: kept master's HP-guard logic
  (characterTooWounded, vitals pause-on-enter) alongside this branch's
  multi-line calculate() call style.
- Inventory detail panel (.html/.ts/.scss/.spec.ts): master had
  redesigned the panel (wrapping section, rarity styling, flavour
  text, a shared inventory.labels.ts) on top of the OLD level-gated
  component, since this branch's removal of the level gate (R4, Task
  8/14) hadn't reached master yet. Kept master's visual redesign in
  full, but with the level-gate concept removed throughout: no
  requiredLevel stat block, no meetsLevelRequirement() branch in the
  equip button, no now-dead .detail__value--unmet SCSS rule. Kept both
  branches' independent tests (non-equippable-item, flavour-text).
- inventory-page.component.ts: dropped master's dead characterLevel
  computed (nothing in the template read it, and the level concept is
  gone); kept its independent bagCells/bagUsed/bagCapacity grid
  feature, which has nothing to do with renown or level.

Post-merge fixture repairs (three files failed the Angular bundle
compile because they predate master's hpRegenPerSecond/hpRegenSince
fields or master's item description field, neither conflict-marked
since git considered them non-overlapping edits):
- app.spec.ts: a 'renders loaded character values' test added on
  master after this branch forked still used the abolished level/
  experience fields on its decoy fixture -- retargeted to renown.
- inventory-detail-panel.component.spec.ts: the ashPelt fixture added
  by this branch's final-review follow-up predates master's required
  description field.
- top-bar.component.spec.ts: this branch's fixture predates master's
  required hpRegenPerSecond/hpRegenSince fields.

No database migration touches the same column: master's
1792000000000-AddHpRegeneration only adds characters.hp_regen_since,
independent of this slice's 1791000000000-CreateRenownAndReputation.
Timestamp ordering between the two was already correct with no rename
needed.

Verified: API 288/288 (267 from this slice + 21 from master), API
build zero errors, web 237/237 (230 from this slice + 7 from master).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 19:23:10 +02:00
Bastian Wagner
23ed527eea fix: address the final whole-branch review's findings
The branch review approved the slice with no blocking findings. These
are the substantive non-blocking ones.

N1, the one no per-task review could see: every seeded monster rolls
silverMin/silverMax = 0 (R7), so the victory screen showed "Silber +0"
after every fight in the shipped game. Three tasks were each correct in
isolation -- the mechanism stays, the values are zero, the field still
exists -- and the composite was wrong. Now conditional, with a test.
This does not contradict R16: R16 deleted the XP block because the
field ceased to exist, leaving nothing to hide. Silver still exists and
can be non-zero, so a conditional is the right tool.

N2: world.store.ts still said a combat "granted XP and silver". Same
false-fact-in-a-comment defect fixed in b5bcd50, one file over.

N4: resolveReputationRank threw a TypeError on negative input, since
findIndex returns -1 and REPUTATION_RANKS[-1] is undefined. Unreachable
today, but grantReputation is public and accepts any number.

N5/N6: grantReputation resolved factions without the enabled filter the
read path applies, so a disabled faction could accumulate invisible
reputation -- "disabled" was not actually a kill switch. The dense read
also had no ORDER BY, so the list could reorder between requests.

N8: design 13 requires silver stay 0 for every seeded monster; only two
of four were pinned. Re-adding silver to the others would have shipped
silently.

N9: spec 36's "a normal kill grants no Renown" had no test. It was
structurally guaranteed but unasserted -- now locked down against a
later slice wiring renown into combat.

N10: an impossible renown: 0 fixture, and RENOWN_MIN exported but never
used to clamp the floor.

API 268/268, web 230/230, API build zero errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 18:50:30 +02:00
Bastian Wagner
caf63b34a4 chore(api): anchor the seeded demo character's HP regeneration
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 15:53:20 +02:00
Bastian Wagner
e5752c7ee9 fix(content): stop promising rewards the burned road can no longer drop
The location view's "Mögliche Belohnungen" preview listed Silber and
Erfahrung. This slice made both false:

- Experience no longer exists as a concept (spec 1).
- Every monster seeded on this road now rolls silverMin/silverMax = 0
  (design R7), so a kill yields no silver. Silver reaches the player
  through turn-ins instead.

The list's own comment states the rule it was breaking: the view may not
promise a drop the roll does not guarantee (spec 8). Equipment and
material stay -- the loot tables still back both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:30:53 +02:00
Bastian Wagner
8835671657 fix(monsters): finish deleting experienceReward, column included
Design ruling R7 abolishes XP as a concept and says the column goes with
it, but no task in the plan actually dropped it -- the plan only dropped
characters.experience and combat_rewards.experience_granted. Task 9
removed experienceReward from the seed literals, leaving
monster_definitions.experience_reward as a NOT NULL column with no
default that nothing supplies. The first monster insert against a real
database would have failed on a constraint violation.

No suite here could have caught it: none of them connect to Postgres.

Drops the column in the slice migration (which has never been run, so
amending it in place is correct rather than stacking a second one),
removes the entity field, and clears the three test fixtures that still
set it. silver_min/silver_max deliberately stay -- spec 15 keeps a
direct currency drop available as a lore-valid exception, and XP has no
such carve-out.

Also retargets the seed idempotency test off renown: 1, which is the
seed's own default and so could not distinguish "preserved" from
"reset to default".

NOTE ON SCOPE: this commit also absorbs a Prettier reformatting pass
that was already sitting uncommitted in the working tree, which is why
it touches ~59 files. That churn is purely cosmetic line-rewrapping --
verified by inspection, and the suite is green at 267/267 with the build
at exactly the 3 expected errors owned by Tasks 10 and 11. The repo is
not Prettier-clean at baseline (119 files still flagged), so this was a
partial run by an earlier step, not a deliberate repo-wide format.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:10:19 +02:00
Bastian Wagner
dce26e00ad feat(content): migrate ItemType, seed Räuberabzeichen/Grenzwacht/turn-ins, zero direct monster rewards 2026-08-21 13:49:49 +02:00
Bastian Wagner
49a0008a2b Merge branch 'worktree-playable-slice-0.5-first-upgrade'
# Conflicts:
#	apps/web/src/app/core/api/game-api.service.ts
#	apps/web/src/app/features/combat/combat-page/combat-page.component.html
#	apps/web/src/app/features/combat/combat-page/combat-page.component.scss
#	apps/web/src/app/features/combat/combat-page/combat-page.component.spec.ts
#	apps/web/src/app/features/combat/combat-page/combat-page.component.ts
2026-08-20 19:42:10 +02:00
Bastian Wagner
0ce3b420e6 fix: address final-review findings (TopBar hydration, snapshot test, armor doc, inventory refresh test)
Fixes 4 Important findings from the final whole-branch review:
- /inventory never called WorldStore.load(), leaving the TopBar stuck on
  "loading" and characterLevel() silently defaulting to 1 for any character
  above level 1. Mirrors the same guard already used in HuntPageComponent.
- The combat/equipment snapshot-immutability integration test asserted only
  status/round, never the actual playerState snapshot the whole test claims
  to prove is untouched after a post-fight equip.
- Documented (comment only, no behavior change) that the demo character's
  armor dropping from the old hardcoded 6 to 0 is an intentional,
  spec-sanctioned tradeoff (Slice 0.5 spec Section19), not a bug.
- inventory.store.spec.ts's equip test used an identical inventory fixture
  before and after equip(), so a regression dropping the post-equip
  inventory re-fetch would still have passed. Now asserts the refetched
  fixture is actually reflected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 18:44:39 +02:00
Bastian Wagner
90094ba1ae fix(api): key starting-sword seed idempotency on its real unique index
The CharacterItem idempotency check was looking up by the seed's own
literal id instead of the (characterId, itemDefinitionId) unique index
that CharacterItem actually enforces. If the demo character had already
looted a worn-short-sword naturally, re-running the seed would miss
that row and try to insert a colliding duplicate, breaking seeding
instead of being a safe no-op. Look up by the real domain key and reuse
whatever id is found when wiring up the CharacterEquipment row.
2026-08-20 17:23:45 +02:00
Bastian Wagner
6178b88573 feat(api): seed the starting sword as a real, equipped CharacterItem 2026-08-20 17:11:18 +02:00
Bastian Wagner
3ee107694c address code review: POI order matches plan, self-documenting height reserve
Reorders the Verbrannte Straße POIs to plan §8's authored sequence
(hunt, investigate, search, then the scout) — purely a keyboard tab-order
fix, since hotspots are placed by percentage, not list order.

Rewrites the location page's viewport-height reserve as a calc() over the
same rem values the top bar and footer already declare as their own
min-block-size, with file:line pointers to both, instead of an opaque
191px constant. Doesn't remove the underlying coupling (still no
ResizeObserver / shared token), but a future edit to either component's
minimum height now has a documented, unit-matching term to update instead
of an unexplained magic number.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 16:33:42 +02:00
Bastian Wagner
c158260623 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>
2026-08-20 16:03:59 +02:00
Bastian Wagner
60e1734f59 fix(web): keep the primary action bar on screen at every desktop width
The shell sizes itself with min-block-size everywhere, which is a floor,
not a ceiling. Against that indefinite ancestor, the location page's
minmax(0, 1fr) scene row fell back to content-based sizing instead of
being bounded, so the artwork could grow tall enough to push the action
bar off screen — confirmed visually at 1920 and 1024px widths, where the
bar was fully or partially clipped.

Gives the location page its own definite, viewport-bounded height
(reserve = stable top bar + footer + own padding) instead of touching the
shared shell, which other screens still size freely. The narrow/tablet
breakpoint had a second instance of the same class of bug: the sidebar's
auto-sized row claimed its full content height before the 1fr main row
saw any space at all, collapsing the action bar to 0px height. Swapping
which row is auto vs. 1fr — main first — fixes it the same way.

Also re-anchors the four Verbrannte Straße hotspots to painted detail in
the real artwork (cart, roadside grave, road, cracked stones) rather than
the composition-reference coordinates, and lets primary-action labels
stay on one line via clamp() instead of wrapping unevenly across widths.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 15:50:31 +02:00
Bastian Wagner
6f0020137b feat(web): add the local location view at /location
The screen a player stands on between activities: name, region, scene
artwork with hotspots pinned by percentage, a four-button action bar and
a context sidebar covering identity, danger, encounters, interactions and
rewards.

It owns no knowledge of any particular place. Hotspots and actions are
routed by interaction type: HUNT and MAP hand off to the existing hunt
and map screens, and everything that reveals text goes through the
server-authoritative interaction endpoint. A second location therefore
renders by supplying different content, which the Südtor case in the page
spec exercises.

The shell drops its generic area rail on /location, where the screen's
own sidebar says the same thing better, and Ort joins the navigation as
its first entry. Root and unknown routes now land on the location rather
than the map: arriving somewhere should mean arriving at a place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 10:51:08 +02:00
Bastian Wagner
c7e7e97252 Merge branch 'master' into worktree-local-location-view
Brings in the First Loot slice. Resolved additively:

- MonsterDefinition keeps both the new iconPath and master's lootTableId.
- The seed keeps the four-monster pool and the local view content, and
  gives the two new monsters existing loot tables — the road dog shares
  the beast table, the charred looter the raider table.
- The local location view migration moves to 1788700000000 so it orders
  deterministically after the loot migration, which claimed the same
  timestamp.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 10:38:18 +02:00
Bastian Wagner
c85a70484f feat(world): serve local location view content from the API
Adds the server side of the local location view: location_definitions
carries region naming, a location type, a scene-level description and
artwork, plus JSONB points of interest, primary actions and a reward
preview. Locations become content, so a second location renders through
the same components with different data.

GET /api/world/current-location gains those fields, a recommendation
label and a danger rating derived from the weighted average of the
location's own monster pool — a rare elite no longer makes a beginner
road read as lethal. The encounter preview is derived from that same
pool rather than duplicating it.

POST /api/world/current-location/interactions/:key reveals a hotspot's
authored result. The location is resolved from the character, never from
the request, and result text never ships with the location payload, so a
caller cannot read or trigger a hotspot it has not travelled to.

Seeds the Verbrannte Straße with its four hotspots and the Südtor with
its own transition content. Adds Verwilderter Straßenhund and Verkohlter
Plünderer to the road's pool, including combat sprites, so the preview
shows encounters the hunt can actually roll. Medallion icons move to
images/monsters/icons, where both combat and the location view read them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 10:30:01 +02:00
Bastian Wagner
c58a46b7d9 feat(loot): seed tier-1 items and the ash rat and road bandit loot tables
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 23:59:08 +02:00
Bastian Wagner
137a18f4e7 style: fix prettier formatting drift in two API files
vertical-slice.seed.ts and danger-rating.spec.ts had line-wrapping
that no longer matched prettier's output, which a CI prettier --check
would flag. Formatting only, no logic change.
2026-08-19 14:33:41 +02:00
Bastian Wagner
9423c28bd8 feat: seed Aschenratte and Straßenräuber for Verbrannte Straße
Adds the two starter monster definitions and their location-monster
mapping (weights 70/30) to the vertical-slice seed, following the
same findOneBy/update-or-insert pattern used for locations, plus an
upsert on (locationId, monsterId) for the mapping. Copies the source
artwork into the served images/monsters directory and extends the
seed spec harness to cover both idempotent inserts.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-19 12:08:26 +02:00
Bastian Wagner
1e2bfe6e4b feat: render interactive ashen fields world 2026-08-19 08:25:42 +02:00
Bastian Wagner
0615bb1841 fix: seed character at resolved south gate 2026-08-18 19:45:05 +02:00
Bastian Wagner
c35859a4de fix: preserve seeded location identities 2026-08-18 19:40:13 +02:00
Bastian Wagner
067134b684 feat: seed demo character and first locations 2026-08-18 19:30:31 +02:00