1929 lines
25 KiB
Markdown
1929 lines
25 KiB
Markdown
# 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.** |