# 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