Commit Graph

137 Commits

Author SHA1 Message Date
Bastian Wagner
d0eecc45d6 sprites 2026-08-23 19:25:57 +02:00
Bastian Wagner
bbd5dbbd1f traveling 2026-08-23 18:47:11 +02:00
Bastian Wagner
5fa344dbe2 seed 2026-08-23 18:31:31 +02:00
Bastian Wagner
c39ba369c3 art 2026-08-23 17:28:56 +02:00
Bastian Wagner
40d69693ba feat(web): announce a newly discovered route 2026-08-23 16:56:48 +02:00
Bastian Wagner
06721f24e0 fix(web): style combat guard/enrage banners off semantic classes, not test hooks 2026-08-23 16:51:07 +02:00
Bastian Wagner
b2a7fc59d0 feat(web): show monster guard and rage on the combat screen 2026-08-23 16:44:07 +02:00
Bastian Wagner
2489b9308f fix(web): resize the Raider Captain icon to actual icon size
The hand-made captain icon was copied verbatim at its 1254x1254 authoring
resolution instead of being scaled down, shipping an 80x heavier PNG under
a -128 filename to every hunt card and combat surface showing the Captain.
Resize it to 128x128 like its siblings while keeping the authored
composition; also stop the loop from writing a throwaway cropped icon for
that key before the hand-made one overwrites it.
2026-08-23 12:09:55 +02:00
Bastian Wagner
06450a0d36 feat(web): derive and register the watchpost artwork 2026-08-23 12:02:20 +02:00
Bastian Wagner
2c96163a03 Merge branch 'master' into slice/0.9-first-quest-and-bag-tutorial
master turned the gate watch into painted scenery with its own portrait and
result image; Slice 0.9 turns that same hotspot into a real NPC. Kept the
NPC doorway and dropped the result text and image with it -- a hotspot
carries one or the other, never both.

The warden now uses master's `graufurt-gate-watch.png` instead of the raw
copy this branch added: same figure, already optimized. Borin keeps master's
optimized portrait for the same reason.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 08:58:21 +02:00
Bastian Wagner
3a84fd5371 feat(web): show quest markers on location hotspots
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 23:28:55 +02:00
Bastian Wagner
ff8b7c8b78 feat(web): run quest steps from the NPC screen
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 23:19:16 +02:00
Bastian Wagner
bc0d910190 feat(web): add the quest journal
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 23:12:09 +02:00
Bastian Wagner
636efb6b5a feat(web): add quest API contracts
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 23:05:53 +02:00
Bastian Wagner
7b738a072d art 2026-08-22 23:03:48 +02:00
Bastian Wagner
69a26460ae notice board 2026-08-22 22:46:58 +02:00
Bastian Wagner
fb7a96f032 layout interactions 2026-08-22 22:42:55 +02:00
Bastian Wagner
242c0935fa feat(quests): seed Trouble Beyond the Gate and the South Gate Warden
The demo character no longer starts with a free Basic Hide Bag: the
bagless HIDE capacity of 1 is what the quest chain exists to teach.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 22:39:30 +02:00
Bastian Wagner
93916457cb icons 2026-08-22 22:34:51 +02:00
Bastian Wagner
2dadc5c4a7 art 2026-08-22 21:56:28 +02:00
Bastian Wagner
139c5517f2 Merge branch 'slice/0.8.5-reputation-gated-merchant-offers' 2026-08-22 21:27:30 +02:00
Bastian Wagner
ab8bbeae5d fix(shop): render a bag offer's capacity line once, not twice
`resolveTarget` gave a bag target the same string for `description` and
`effectSummary`, and the shop row renders both, so the slice's two flagship
offers showed "Capacity: 5 Raider Trophies" on consecutive lines. Spec §5's
worked example shows it once.

