245 lines
7.3 KiB
Markdown
245 lines
7.3 KiB
Markdown
# 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
|