25 KiB
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:
World
→ Travel
→ Hunt
→ Encounter
→ Combat
→ Victory
→ Reward
becomes:
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:
Abgenutztes Kurzschwert
8 Waffenschaden
becomes:
Räuberklinge
11 Waffenschaden
+1 Angriff
and the next combat deals more damage.
2. Core player flow
The intended flow is:
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:
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:
Abgenutztes Kurzschwert
Räuberklinge
Räuberhaube
where those items already exist in content.
4. Explicit non-goals
Do not implement:
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:
20 % strength from level/base stats
80 % strength from equipment
Therefore equipment changes must be noticeable.
The player should immediately understand:
old weapon
↓
new weapon
↓
more damage
Do not hide the effect behind tiny percentage changes.
6. Equipment slots
The final Vertical Slice uses:
WEAPON
HEAD
CHEST
HANDS
LEGS
FEET
AMULET
Slice 0.5 should support this enum/data model cleanly.
However, the actual playable demonstration only requires:
WEAPON
and optionally:
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:
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:
worn-short-sword
Stats:
equipmentSlot: WEAPON
requiredLevel: 1
weaponDamage: 8
bonusAttack: 0
bonusHp: 0
bonusArmor: 0
This is the starting weapon.
9. Räuberklinge
Stable key:
bandit-blade
Stats:
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:
bandit-hood
Stats:
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:
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:
apps/api/src/equipment/entities/character-equipment.entity.ts
Conceptual fields:
id: uuid;
characterId: uuid;
slot: EquipmentSlot;
characterItemId: uuid;
createdAt: timestamptz;
updatedAt: timestamptz;
Important invariants:
one equipped item per character per slot
and:
one CharacterItem cannot occupy multiple equipment slots simultaneously
Protect these through service validation and appropriate database constraints.
13. Ownership rule
The server must validate:
CharacterItem belongs to current character
before equipment changes.
Never accept:
ItemDefinition.id
alone as sufficient proof of ownership.
Equip requests should identify the owned item:
CharacterItem.id
14. Equip validation
Before equipping:
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:
POST /api/equipment
with:
{
"characterItemId": "uuid"
}
The server derives:
slot
from the item definition.
15. Equip replacement behavior
If the slot already contains an item:
current item
↓
new item equipped
↓
old item becomes unequipped
↓
old item remains in inventory
Do not destroy or delete the old CharacterItem.
Example:
WEAPON:
Abgenutztes Kurzschwert
then equip:
Räuberklinge
result:
WEAPON:
Räuberklinge
Inventory:
Abgenutztes Kurzschwert still owned
16. Unequip behavior
For Slice 0.5, an explicit unequip endpoint is optional.
If implemented:
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:
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:
CharacterStatsService
This becomes the single authoritative source for effective character stats.
Conceptual return type:
interface EffectiveCharacterStats {
maxHp: number;
currentHp: number;
attack: number;
weaponDamage: number;
armor: number;
combatPower: number;
}
The service calculates values from:
Character base stats
+
equipped ItemDefinitions
Do not calculate effective stats in:
Angular
CombatController
InventoryController
19. Base stats
The starting level-1 character uses:
baseHp = 100
baseAttack = 6
Equipment supplies:
weaponDamage
armor
bonusHp
bonusAttack
Starting equipment target:
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:
maxHp =
baseHp
+ sum equipped bonusHp
attack =
baseAttack
+ sum equipped bonusAttack
weaponDamage =
equipped weapon weaponDamage
armor =
sum equipped bonusArmor
If no weapon is equipped:
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:
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:
Combat creation
↓
CharacterStatsService.calculate(...)
↓
snapshot current effective equipment-derived stats
Therefore equipping Räuberklinge affects:
future combats
but must not retroactively alter:
already-active combat
This snapshot behavior is important.
23. Example power change
Before upgrade:
Base attack: 6
Weapon damage: 8
Raw attack damage:
14
After equipping Räuberklinge:
Attack:
6 + 1 = 7
Weapon damage:
11
Raw attack damage:
18
Therefore the upgrade should be clearly noticeable.
24. Inventory API
Implement:
GET /api/inventory
The server resolves the current character.
Do not send:
characterId
from Angular.
Example response:
{
"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:
GET /api/equipment
Response conceptually:
{
"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:
POST /api/equipment
Request:
{
"characterItemId": "uuid"
}
Server determines:
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:
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:
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:
/inventory
Enable:
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:
left / center:
inventory item slots/grid
right:
selected item details / comparison
Avoid:
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:
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:
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:
name
icon
rarity
equipment slot
required level
stats
equipped state
Example:
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:
Räuberklinge
11 Waffenschaden +3
+1 Angriff +1
compared to:
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:
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:
Ausrüsten
Clicking it calls:
POST /api/equipment
After success:
inventory state refreshes
equipment state refreshes
effective stats refresh
equipped indicator updates
36. Already equipped state
If the selected item is already equipped:
Ausgerüstet
should be clearly visible.
Do not show another active:
Ausrüsten
button for the same item.
37. Required-level state
If the player's level is too low:
Display:
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:
Waffe
Kopf
Brust
Handschuhe
Beine
Stiefel
Amulett
Slots without items should clearly show:
Leer
Do not add fake starter gear to fill every visual slot.
39. Effective stats presentation
Display a small character stat summary:
Leben
Angriff
Waffenschaden
Rüstung
These values must come from the backend.
Example before:
Angriff: 6
Waffenschaden: 8
after:
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:
Inventar öffnen
when equipment loot is obtained.
Example:
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:
baseAttack = 6
baseHp = 100
worn-short-sword:
weaponDamage = 8
verify:
attack = 6
weaponDamage = 8
Räuberklinge
Given:
bandit-blade:
weaponDamage = 11
bonusAttack = 1
verify:
attack = 7
weaponDamage = 11
Armor aggregation
Given multiple armor items:
sum bonusArmor
must be correct.
HP aggregation
Given equipment with:
bonusHp
verify:
maxHp = baseHp + bonuses
Combat Power
Verify formula:
HP / 10
+ attack × 2
+ weaponDamage × 2
+ armor × 1.5
43. Equipment service tests
At minimum test:
Equip owned weapon
owned Räuberklinge
↓
equip
↓
WEAPON = Räuberklinge
Replace weapon
Given:
WEAPON = Abgenutztes Kurzschwert
equip Räuberklinge.
Verify:
WEAPON = Räuberklinge
old sword still owned
Item not owned
Attempt to equip another character's item.
Expected:
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:
only current character items returned
and each item correctly reports:
definition data
quantity
equipped state
45. Combat integration tests
This is a critical Slice 0.5 proof.
Test:
combat with Abgenutztes Kurzschwert
↓
record player damage
then:
equip Räuberklinge
↓
start new combat
↓
record player damage
Expected:
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:
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:
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:
/inventory
loads server inventory and equipment.
Item rendering
Räuberklinge displays:
icon
name
weapon damage
bonus attack
Comparison
Given equipped:
Abgenutztes Kurzschwert
and selected:
Räuberklinge
show the direct stat improvements.
Equip request
Click:
Ausrüsten
verify request contains only:
{
"characterItemId": "uuid"
}
Updated state
After success:
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:
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:
spreadsheet table
white cards
generic admin UI
Material default list
Bootstrap grid
50. Item rarity
Use the established rarity levels:
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:
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:
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:
Waffenschaden 11 (+3)
is preferable to color alone.
53. Server-authority checklist
Server decides:
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:
which owned item the player selects
whether the player requests to equip it
54. Database changes
Create or extend migrations for:
character_equipment
and any missing indexes/constraints for character_item.
Required constraints include conceptually:
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:
Abgenutztes Kurzschwert
as a real CharacterItem and equipped WEAPON item.
The seed must remain idempotent.
Re-running must not:
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:
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:
API unit tests
API integration tests
Angular tests
API build
Angular build
migration compilation
migration execution
seed execution
Perform manual browser walkthroughs for:
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:
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:
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:
0.6 – Full First Combat Actions
with:
Schwerer Hieb
Schildstoß
Verteidigen
Trank
or:
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:
Räuberklinge is equipped.
It is:
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.