A bag definition carries no flavour text of its own, so the description is now
empty and the row omits the span entirely rather than emitting an empty one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 21:06:46 +02:00
Bastian Wagner
374926e494 feat(web): announce offers a trade just unlocked 2026-08-22 20:30:36 +02:00
Bastian Wagner
681af334bf fix(web): make ShopOfferView.effectSummary/requirements required
The server always sends both fields; an optional frontend type only
dodged a transient gap in merchant.store.spec.ts's shopView() fixture
and risked a requirement line silently failing to render if a future
fixture omitted the fields. Add the two fields to that single fixture
instead (an open offer, so null/[] are the honest values) and drop the
now-unnecessary `?? []` tolerance in the template.
2026-08-22 20:20:26 +02:00
Bastian Wagner
00784094ab feat(web): show offer requirements, current progress and effects 2026-08-22 20:15:51 +02:00
Bastian Wagner
8e6ec7b43c fix(web): stop hunting encounter cards from collapsing to 0px height
The host switched to display:grid + justify-items:center to center the
new flavor-text line under each card. Grid items size to their own
content by default, and .encounter-card has no in-flow content (every
child is absolutely positioned), so its intrinsic width collapsed to 0
-- taking the aspect-ratio-derived height down with it.

Revert the host to a plain block layout and center the flavor line with
its own margin instead of grid alignment.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JDyWTLMpKYUGMz84CKrtT8
2026-08-22 17:03:11 +02:00
Bastian Wagner
081c9f83f9 docs 2026-08-22 16:41:47 +02:00
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
fdd83f2dee fix(test): translate the remaining Worn Shortsword and Padded Helm fixture names
Task 10 review sweep found two German strings the closing sweep missed:
'Abgenutztes Kurzschwert' (8 occurrences across 5 spec files, the real
seeded worn-short-sword item) and 'Gepolsterter Helm' (1 occurrence, a
synthetic test-only bonus-hp-helm fixture). Translated both to their
English equivalents per the item-content.ts glossary and slice style.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACkMEDYiwtcfchqKkiUJNX
2026-08-21 23:18:12 +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
ae163f000a test: reconcile the shared location fixture with Task 1's English seed content
current-location.fixture.ts was the one shared test fixture deliberately
left German through the slice. Updates location name/description,
monster names, and POI/reward-preview labels to match local-location.content.ts
and vertical-slice.seed.ts verbatim (no keys touched).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACkMEDYiwtcfchqKkiUJNX
2026-08-21 22:59:14 +02:00
Bastian Wagner
846bb7428c feat(web): translate topbar, navigation, context panel, footer, and reputation display to English
Translates the remaining German static copy in the layout/shared components
per the design doc glossary: top-bar (World Renown, Silver, loading text),
side-navigation (nav labels and aria-labels), context-panel (area info,
safety/hunt facts, encounters), game-footer (connection status, Ashen Fields),
and reputation-display (aria-label, highest-rank text). Also fixes stale
German rank-label fixtures in reputation-display.component.spec.ts flagged
by Task 2's reviewer, and updates app.spec.ts assertions that exercised the
now-translated side-navigation/top-bar output via the app shell.
2026-08-21 22:23:20 +02:00
Bastian Wagner
14d26d06a4 feat(web): translate hunt screen and encounter card copy to English
Translates every static German string in hunt-page.component.html and
encounter-card.component.html/ts: headings, buttons, aria-labels,
loading/error text, the monster level label, and the encounter
action-label logic (Defeated/In Combat/Attack).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACkMEDYiwtcfchqKkiUJNX
2026-08-21 22:11:50 +02:00
Bastian Wagner
1c0a13475c feat(web): translate location, travel, and world-map screen copy to English 2026-08-21 22:02:26 +02:00
Bastian Wagner
ca3e072807 feat(web): translate inventory and item-detail screen copy to English 2026-08-21 21:52:53 +02:00
Bastian Wagner
833074d9fe fix(web): translate the combat page's monster level badge to English
Closes a task-5 review finding: the fighter__level badge on the combat
page still read "Stufe {{ level }}"; the design doc gap that excluded
it has since been fixed. Changed to "Level {{ level }}" to match
encounter-card.component.html's pending equivalent (out of scope here).
2026-08-21 21:45:53 +02:00
Bastian Wagner
0923fc0a23 feat(web): translate combat log, action buttons, and victory/defeat screen to English
Translates formatEvent() and monsterIntentLabel() combat-log templates,
default participant-name fallbacks (Du/Der Gegner -> You/The enemy), and
every static string in the combat page template: round header, action
buttons, outcome headings, rewards panel, post-combat buttons, log panel,
and loading/error/aria-label text.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACkMEDYiwtcfchqKkiUJNX
2026-08-21 21:40:04 +02:00
Bastian Wagner
5586e6c672 feat(web): translate error messages surfaced from API error codes to English
Translates the five client-side error-message maps (world, hunting, combat,
inventory, local-location stores) that re-translate already-English API
error codes back to display text, per design doc §4. Also fixes four
component specs that hard-coded the same German strings when asserting
rendered error text (combat-page, hunt-page, location-interaction-panel,
location-page).
2026-08-21 21:29:10 +02:00
Bastian Wagner
5222bfb2ab feat(web): translate slot, rarity, danger, and location-type labels to English
Translates the four static Angular label maps (SLOT_LABELS, RARITY_LABELS,
DANGER_LABELS, LOCATION_TYPE_LABELS) and the inventory detail panel's
hardcoded item-type fallback per the English Game Content Foundation
design doc glossary. Updates every spec assertion across the web app that
checks rendered text sourced from these maps, including specs owned by
later tasks (combat-page, encounter-card, inventory-page) where they
render one of these labels.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 20:16:56 +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
0c9f00d9a8 fix(web): finish the reward-preview sweep the seed change started
When I dropped Silber and Erfahrung from the burned road's seeded
reward preview (e5752c7, because R7 zeroed every monster's silver on
that road), I updated the server content but not the web fixture that
mirrors it. burnedRoadFixture() still advertised Silber, and the
sidebar spec still asserted it rendered -- so the web tests were
proving the UI shows a category the API no longer sends.

