# 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 1–5 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.1–2 : 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 : 15min–2h |

---

## 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
