Files
ashen-realms/docs/playable-slices/Ashen Realms – Playable Slice 0.5_ First Upgrade.md
Bastian Wagner 5b662aad4c docs
2026-08-19 11:14:17 +02:00

1929 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Ashen Realms Playable Slice 0.5: First Upgrade
**Status:** Ready for implementation
**Prerequisite:** Playable Slice 0.4 First Loot
**Scope:** First usable inventory and equipment upgrade
**Primary Upgrade:** Abgenutztes Kurzschwert → Räuberklinge
**Primary Goal:** A looted item can be equipped and measurably changes the character's effective combat stats.
---
# 1. Goal
Playable Slice 0.5 completes the first full Ashen Realms progression loop.
The existing playable flow:
```text
World
→ Travel
→ Hunt
→ Encounter
→ Combat
→ Victory
→ Reward
```
becomes:
```text
World
→ Travel
→ Hunt
→ Combat
→ Loot
→ Inventory
→ Equip upgrade
→ Character becomes stronger
→ next combat becomes easier
```
This slice must prove that:
- player-owned items are persistent
- inventory is server-authoritative
- equipment is server-authoritative
- equipment slots are enforced
- item ownership is validated
- required level is validated
- effective character stats are derived from character base stats plus equipment
- equipping an upgrade changes combat-relevant stats
- the frontend does not calculate authoritative character power
- a newly obtained weapon produces a visibly meaningful improvement
The primary demonstration is:
```text
Abgenutztes Kurzschwert
8 Waffenschaden
```
becomes:
```text
Räuberklinge
11 Waffenschaden
+1 Angriff
```
and the next combat deals more damage.
---
# 2. Core player flow
The intended flow is:
```text
Straßenräuber besiegen
Räuberklinge erhalten
Reward screen
Inventar öffnen
Räuberklinge auswählen
current item and new item compared
Ausrüsten
server validates item
server equips Räuberklinge in Waffe slot
effective stats recalculated
Topbar / character UI updates
return to hunt
fight another enemy
player deals more damage
```
This moment is the first real proof of item-driven progression.
---
# 3. Scope
Implement:
```text
Inventory API
Equipment API
CharacterEquipment persistence
CharacterStatsService
effective character stats
weapon equipment slot
basic armor equipment slots where already supported
inventory page
item selection
item detail presentation
simple equipped-item comparison
equip action
unequip/replace behavior as required
equipped state display
effective stat refresh
```
At minimum, the system must support:
```text
Abgenutztes Kurzschwert
Räuberklinge
Räuberhaube
```
where those items already exist in content.
---
# 4. Explicit non-goals
Do not implement:
```text
item selling
merchants
item durability
repairs
salvaging
crafting
socketing
random affixes
item upgrades +1 to +10
transmog
set bonuses
complex tooltips
drag-and-drop inventory
inventory sorting systems
filters
search
bank/storage
stash
equipment presets
multiple builds
comparison against every possible slot
```
Do not expand the slice into a complete MMO inventory system.
The goal is only:
> obtain item → inspect item → equip item → become stronger.
---
# 5. Equipment philosophy
Ashen Realms uses equipment as the primary source of character power.
The Vertical Slice targets roughly:
```text
20 % strength from level/base stats
80 % strength from equipment
```
Therefore equipment changes must be noticeable.
The player should immediately understand:
```text
old weapon
new weapon
more damage
```
Do not hide the effect behind tiny percentage changes.
---
# 6. Equipment slots
The final Vertical Slice uses:
```text
WEAPON
HEAD
CHEST
HANDS
LEGS
FEET
AMULET
```
Slice 0.5 should support this enum/data model cleanly.
However, the actual playable demonstration only requires:
```text
WEAPON
```
and optionally:
```text
HEAD
```
if Räuberhaube is already part of the first loot implementation.
Do not build seven complex UI workflows if only one or two slots currently have content.
---
# 7. ItemDefinition
Reuse the existing `ItemDefinition`.
Do not create a second item model.
Relevant conceptual fields:
```ts
id: uuid;
key: string;
name: string;
description: string;
type: ItemType;
equipmentSlot?: EquipmentSlot;
rarity: ItemRarity;
tier: number;
requiredLevel: number;
weaponDamage: number;
bonusHp: number;
bonusAttack: number;
bonusArmor: number;
sellPrice: number;
iconPath: string;
```
Only fields already supported by the existing architecture should be introduced.
Do not add speculative attributes.
---
# 8. Initial item content
## Abgenutztes Kurzschwert
Stable key:
```text
worn-short-sword
```
Stats:
```text
equipmentSlot: WEAPON
requiredLevel: 1
weaponDamage: 8
bonusAttack: 0
bonusHp: 0
bonusArmor: 0
```
This is the starting weapon.
---
# 9. Räuberklinge
Stable key:
```text
bandit-blade
```
Stats:
```text
equipmentSlot: WEAPON
requiredLevel: 1
weaponDamage: 11
bonusAttack: 1
```
This item must be a clear upgrade from the starting weapon.
If finalized content in the repository differs, preserve the established project values.
---
# 10. Räuberhaube
Stable key:
```text
bandit-hood
```
Stats:
```text
equipmentSlot: HEAD
bonusArmor: 3
bonusHp: 5
```
Only include this in the playable inventory if it already exists in Slice 0.4 content.
The weapon upgrade remains the required core test.
---
# 11. CharacterItem
Reuse the persistent player-owned item entity introduced in Slice 0.4.
Conceptually:
```ts
id: uuid;
characterId: uuid;
itemDefinitionId: uuid;
quantity: number;
createdAt: timestamptz;
updatedAt: timestamptz;
```
Equipment ownership must originate from `CharacterItem`.
The frontend must not be able to equip arbitrary `ItemDefinition.id` values.
---
# 12. CharacterEquipment
Create or reuse:
```text
apps/api/src/equipment/entities/character-equipment.entity.ts
```
Conceptual fields:
```ts
id: uuid;
characterId: uuid;
slot: EquipmentSlot;
characterItemId: uuid;
createdAt: timestamptz;
updatedAt: timestamptz;
```
Important invariants:
```text
one equipped item per character per slot
```
and:
```text
one CharacterItem cannot occupy multiple equipment slots simultaneously
```
Protect these through service validation and appropriate database constraints.
---
# 13. Ownership rule
The server must validate:
```text
CharacterItem belongs to current character
```
before equipment changes.
Never accept:
```text
ItemDefinition.id
```
alone as sufficient proof of ownership.
Equip requests should identify the owned item:
```text
CharacterItem.id
```
---
# 14. Equip validation
Before equipping:
```text
CharacterItem exists
belongs to current character
ItemDefinition is equippable
equipmentSlot exists
requiredLevel <= character level
item is valid for target slot
```
The client must not choose an arbitrary target slot if the item already defines its slot.
Prefer:
```http
POST /api/equipment
```
with:
```json
{
"characterItemId": "uuid"
}
```
The server derives:
```text
slot
```
from the item definition.
---
# 15. Equip replacement behavior
If the slot already contains an item:
```text
current item
new item equipped
old item becomes unequipped
old item remains in inventory
```
Do not destroy or delete the old CharacterItem.
Example:
```text
WEAPON:
Abgenutztes Kurzschwert
```
then equip:
```text
Räuberklinge
```
result:
```text
WEAPON:
Räuberklinge
Inventory:
Abgenutztes Kurzschwert still owned
```
---
# 16. Unequip behavior
For Slice 0.5, an explicit unequip endpoint is optional.
If implemented:
```http
DELETE /api/equipment/:slot
```
It must remove only the equipped relation.
The item remains owned.
Do not make explicit unequip necessary for replacing an item.
---
# 17. Starting equipment
The demo character must begin with:
```text
Abgenutztes Kurzschwert
```
actually represented through the same inventory/equipment model used for future loot.
Do not keep the starting weapon as an unrelated hardcoded combat constant once equipment is implemented.
The goal is to remove temporary Slice 0.3 combat-stat shortcuts where possible.
---
# 18. CharacterStatsService
Introduce or complete:
```text
CharacterStatsService
```
This becomes the single authoritative source for effective character stats.
Conceptual return type:
```ts
interface EffectiveCharacterStats {
maxHp: number;
currentHp: number;
attack: number;
weaponDamage: number;
armor: number;
combatPower: number;
}
```
The service calculates values from:
```text
Character base stats
+
equipped ItemDefinitions
```
Do not calculate effective stats in:
```text
Angular
CombatController
InventoryController
```
---
# 19. Base stats
The starting level-1 character uses:
```text
baseHp = 100
baseAttack = 6
```
Equipment supplies:
```text
weaponDamage
armor
bonusHp
bonusAttack
```
Starting equipment target:
```text
100 HP
6 base attack
8 weapon damage
6 armor
```
if the corresponding starting armor pieces are already modeled.
If not all armor items currently exist, do not fabricate an entire starter gear set merely for Slice 0.5.
Preserve the existing demo balance as closely as the implemented content allows.
---
# 20. Effective stat calculation
Conceptually:
```text
maxHp =
baseHp
+ sum equipped bonusHp
attack =
baseAttack
+ sum equipped bonusAttack
weaponDamage =
equipped weapon weaponDamage
armor =
sum equipped bonusArmor
```
If no weapon is equipped:
```text
weaponDamage = 0
```
or use another existing explicit project rule.
Do not silently inject a fake weapon value.
---
# 21. Combat Power
Calculate internal Combat Power using the established formula:
```text
Combat Power =
HP / 10
+ Attack × 2
+ Weapon Damage × 2
+ Armor × 1.5
```
This value remains internal for balancing unless the existing UI already exposes it.
Do not add Combat Power to the player-facing UI just because the service now calculates it.
---
# 22. Combat integration
Slice 0.3 combat snapshots player stats when combat begins.
After Slice 0.5:
```text
Combat creation
CharacterStatsService.calculate(...)
snapshot current effective equipment-derived stats
```
Therefore equipping Räuberklinge affects:
```text
future combats
```
but must not retroactively alter:
```text
already-active combat
```
This snapshot behavior is important.
---
# 23. Example power change
Before upgrade:
```text
Base attack: 6
Weapon damage: 8
```
Raw attack damage:
```text
14
```
After equipping Räuberklinge:
```text
Attack:
6 + 1 = 7
Weapon damage:
11
```
Raw attack damage:
```text
18
```
Therefore the upgrade should be clearly noticeable.
---
# 24. Inventory API
Implement:
```http
GET /api/inventory
```
The server resolves the current character.
Do not send:
```text
characterId
```
from Angular.
Example response:
```json
{
"items": [
{
"id": "character-item-uuid",
"quantity": 1,
"equipped": false,
"item": {
"key": "bandit-blade",
"name": "Räuberklinge",
"rarity": "COMMON",
"equipmentSlot": "WEAPON",
"requiredLevel": 1,
"weaponDamage": 11,
"bonusAttack": 1,
"bonusHp": 0,
"bonusArmor": 0,
"iconPath": "/assets/items/bandit-blade.webp"
}
}
]
}
```
Do not expose unnecessary persistence details.
---
# 25. Equipment API
Implement:
```http
GET /api/equipment
```
Response conceptually:
```json
{
"slots": {
"WEAPON": {
"characterItemId": "uuid",
"item": {
"key": "worn-short-sword",
"name": "Abgenutztes Kurzschwert"
}
},
"HEAD": null
},
"stats": {
"maxHp": 100,
"attack": 6,
"weaponDamage": 8,
"armor": 6
}
}
```
Only expose slots supported by the current shared enum.
---
# 26. Equip API
Implement:
```http
POST /api/equipment
```
Request:
```json
{
"characterItemId": "uuid"
}
```
Server determines:
```text
character
item
equipment slot
validity
```
Response should return authoritative updated equipment and effective stats.
Do not require the frontend to manually reload multiple unrelated resources if one coherent response can provide the changed state.
---
# 27. Domain errors
Use stable errors.
Examples:
```text
CHARACTER_ITEM_NOT_FOUND
ITEM_NOT_OWNED
ITEM_NOT_EQUIPPABLE
ITEM_LEVEL_REQUIREMENT_NOT_MET
INVALID_EQUIPMENT_SLOT
```
If another item occupies the slot, replacement is valid and should not be an error.
---
# 28. Transaction
Equip operations must be transactional.
Conceptually:
```text
load/lock CharacterItem
validate ownership
load ItemDefinition
validate level/slot
remove/replace current CharacterEquipment relation
create/update equipment relation
commit
```
Concurrent equip requests must not create duplicate slot occupancy.
---
# 29. Inventory page route
Activate or create:
```text
/inventory
```
Enable:
```text
Inventar
```
in the left navigation.
The screen must remain inside the existing AppShell.
---
# 30. Inventory page layout
The inventory page should preserve the Ashen Realms visual language.
Recommended desktop composition:
```text
left / center:
inventory item slots/grid
right:
selected item details / comparison
```
Avoid:
```text
generic ecommerce product grid
admin table
SaaS data table
```
The inventory should feel like RPG equipment management.
---
# 31. Inventory grid
The full Vertical Slice concept allows approximately:
```text
24 slots
```
However Slice 0.5 does not require enforcing capacity yet unless this already exists.
The UI may visually establish a slot-based inventory.
At minimum display:
```text
item icon
quantity
rarity
equipped indicator
```
Empty slots may be displayed for visual structure.
---
# 32. Item selection
Clicking an owned item selects it.
Selected item details show:
```text
name
icon
rarity
equipment slot
required level
stats
equipped state
```
Example:
```text
Räuberklinge
Waffe
Stufe 1
11 Waffenschaden
+1 Angriff
```
---
# 33. Item comparison
When selecting an equippable item, compare it to the currently equipped item in the same slot.
Example:
```text
Räuberklinge
11 Waffenschaden +3
+1 Angriff +1
```
compared to:
```text
Abgenutztes Kurzschwert
8 Waffenschaden
```
The comparison exists for player clarity only.
The backend remains authoritative over actual stat calculations.
---
# 34. Comparison rules
Use simple deterministic numeric comparison.
For Slice 0.5, show only direct fixed stats:
```text
weaponDamage
bonusAttack
bonusHp
bonusArmor
```
Do not invent an opaque overall item score.
Do not use Combat Power as the item comparison metric.
---
# 35. Equip action
Selected equippable, owned, level-valid items show:
```text
Ausrüsten
```
Clicking it calls:
```http
POST /api/equipment
```
After success:
```text
inventory state refreshes
equipment state refreshes
effective stats refresh
equipped indicator updates
```
---
# 36. Already equipped state
If the selected item is already equipped:
```text
Ausgerüstet
```
should be clearly visible.
Do not show another active:
```text
Ausrüsten
```
button for the same item.
---
# 37. Required-level state
If the player's level is too low:
Display:
```text
Benötigt Stufe X
```
and disable the equip action.
Backend must still enforce the same validation.
---
# 38. Equipment overview
The inventory or right context area should show current equipped slots.
At minimum:
```text
Waffe
Kopf
Brust
Handschuhe
Beine
Stiefel
Amulett
```
Slots without items should clearly show:
```text
Leer
```
Do not add fake starter gear to fill every visual slot.
---
# 39. Effective stats presentation
Display a small character stat summary:
```text
Leben
Angriff
Waffenschaden
Rüstung
```
These values must come from the backend.
Example before:
```text
Angriff: 6
Waffenschaden: 8
```
after:
```text
Angriff: 7
Waffenschaden: 11
```
The visual update is important to make the upgrade obvious.
---
# 40. Topbar integration
If the TopBar displays HP or other affected values, refresh its authoritative character state after equipment changes.
Do not create a second conflicting stat representation.
Reuse the existing character/world store architecture where appropriate.
---
# 41. Reward screen integration
Slice 0.4 currently presents dropped items.
Add a useful navigation path:
```text
Inventar öffnen
```
when equipment loot is obtained.
Example:
```text
Räuberklinge erhalten
[ Inventar öffnen ]
[ Zur Jagd ]
```
Do not equip loot directly from the reward screen in this slice.
The player should consciously inspect and equip the item.
---
# 42. CharacterStatsService tests
At minimum test:
## Starting weapon
Given:
```text
baseAttack = 6
baseHp = 100
worn-short-sword:
weaponDamage = 8
```
verify:
```text
attack = 6
weaponDamage = 8
```
---
## Räuberklinge
Given:
```text
bandit-blade:
weaponDamage = 11
bonusAttack = 1
```
verify:
```text
attack = 7
weaponDamage = 11
```
---
## Armor aggregation
Given multiple armor items:
```text
sum bonusArmor
```
must be correct.
---
## HP aggregation
Given equipment with:
```text
bonusHp
```
verify:
```text
maxHp = baseHp + bonuses
```
---
## Combat Power
Verify formula:
```text
HP / 10
+ attack × 2
+ weaponDamage × 2
+ armor × 1.5
```
---
# 43. Equipment service tests
At minimum test:
## Equip owned weapon
```text
owned Räuberklinge
equip
WEAPON = Räuberklinge
```
---
## Replace weapon
Given:
```text
WEAPON = Abgenutztes Kurzschwert
```
equip Räuberklinge.
Verify:
```text
WEAPON = Räuberklinge
old sword still owned
```
---
## Item not owned
Attempt to equip another character's item.
Expected:
```text
ITEM_NOT_OWNED
```
---
## Required level
Attempt to equip an item above character level.
Expected rejection.
---
## Not equippable
Attempt to equip material/trade good.
Expected rejection.
---
## Duplicate slot protection
Concurrent or repeated operations must not produce two equipped weapons.
---
# 44. Inventory API tests
Verify:
```text
only current character items returned
```
and each item correctly reports:
```text
definition data
quantity
equipped state
```
---
# 45. Combat integration tests
This is a critical Slice 0.5 proof.
Test:
```text
combat with Abgenutztes Kurzschwert
record player damage
```
then:
```text
equip Räuberklinge
start new combat
record player damage
```
Expected:
```text
new combat deals more damage
```
Do not assert only that a stat number changed.
Prove the upgrade actually affects gameplay.
---
# 46. Existing combat snapshots
Also verify:
```text
start combat with old weapon
equip change occurs outside combat if technically possible
```
must not alter the already-created combat snapshot.
Prefer preventing equipment changes while an active combat exists.
This is simpler and fits the game model.
Recommended rule:
> Equipment cannot be changed during an active combat.
Return:
```text
CHARACTER_IN_COMBAT
```
if attempted.
---
# 47. Travel restriction
Prefer also preventing equipment changes while travelling if the current product design considers travelling an unavailable interaction state.
However, do not introduce this rule solely by assumption if existing UI already permits inventory management during travel.
Follow the established behavior of the application.
---
# 48. Frontend tests
At minimum cover:
## Inventory load
Opening:
```text
/inventory
```
loads server inventory and equipment.
---
## Item rendering
Räuberklinge displays:
```text
icon
name
weapon damage
bonus attack
```
---
## Comparison
Given equipped:
```text
Abgenutztes Kurzschwert
```
and selected:
```text
Räuberklinge
```
show the direct stat improvements.
---
## Equip request
Click:
```text
Ausrüsten
```
verify request contains only:
```json
{
"characterItemId": "uuid"
}
```
---
## Updated state
After success:
```text
Räuberklinge marked equipped
Abgenutztes Kurzschwert marked unequipped
effective attack updated
weapon damage updated
```
---
## Invalid item
Server validation errors display in-shell.
No browser alerts.
---
# 49. Visual requirements
Follow the established Ashen Realms UI:
```text
dark metal
stone
leather
bronze borders
high-quality item icons
clear rarity styling
large selected-item detail
```
The inventory must feel like an RPG screen.
Avoid:
```text
spreadsheet table
white cards
generic admin UI
Material default list
Bootstrap grid
```
---
# 50. Item rarity
Use the established rarity levels:
```text
COMMON / Basis
RARE / Selten
EPIC / Besonders
```
or the actual enum names already present in the repository.
Do not add more rarity tiers.
Rarity may affect:
```text
border treatment
name emphasis
small accent
```
but must not override readability.
---
# 51. Item icons
Item icons are an important progression signal.
Use the existing generated Ashen Realms item assets.
Do not replace them with generic icon libraries if appropriate game-specific icons already exist.
A weapon upgrade should visually feel valuable before reading every number.
---
# 52. Accessibility
Required:
```text
keyboard-accessible item selection
real buttons
visible focus states
textual equipped indicator
textual rarity
readable stat changes
disabled action explanation
```
Do not encode improvements only through green/red color.
Example:
```text
Waffenschaden 11 (+3)
```
is preferable to color alone.
---
# 53. Server-authority checklist
Server decides:
```text
which items character owns
which item is equipped
which equipment slot an item uses
whether level requirement is met
effective HP
effective attack
effective weapon damage
effective armor
Combat Power
combat snapshot stats
```
Client decides only:
```text
which owned item the player selects
whether the player requests to equip it
```
---
# 54. Database changes
Create or extend migrations for:
```text
character_equipment
```
and any missing indexes/constraints for `character_item`.
Required constraints include conceptually:
```text
unique(characterId, slot)
```
and appropriate ownership relations.
Do not drop or recreate existing loot/combat/hunt/world data.
---
# 55. Seed changes
Ensure the demo character starts with:
```text
Abgenutztes Kurzschwert
```
as a real CharacterItem and equipped WEAPON item.
The seed must remain idempotent.
Re-running must not:
```text
duplicate starting sword
duplicate equipment row
delete earned Räuberklinge
reset player inventory
reset rewards
reset XP
reset silver
```
Be especially careful not to turn the seed into a player-state reset.
---
# 56. Definition of Done
Playable Slice 0.5 is complete when this complete flow works:
```text
start with Abgenutztes Kurzschwert
travel to Verbrannte Straße
hunt Straßenräuber
win combat
Räuberklinge drops
reward persists
open Inventar
see Abgenutztes Kurzschwert equipped
select Räuberklinge
see stat comparison
click Ausrüsten
server replaces weapon
Räuberklinge becomes equipped
effective stats increase
return to hunt
start another combat
player deals more damage than before
```
---
# 57. Required verification
Before completion run:
```text
API unit tests
API integration tests
Angular tests
API build
Angular build
migration compilation
migration execution
seed execution
```
Perform manual browser walkthroughs for:
```text
inventory with starting weapon
inventory after item drop
select Räuberklinge
comparison against starting sword
equip Räuberklinge
refresh inventory page
equipped state survives refresh
start new combat
verify increased player damage
```
Also verify error cases:
```text
equip item not owned
equip invalid item
equip level-locked item
equip while active combat exists
```
where applicable.
---
# 58. Acceptance criteria
The slice is accepted when:
- inventory is backed by persistent CharacterItem data
- starting weapon uses the same item/equipment model as loot
- equipment state is persisted
- only owned items can be equipped
- equipment slots are validated server-side
- required level is validated server-side
- replacing an item does not delete the previous item
- CharacterStatsService is the authoritative effective-stat source
- combat creation uses CharacterStatsService
- active combat keeps its stat snapshot
- Räuberklinge increases weapon damage
- Räuberklinge increases attack
- next combat demonstrably deals more damage
- Angular does not calculate authoritative stats
- inventory screen matches the existing Ashen Realms visual language
- item comparison is understandable
- refresh preserves equipment state
- all tests pass
- frontend and backend builds succeed
---
# 59. Handoff after Slice 0.5
At the end of Slice 0.5, Ashen Realms has proven its first complete gameplay loop:
```text
Travel
Hunt
Choose enemy
Combat
Victory
Loot
Inventory
Equip upgrade
Become stronger
Fight again
```
This is the first major Vertical Slice milestone.
Before dramatically expanding the content, the next development step should build on this verified loop rather than introducing unrelated systems.
Possible next slices include:
```text
0.6 Full First Combat Actions
```
with:
```text
Schwerer Hieb
Schildstoß
Verteidigen
Trank
```
or:
```text
0.6 Complete Verbrannte Straße
```
with additional enemies, content and first progression pacing.
The decision should be based on which aspect of the core loop should be validated next.
---
# 60. Key gameplay proof
The most important acceptance test is not simply:
```text
Räuberklinge is equipped.
```
It is:
```text
Before:
the enemy takes X damage.
Player equips Räuberklinge.
After:
the same type of enemy takes visibly more damage.
```
If that works correctly, the first central Ashen Realms progression promise has been proven:
> **A good item makes the player meaningfully stronger.**