Declares every new TypeORM entity Slice 0.4 needs (ItemDefinition,
CharacterItem, LootTable, LootTableEntry, CombatReward, CombatRewardItem)
plus the ItemType/EquipmentSlot/ItemRarity enums, and adds the two columns
existing entities gain: Character.silver and MonsterDefinition.lootTableId.
No migration SQL or service logic yet - just schema declarations backed by
a metadata-driven schema spec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The server resolves a whole round in one call, so both blows used to land
at the same instant. The page now keeps its own view of the combat and
plays the round back: the swing animates, the monster's HP and log line
land, then after a beat the monster strikes and the player recoils.
Both animations are six-frame sprite sheets driven by steps(6), which is
why the phase durations mirror the stylesheet.
The status row reflows on the stage's own width via a container query —
the side rails can squeeze it narrow while the viewport is still wide,
which previously overlapped the round marker with the player's name. The
component-style budget moves to 12kB to fit this screen's stylesheet.
Swap the enemy body art for the background-free versions and trim every
sprite to its opaque bounds so the fighters share one ground baseline
instead of floating in a padded box.
Sprite height is now a share of the battlefield rather than a fixed
clamp, set per monster key, so a low-slung rat and a standing bandit keep
believable proportions against the player at any stage size.
Attacking while a combat is already active returned COMBAT_ALREADY_ACTIVE
and left the hunt page showing an error the player could not act on, with
no way back into the fight they were already in.
Add GET /api/combats/active so the client can resolve that combat, and
have the hunt page navigate into it when an attack is rejected for this
reason. CombatStore now also exposes the error code so callers can tell
this case apart from a genuinely failed attack.
Move both health bars to a status row at the top of the scene with a
circular portrait beside each, and place full-body sprites for the
player and the monster standing on the location background instead of
square portraits. The Angriff action now uses the ornate HUD frame art,
with the keybind in the frame's own tab.
Sprite and icon derivatives are keyed by monster key so both seeded
monsters resolve; the Aschenratte body art still carries its original
backdrop until a cut-out version replaces it at the same path.
CombatService only had engine-level coverage of the LOST transition.
Add service-level tests that force a loss (character.baseHp: 1) and
assert the persisted status, completedAt, further-action rejection,
and getCombat refresh behavior for a LOST combat.
Wires up the Slice 0.3 combat screen (player/monster HP bars, round
display, Angriff action, grouped German combat log, victory/defeat
panels) and replaces the Slice 0.2 combat/new placeholder route with
combat/:combatId loading CombatPageComponent.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signal-based store wrapping GameApiService.startCombat/getCombat/
performCombatAction with loading/error/actionPending state, following
the HuntingStore/WorldStore pattern. startCombat failures clear any
previously-loaded combat; loadCombat/attack failures preserve the
last-known-good combat. attack() guards against re-entrancy and
no-loaded-combat calls.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds Combat domain models (Combat, CombatPlayer, CombatMonster, CombatEvent)
and three GameApiService methods to interact with backend combat endpoints:
- startCombat(encounterId): POST /api/hunt-encounters/:id/attack
- getCombat(combatId): GET /api/combats/:id
- performCombatAction(combatId, action): POST /api/combats/:id/actions
Includes test coverage for all three methods.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Provide and export CharacterCombatStatsService from CharactersModule,
create CombatModule (registering the combat entities, controllers,
CombatService and CombatEngineService, and importing TravelModule and
CharactersModule), and register CombatModule in AppModule so the three
combat endpoints (attack, get combat, post action) are reachable from
the running app.
Wires the pure combat engine, character combat stats, and domain errors
into a transactional service that validates the HuntEncounter boundary,
snapshots stats into a new Combat row, and persists engine results.
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.
encounter-card.component rendered the raw 2.39MB ash-rat.png and
2.00MB road-bandit.png directly, up to 3 cards per hunt (~7MB/load,
re-rendered on "Neu suchen"). Add 560px-wide JPEG runtime derivatives
(generated via PowerShell System.Drawing, HighQualityBicubic, quality
82) and switch to the <picture>/<source srcset> pattern already used
by context-panel for location backgrounds, keeping the original PNGs
as the <img> fallback. Also add loading="lazy" decoding="async".
ash-rat.png: 2,506,677 B -> ash-rat-560.jpg: 35,896 B
road-bandit.png: 2,097,049 B -> road-bandit-560.jpg: 50,797 B
HuntPageComponent never called WorldStore.load(), so opening /hunt
directly (bookmark/hard refresh) without first visiting /world left
currentLocation() at null forever, stranding the page and the context
panel on their empty states with no recovery. Add ngOnInit that calls
worldStore.load() only when no location is present yet, mirroring
WorldPageComponent's existing call and avoiding a duplicate request.
Wires up Slice 0.2 end-to-end: HuntPageComponent renders the
hunting-unavailable/ready/loading/encounters-found states off
HuntingStore and WorldStore, Angreifen hands the HuntEncounter id to a
new inert CombatPlaceholderPageComponent via /combat/new, the Jagd nav
entry is enabled with router-driven active state (matching Karte's),
and the context panel now lists possible encounters for hunting-enabled
locations.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Code review flagged .encounter-card__artwork-frame using a new
hardcoded #050607 background instead of reusing an existing design
token, violating the no-new-hex-colors requirement.
Renders the 5 HuntEncounter danger tiers as German text plus a
per-tier BEM modifier class, mirroring travel-panel's text+class
danger pattern so color is never the sole signal. Colors map across
the existing 3 semantic tokens (success/warning/danger).
Mirrors WorldStore's signal-store architecture to own hunt state for
Slice 0.2 (currentHunt, selectedEncounterId, loading, error) with a
computed encounters accessor and the four hunt error codes mapped to
German messages.
Add to game-api.models.ts:
- DangerRating type for hunt encounter danger levels
- MonsterSummary interface with key, name, level, artworkPath
- HuntEncounter interface for individual hunt encounters
- HuntResult interface for hunt completion results, reusing LocationSummary
Extend CurrentLocationResponse with possibleMonsters: string[] field.
Add to game-api.service.ts:
- startHunt(): Observable<HuntResult> method posting to /api/hunts
Update test fixtures to include possibleMonsters field in mock locations.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Registers HuntingModule (POST /api/hunts) into the DI graph alongside its
new entities, and adds possibleMonsters (enabled LocationMonster pool,
weight-descending, empty when hunting is disabled) to
WorldService.getCurrentLocation. Also updates the pre-existing
DB-less app.e2e-spec.ts to override HuntingModule the same way the other
feature modules already are, since it now needs a real DataSource.
Adds startHunt's server-authoritative flow: complete due travel, verify
the location allows hunting, weighted-random-roll exactly 3 encounters
from the location's enabled monster pool inside a locked transaction
that supersedes any prior active hunt, and compute a danger rating per
encounter from the rolled monster's own stats. Mirrors TravelService's
transaction/locking pattern and error conventions.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
Adds the migration and metadata spec for the hunting schema (monster
definitions, location monster spawn tables, hunts, and hunt encounters)
required by Playable Slice 0.2. Mirrors the raw-SQL style of the visible
vertical slice migration; explicitly asserts the CASCADE-vs-RESTRICT
deviation on HuntEncounter's relation to Hunt.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
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>