feat: Sprint 2 — moteur de combat PvE TetaRdPG

Moteur combat stateless (POST /api/combat/start résout le combat complet).
Formules GDD : Mêlée/Distance/Magie × 1.5, critique (5% + Chance×0.2%), esquive (5% + Chance×0.1%).
5 monstres seedés (Têtard Vase → Golem de Boue, level 1–9).
Level up : XP → seuil atteint → level++, +5 statPoints.
Persiste combat_logs (jsonb rounds). Validé : victoire, défaite, 401, 400, 404.
This commit is contained in:
2026-03-15 06:10:06 +01:00
parent da3237bf3f
commit 6d1230d16a
15 changed files with 806 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
import { Controller, Get, UseGuards } from '@nestjs/common';
import { MonsterService } from './monster.service';
import { AuthGuard } from '../auth/guards/auth.guard';
@Controller('monsters')
@UseGuards(AuthGuard)
export class MonsterController {
constructor(private readonly monsterService: MonsterService) {}
@Get()
findAll() {
return this.monsterService.findAll();
}
}

View File

@@ -0,0 +1,39 @@
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
export type AttackType = 'melee' | 'ranged' | 'magic';
@Entity('monsters')
export class Monster {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ length: 100 })
name: string;
@Column({ name: 'min_level' })
minLevel: number;
@Column({ name: 'max_level' })
maxLevel: number;
@Column()
hp: number;
@Column()
attack: number;
@Column({ default: 0 })
defense: number;
@Column({ name: 'attack_type', type: 'varchar', length: 20 })
attackType: AttackType;
@Column({ name: 'xp_reward' })
xpReward: number;
@Column({ name: 'gold_min' })
goldMin: number;
@Column({ name: 'gold_max' })
goldMax: number;
}

View File

@@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Monster } from './monster.entity';
import { MonsterService } from './monster.service';
import { MonsterController } from './monster.controller';
import { AuthModule } from '../auth/auth.module';
@Module({
imports: [TypeOrmModule.forFeature([Monster]), AuthModule],
controllers: [MonsterController],
providers: [MonsterService],
exports: [MonsterService, TypeOrmModule],
})
export class MonsterModule {}

View File

@@ -0,0 +1,22 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Monster } from './monster.entity';
@Injectable()
export class MonsterService {
constructor(
@InjectRepository(Monster)
private readonly monsterRepository: Repository<Monster>,
) {}
findAll(): Promise<Monster[]> {
return this.monsterRepository.find({ order: { minLevel: 'ASC' } });
}
async findOne(id: string): Promise<Monster> {
const monster = await this.monsterRepository.findOne({ where: { id } });
if (!monster) throw new NotFoundException(`Monstre introuvable`);
return monster;
}
}