feat(sprint3): items + forge + craft + loot — équipement, artisanat lazy-calc, forge risque GDD

This commit is contained in:
2026-03-15 08:22:20 +01:00
parent 6d1230d16a
commit 23f7dd0f3c
25 changed files with 1169 additions and 2 deletions

244
SPRINT3.md Normal file
View File

@@ -0,0 +1,244 @@
# TetaRdPG — Brief Sprint 3
> Statut : 🔄 En cours
> Objectif : Items + Inventaire + Artisanat (Craft) + Forge
> Stack : NestJS · PostgreSQL · TypeORM (synchronize dev)
> Prérequis : Sprint 2 livré ✅
---
## Scope Sprint 3
### ✅ In scope
- Entité `items` (armes + armures) avec bonus stats
- Inventaire joueur (`character_items`) — possession + équipement actif
- Intégration combat : `player.attack` et `player.defense` depuis l'équipement équipé
- Entité `materials` + inventaire joueur (`character_materials`) — loot post-combat
- Entité `recipes` + ingrédients en jsonb
- Artisanat (`craft_jobs`) — lazy calc timer (même pattern que endurance)
- Forge — amélioration item niveau 15 avec risque croissant
- Seeds : 5 items de base, 5 matériaux, 3 recettes
- API : voir section dédiée
### ❌ Out of scope
- Boutique (achat/vente) — Sprint 4
- Bonus de sets d'équipement — Sprint 4
- TetardCoin (accélération craft, forge garantie) — sprint monétisation
- Twitch, PvP, guildes
- Frontend React
---
## Décisions de design (game-designer)
| Décision | Valeur | Justification |
|----------|--------|---------------|
| Slots équipement | weapon + armor | Simplifié Sprint 3 — casque/bottes Sprint 4 |
| Player.attack en combat | `char.weapon?.attackBonus ?? 0` | Remplace attack=0 Sprint 2 |
| Player.defense en combat | `char.armor?.defenseBonus ?? 0` | Remplace defense=0 Sprint 2 |
| Loot drop | 40% de chance après victoire | 1 matériau aléatoire parmi ceux du monstre |
| Craft timer | lazy calc : `startedAt + durationMs` | Même pattern endurance — zéro job schedulé |
| Forge risque | Niv.12 : 0% | 3 : 20% | 4 : 30% | 5 : 40% | GDD exact |
| Forge succès garanti | 12 TetardCoin (non implémenté Sprint 3) | Placeholder, champ `forcedSuccess` |
| Forge échec | item inchangé, pas de coût matériaux Sprint 3 | coût matériaux forge = Sprint 4 |
| Durée craft | court: 15s (dev) / long: 60s (dev) | Valeurs réelles en prod : 15min2h |
---
## Schéma DB
### `items`
```
id uuid PK
name varchar(100)
description text
type varchar(20) -- 'weapon' | 'armor'
rarity varchar(20) -- 'common' | 'rare' | 'epic' | 'legendary'
attack_bonus int default 0
defense_bonus int default 0
force_bonus int default 0
agilite_bonus int default 0
intelligence_bonus int default 0
chance_bonus int default 0
vitalite_bonus int default 0
```
### `character_items`
```
id uuid PK
character_id uuid FK characters
item_id uuid FK items
forge_level int default 0 -- 0 = non forgé
equipped boolean default false
acquired_at timestamp
```
### `materials`
```
id uuid PK
name varchar(100)
description text
rarity varchar(20)
```
### `character_materials`
```
id uuid PK
character_id uuid FK characters
material_id uuid FK materials
quantity int default 0
```
### `recipes`
```
id uuid PK
name varchar(100)
result_item_id uuid FK items
craft_duration_seconds int
endurance_cost int
ingredients jsonb -- [{ materialId, quantity }]
```
### `craft_jobs`
```
id uuid PK
character_id uuid FK characters
recipe_id uuid FK recipes
started_at timestamp
completed_at timestamp -- lazy : startedAt + duration
collected boolean default false
```
---
## Seeds
### Items (5)
| Nom | Type | Rareté | Attack | Defense | Notes |
|-----|------|--------|--------|---------|-------|
| Bâton de Roseau | weapon | common | 3 | 0 | Arme de départ |
| Dague Rouillée | weapon | common | 5 | 0 | |
| Épée Courte | weapon | rare | 9 | 0 | |
| Gilet de Cuir | armor | common | 0 | 3 | |
| Cotte de Mailles | armor | rare | 0 | 7 | |
### Matériaux (5)
| Nom | Rareté | Sources |
|-----|--------|---------|
| Bave de Têtard | common | Têtard Vase |
| Écailles de Grenouille | common | Grenouille Boueuse |
| Venin de Serpent | rare | Serpent des Marais |
| Spores Vénéneuses | rare | Champi Vénéneux |
| Fragment de Boue | common | Golem de Boue |
### Recettes (3)
| Nom | Résultat | Durée (dev) | Endurance | Ingrédients |
|-----|----------|-------------|-----------|-------------|
| Forge Bâton Renforcé | Bâton de Roseau+? | 15s | 5 | 2× Bave de Têtard |
| Craft Dague | Dague Rouillée | 15s | 8 | 3× Bave + 1× Écaille |
| Craft Gilet de Cuir | Gilet de Cuir | 30s | 10 | 3× Écaille + 2× Fragment |
---
## API Sprint 3
```
# Items
GET /api/items → liste tous les items (catalogue)
GET /api/items/inventory → inventaire du personnage connecté
POST /api/items/equip/:itemId → équiper un item (character_items.id)
POST /api/items/unequip/:slot → déséquiper un slot (weapon|armor)
# Materials
GET /api/materials → catalogue matériaux
GET /api/materials/inventory → matériaux du personnage connecté
# Craft
GET /api/craft/recipes → liste recettes
POST /api/craft/start → { recipeId } → lance le craft
GET /api/craft/active → craft en cours (lazy status)
POST /api/craft/collect/:jobId → collecter si terminé
# Forge
POST /api/forge/upgrade → { characterItemId } → tente amélioration
```
---
## Architecture modules
```
src/
├── item/
│ ├── item.entity.ts
│ ├── character-item.entity.ts
│ ├── item.module.ts
│ ├── item.service.ts
│ └── item.controller.ts
├── material/
│ ├── material.entity.ts
│ ├── character-material.entity.ts
│ ├── material.module.ts
│ ├── material.service.ts
│ └── material.controller.ts
├── craft/
│ ├── recipe.entity.ts
│ ├── craft-job.entity.ts
│ ├── craft.module.ts
│ ├── craft.service.ts
│ └── craft.controller.ts
├── forge/
│ ├── forge.module.ts
│ ├── forge.service.ts
│ └── forge.controller.ts
└── database/
└── items-seed.ts
```
---
## Intégration combat (CombatEngine)
```typescript
// Dans CombatService.startCombat() — charger l'équipement
const weaponBonus = char.equippedWeapon?.item.attackBonus ?? 0;
const armorBonus = char.equippedArmor?.item.defenseBonus ?? 0;
const playerStats: CombatantStats = {
...
attack: weaponBonus, // était 0 Sprint 2
defense: armorBonus, // était 0 Sprint 2
};
```
Loot post-victoire :
```typescript
if (result.winner === 'player' && Math.random() < 0.4) {
// créditer 1 matériau aléatoire dans character_materials
}
```
---
## Critères de validation integrator
- [ ] `GET /api/items` → 5 items seedés
- [ ] `GET /api/materials` → 5 matériaux seedés
- [ ] `POST /api/items/equip/:id` → item équipé, character mis à jour
- [ ] Combat avec arme équipée → player.attack = weaponBonus
- [ ] Victoire combat → chance de loot matériau (40%)
- [ ] `GET /api/materials/inventory` → quantité augmentée après loot
- [ ] `GET /api/craft/recipes` → 3 recettes disponibles
- [ ] `POST /api/craft/start` → job créé, endurance déduite
- [ ] `GET /api/craft/active` → status `pending` | `ready` | `none`
- [ ] `POST /api/craft/collect/:jobId` → item ajouté à l'inventaire
- [ ] Collect avant fin → 400 "not ready yet"
- [ ] `POST /api/forge/upgrade` → niveau 1 : succès garanti
- [ ] Forge niveau 3 → 20% chance échec (matériaux déduits, forgeLvl inchangé)
- [ ] Sans cookie → 401 sur toutes les routes protégées
- [ ] Endurance insuffisante pour craft → 400