Also clears dead level/experience fixture fields and an experience key
in a reward-service mock from combat-equipment-integration.spec.ts.
They were inert (nothing read them) but they date from after the
API-side sweep and describe fields the entity and DTO no longer have.

API 267/267, web 229/229.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 18:28:52 +02:00
Bastian Wagner
688c4cf70f test(inventory): cover the non-equippable branch of the detail panel
The panel's three-way actions block had no test for its middle branch:
an item with equipmentSlot null should show "Nicht ausrüstbar" and no
equip button. The gap predates this slice -- Task 14's review found it
while confirming the removed level gate had not cost coverage.

Deferred until now on purpose: the web bundle could not compile for the
whole slice, so committing this earlier would have meant shipping a test
I had never executed.

Load-bearing: dropping the !isEquippable() guard would fall through to
the else branch and render the button, failing the assertion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 18:16:53 +02:00
Bastian Wagner
18f4be586b fix(web): locations recommend Ansehen, not the abolished Stufe
Two location panels still labelled their recommended range "Empfohlene
Stufe". The player no longer has a Stufe -- this slice replaced the
1-7 level scale with Ansehen 1-15, so the UI was recommending a scale
the character cannot be measured on.

Source slice doc section 3 defines this concept as Recommended Renown
per region (Aschenfelder 1-5). The seeded values already sit inside
that range (south-gate 1-1, burned-road 1-2), so only the label was
wrong -- no data change needed.

The min_recommended_level / max_recommended_level column and property
names are left as-is: internal naming, no user-visible effect, and
renaming them would reach across the API, the entity, the DTO and the
seed for no behavioural gain.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 18:15:42 +02:00
Bastian Wagner
b350c3f9b1 fix(web): sweep every remaining fixture off level/experience/requiredLevel
Task 17 (final task) of Renown & Reputation Foundation. Fixes the last 3
compile errors blocking the web suite (app.spec.ts, world.store.spec.ts)
by aligning CharacterResponse fixtures to the real renown-only shape, and
removes the two remaining dead 'experience' reward-preview leftovers
(current-location fixture entry, location-icon glyph) now that the API
no longer seeds them. Full web suite: 24 files / 228 tests, all green,
for the first time this entire slice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 18:13:37 +02:00
Bastian Wagner
b5bcd50dcd docs(web): correct the victory-refresh comment about what combat grants
The comment claimed the server had granted "silver and renown" on a
combat win. It does not: grantVictoryRewards adds silver and item drops,
and nothing in apps/api/src/combat or apps/api/src/rewards touches
renown at all. Renown comes from milestones only.

Introduced when I committed Task 15's staged work after its agent was
cut off mid-step; caught by the task review.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 18:06:38 +02:00
Bastian Wagner
4cb759b628 feat(reputation): add the reusable Regional Reputation display component 2026-08-21 18:06:37 +02:00
Bastian Wagner
221819880c arts und agents 2026-08-21 17:32:05 +02:00
Bastian Wagner
ab6227881e feat(web): bind the HUD health bar to the locally-ticking HP value
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 16:12:55 +02:00