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:
14
src/monster/monster.controller.ts
Normal file
14
src/monster/monster.controller.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
39
src/monster/monster.entity.ts
Normal file
39
src/monster/monster.entity.ts
Normal 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;
|
||||
}
|
||||
14
src/monster/monster.module.ts
Normal file
14
src/monster/monster.module.ts
Normal 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 {}
|
||||
22
src/monster/monster.service.ts
Normal file
22
src/monster/monster.service.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user