All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 1m2s
- Frontend: PKCE flow (oauth.ts, AuthCallback code exchange, 401 interceptor) - Backend: token introspection via SuperOAuth (no more JWT secret) - User model: superOauthId (unified) replaces oauthId+provider - Cookies httpOnly session + refresh token - POST /auth/refresh endpoint - Gitea CI workflow (vps-runner pattern) - DB_SYNC env var for initial schema creation
293 lines
11 KiB
Markdown
293 lines
11 KiB
Markdown
# TetaRdPG — Brief Sprint 4
|
||
|
||
> Statut : ⬜ À lancer
|
||
> Objectif : Succès individuels + Succès communautaires + Hall of Fame + Profil joueur enrichi
|
||
> Stack : NestJS · MySQL · TypeORM
|
||
> Prérequis : Sprint 3 livré ✅ (items, forge, craft, economy, twitch)
|
||
> Source design : `TetaRdPG/Sprint 4 _ Focus Succès & Hall of Fame.docx` + `Annexes/5. Système de succès.docx`
|
||
|
||
---
|
||
|
||
## Scope Sprint 4
|
||
|
||
### ✅ In scope
|
||
|
||
- Entité `achievements` — catalogue de succès avec critères de déblocage
|
||
- Entité `player_achievements` — suivi progression par joueur
|
||
- 5 catégories : Progression, Combat, Zones, Équipements, Économie
|
||
- Récompenses au déblocage : Or, XP bonus, titres honorifiques
|
||
- Entité `community_goals` — objectifs collectifs (monstres tués, TetardCoin cumulés)
|
||
- Barre de progression communautaire
|
||
- Récompenses communautaires : boosts globaux temporaires (XP/loot)
|
||
- Hall of Fame mensuel — classement contributeurs + badges
|
||
- Interface profil enrichi : badges, titres, % progression succès
|
||
- Seeds : 15 succès individuels + 3 objectifs communautaires
|
||
- API : voir section dédiée
|
||
|
||
### ❌ Out of scope
|
||
|
||
- Notifications Twitch temps réel (extension Twitch) — Sprint 5
|
||
- GIGABOSS communautaire (événement 72h) — Sprint événements
|
||
- Marché communautaire (échange joueurs) — Sprint économie avancée
|
||
- Guildes et alliances — Sprint social
|
||
- Boutique événementielle — Sprint économie avancée
|
||
- Frontend React complet
|
||
|
||
---
|
||
|
||
## Décisions de design (game-designer)
|
||
|
||
| Décision | Valeur | Justification |
|
||
|----------|--------|---------------|
|
||
| Tracking succès | Event-driven : chaque action (combat, craft, forge, level) émet un check | Pas de cron — cohérent avec le pattern lazy du projet |
|
||
| Catégories succès | 5 : progression, combat, zones, equipment, economy | GDD §5.1 |
|
||
| Paliers succès | 3 niveaux par succès (bronze/silver/gold) | Engagement long terme |
|
||
| Récompenses déblocage | Or + titre. Pas d'item pour éviter la complexité inventaire Sprint 4 | Simplification — items récompense = Sprint 5 |
|
||
| Titres joueur | 1 titre actif à la fois, affiché sur le profil | GDD §4 titres liés aux zones |
|
||
| Community goals | Reset mensuel, contribution individuelle trackée | GDD §5.2 |
|
||
| Hall of Fame | Classement mensuel, top 10, badges persistants | GDD §5.3 |
|
||
| Boost communautaire | Stocké en DB, appliqué comme multiplicateur dans combat/craft | Ex: +20% XP pendant 3j |
|
||
| Progression communautaire | Compteur global incrémenté à chaque action qualifiante | Pas de WebSocket — poll GET |
|
||
|
||
---
|
||
|
||
## Schéma DB
|
||
|
||
### `achievements`
|
||
```
|
||
id uuid PK
|
||
key varchar(50) UNIQUE -- 'combat_100', 'level_50', 'zone_marais_complete'
|
||
name varchar(100)
|
||
description text
|
||
category varchar(20) -- 'progression' | 'combat' | 'zones' | 'equipment' | 'economy'
|
||
tier varchar(10) -- 'bronze' | 'silver' | 'gold'
|
||
criteria_type varchar(30) -- 'combat_wins' | 'level_reached' | 'gold_accumulated' | ...
|
||
criteria_value int -- seuil à atteindre
|
||
reward_gold int default 0
|
||
reward_title varchar(100) NULL -- titre débloqué (nullable)
|
||
```
|
||
|
||
### `player_achievements`
|
||
```
|
||
id uuid PK
|
||
character_id uuid FK characters
|
||
achievement_id uuid FK achievements
|
||
progress int default 0 -- compteur courant
|
||
unlocked boolean default false
|
||
unlocked_at timestamp NULL
|
||
```
|
||
|
||
### `community_goals`
|
||
```
|
||
id uuid PK
|
||
name varchar(100)
|
||
description text
|
||
criteria_type varchar(30) -- 'total_monsters_killed' | 'total_tetardcoin' | ...
|
||
target_value bigint -- objectif collectif
|
||
current_value bigint default 0
|
||
reward_type varchar(30) -- 'xp_boost' | 'loot_boost'
|
||
reward_multiplier decimal(3,2) -- ex: 1.20 = +20%
|
||
reward_duration_hours int -- durée du boost
|
||
period_start date
|
||
period_end date
|
||
completed boolean default false
|
||
completed_at timestamp NULL
|
||
```
|
||
|
||
### `community_contributions`
|
||
```
|
||
id uuid PK
|
||
community_goal_id uuid FK community_goals
|
||
character_id uuid FK characters
|
||
contribution_value bigint default 0
|
||
```
|
||
|
||
### `hall_of_fame`
|
||
```
|
||
id uuid PK
|
||
character_id uuid FK characters
|
||
period varchar(7) -- '2026-04' format YYYY-MM
|
||
rank int
|
||
contribution_total bigint
|
||
badge varchar(50) -- 'top1_april_2026'
|
||
```
|
||
|
||
### `active_boosts` (communautaires)
|
||
```
|
||
id uuid PK
|
||
boost_type varchar(30) -- 'xp_boost' | 'loot_boost'
|
||
multiplier decimal(3,2)
|
||
expires_at timestamp
|
||
source_goal_id uuid FK community_goals
|
||
```
|
||
|
||
---
|
||
|
||
## Seeds
|
||
|
||
### Succès individuels (15)
|
||
|
||
| Key | Nom | Catégorie | Tier | Critère | Seuil | Récompense Or | Titre |
|
||
|-----|-----|-----------|------|---------|-------|---------------|-------|
|
||
| `combat_10` | Apprenti Guerrier | combat | bronze | combat_wins | 10 | 50 | — |
|
||
| `combat_100` | Guerrier Aguerri | combat | silver | combat_wins | 100 | 200 | Guerrier Aguerri |
|
||
| `combat_1000` | Légende du Combat | combat | gold | combat_wins | 1000 | 1000 | Légende |
|
||
| `level_10` | Aventurier | progression | bronze | level_reached | 10 | 100 | — |
|
||
| `level_50` | Héros | progression | silver | level_reached | 50 | 500 | Héros |
|
||
| `level_100` | Légende Vivante | progression | gold | level_reached | 100 | 2000 | Légende Vivante |
|
||
| `gold_1000` | Marchand | economy | bronze | gold_accumulated | 1000 | 100 | — |
|
||
| `gold_10000` | Négociant | economy | silver | gold_accumulated | 10000 | 500 | Négociant |
|
||
| `gold_100000` | Magnat | economy | gold | gold_accumulated | 100000 | 2000 | Magnat |
|
||
| `forge_5` | Apprenti Forgeron | equipment | bronze | forge_upgrades | 5 | 100 | — |
|
||
| `forge_25` | Maître Forgeron | equipment | silver | forge_upgrades | 25 | 500 | Maître Forgeron |
|
||
| `forge_100` | Forgeron Légendaire | equipment | gold | forge_upgrades | 100 | 2000 | Forgeron Légendaire |
|
||
| `craft_5` | Artisan Novice | equipment | bronze | craft_completed | 5 | 75 | — |
|
||
| `craft_25` | Artisan Confirmé | equipment | silver | craft_completed | 25 | 300 | Artisan |
|
||
| `craft_100` | Grand Artisan | equipment | gold | craft_completed | 100 | 1500 | Grand Artisan |
|
||
|
||
### Objectifs communautaires (3)
|
||
|
||
| Nom | Critère | Cible | Boost | Durée |
|
||
|-----|---------|-------|-------|-------|
|
||
| Chasse aux Monstres | total_monsters_killed | 10 000 | +20% XP | 72h |
|
||
| Trésor Communautaire | total_gold_earned | 1 000 000 | +15% loot | 48h |
|
||
| Fièvre de la Forge | total_forge_upgrades | 500 | +10% XP | 48h |
|
||
|
||
---
|
||
|
||
## API Sprint 4
|
||
|
||
```
|
||
# Succès individuels
|
||
GET /api/achievements → catalogue complet des succès
|
||
GET /api/achievements/me → progression du joueur (avec %)
|
||
POST /api/achievements/claim/:id → réclamer la récompense d'un succès débloqué
|
||
|
||
# Succès communautaires
|
||
GET /api/community/goals → objectifs en cours + barre progression
|
||
GET /api/community/goals/:id/top → top 10 contributeurs d'un objectif
|
||
GET /api/community/boosts → boosts actifs (multiplicateurs en cours)
|
||
|
||
# Hall of Fame
|
||
GET /api/halloffame/current → classement du mois en cours
|
||
GET /api/halloffame/:period → classement historique (ex: 2026-04)
|
||
|
||
# Profil enrichi
|
||
GET /api/profile/me → stats + titre actif + badges + succès count
|
||
PUT /api/profile/title → { title: "Héros" } → changer titre actif
|
||
```
|
||
|
||
---
|
||
|
||
## Architecture modules
|
||
|
||
```
|
||
src/
|
||
├── achievement/
|
||
│ ├── achievement.entity.ts
|
||
│ ├── player-achievement.entity.ts
|
||
│ ├── achievement.module.ts
|
||
│ ├── achievement.service.ts → check + unlock logic
|
||
│ ├── achievement.controller.ts
|
||
│ └── achievement.listener.ts → écoute events combat/craft/forge/levelup
|
||
├── community/
|
||
│ ├── community-goal.entity.ts
|
||
│ ├── community-contribution.entity.ts
|
||
│ ├── active-boost.entity.ts
|
||
│ ├── community.module.ts
|
||
│ ├── community.service.ts
|
||
│ └── community.controller.ts
|
||
├── halloffame/
|
||
│ ├── hall-of-fame.entity.ts
|
||
│ ├── halloffame.module.ts
|
||
│ ├── halloffame.service.ts → calcul mensuel + badge attribution
|
||
│ └── halloffame.controller.ts
|
||
├── profile/
|
||
│ ├── profile.module.ts
|
||
│ ├── profile.service.ts
|
||
│ └── profile.controller.ts
|
||
└── database/
|
||
├── achievements-seed.ts
|
||
└── community-goals-seed.ts
|
||
```
|
||
|
||
---
|
||
|
||
## Intégration modules existants
|
||
|
||
### CombatService — émission événements succès
|
||
|
||
```typescript
|
||
// Après résolution combat — émettre pour achievement tracker
|
||
if (result.winner === 'player') {
|
||
this.eventEmitter.emit('achievement.check', {
|
||
characterId: character.id,
|
||
type: 'combat_wins',
|
||
increment: 1,
|
||
});
|
||
this.eventEmitter.emit('community.contribute', {
|
||
characterId: character.id,
|
||
type: 'total_monsters_killed',
|
||
increment: 1,
|
||
});
|
||
}
|
||
```
|
||
|
||
### ForgeService / CraftService — même pattern
|
||
|
||
```typescript
|
||
// Après forge réussie
|
||
this.eventEmitter.emit('achievement.check', {
|
||
characterId, type: 'forge_upgrades', increment: 1,
|
||
});
|
||
this.eventEmitter.emit('community.contribute', {
|
||
characterId, type: 'total_forge_upgrades', increment: 1,
|
||
});
|
||
```
|
||
|
||
### Boosts actifs — application dans CombatEngine
|
||
|
||
```typescript
|
||
// Dans CombatService — vérifier boosts communautaires actifs
|
||
const xpBoost = await this.communityService.getActiveMultiplier('xp_boost');
|
||
rewards.xp = Math.floor(baseXp * xpBoost); // xpBoost = 1.0 si aucun boost
|
||
```
|
||
|
||
---
|
||
|
||
## Migration TypeORM
|
||
|
||
```
|
||
Sprint4Achievements — 6 tables :
|
||
achievements, player_achievements,
|
||
community_goals, community_contributions,
|
||
hall_of_fame, active_boosts
|
||
|
||
+ ALTER characters ADD active_title VARCHAR(100) NULL
|
||
+ ALTER characters ADD total_gold_earned BIGINT DEFAULT 0 -- tracking cumulé pour succès
|
||
```
|
||
|
||
---
|
||
|
||
## Critères de validation integrator
|
||
|
||
- [ ] `GET /api/achievements` → 15 succès seedés, 5 catégories
|
||
- [ ] `POST /api/combat/start` → victoire → `player_achievements.progress` incrémenté pour `combat_wins`
|
||
- [ ] 10 victoires → succès `combat_10` débloqué automatiquement
|
||
- [ ] `GET /api/achievements/me` → progression visible avec %
|
||
- [ ] `POST /api/achievements/claim/:id` → or crédité, titre disponible
|
||
- [ ] Claim succès déjà réclamé → 400
|
||
- [ ] `PUT /api/profile/title` → titre actif changé
|
||
- [ ] `GET /api/profile/me` → titre, badges, count succès
|
||
- [ ] `GET /api/community/goals` → 3 objectifs avec barre progression
|
||
- [ ] Combat victoire → `community_contributions` incrémentée
|
||
- [ ] `GET /api/community/goals/:id/top` → top 10 contributeurs
|
||
- [ ] Objectif communautaire atteint → boost créé dans `active_boosts`
|
||
- [ ] `GET /api/community/boosts` → multiplicateur actif
|
||
- [ ] Combat avec boost actif → XP = baseXp × multiplier
|
||
- [ ] Boost expiré → non retourné par GET
|
||
- [ ] `GET /api/halloffame/current` → classement mois en cours
|
||
- [ ] Sans cookie → 401 sur toutes les routes protégées
|
||
- [ ] Level up → `player_achievements.progress` incrémenté pour `level_reached`
|
||
- [ ] Forge → incrémente `forge_upgrades` + `total_forge_upgrades` communautaire
|