Files
TetaRdPG/src/combat/turn/turn-combat.controller.ts
Tetardtek 9d50adf523 feat: Combat tour par tour — Phases A-D complètes
TurnManager stateless avec sessions en mémoire (TTL 10min).
SpellSystem : 15 sorts (5 par voie du Dao), mana, cooldowns, buffs/debuffs.
CompanionAI : Mira (heal/support) et Vell (tank/dps) — IA contextuelle.
Monster AI : 3 profils (agressif, défensif, chaotique).

Nouvelles entités : Spell, PlayerSpell, PlayerDaoPath.
Character +mana. Monster +aiProfile +isBoss.
Migration : 1743004800000-TurnCombatSystem.

Frontend : TurnCombatPage (select/combat/result), sélecteur compagnon,
barres HP/MP, log scrollable, sous-menu sorts avec cooldowns.

Endpoints : 8 routes sous /combat/turn/ (start, action, session, spells,
unlocked, unlock, dao, dao/choose).

Combat simple (POST /combat/start) et grind ×5/×10 inchangés.
2026-03-25 00:58:47 +01:00

118 lines
3.2 KiB
TypeScript

import {
Controller,
Post,
Get,
Body,
Param,
UseGuards,
Req,
HttpCode,
HttpStatus,
} from '@nestjs/common';
import { Request } from 'express';
import { TurnCombatService } from './turn-combat.service';
import { SpellSystem } from './spell.system';
import { StartTurnCombatDto } from './dto/start-turn-combat.dto';
import { TurnActionDto } from './dto/turn-action.dto';
import { ChooseDaoPathDto } from './dto/choose-dao-path.dto';
import { UnlockSpellDto } from './dto/unlock-spell.dto';
import { AuthGuard } from '../../auth/guards/auth.guard';
import { User } from '../../user/user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Character } from '../../character/entities/character.entity';
@Controller('combat/turn')
@UseGuards(AuthGuard)
export class TurnCombatController {
constructor(
private readonly turnCombatService: TurnCombatService,
private readonly spellSystem: SpellSystem,
@InjectRepository(Character)
private readonly characterRepo: Repository<Character>,
) {}
// ---------- Combat tour par tour ----------
@Post('start')
@HttpCode(HttpStatus.OK)
startCombat(
@Body() dto: StartTurnCombatDto,
@Req() req: Request & { user: User },
) {
return this.turnCombatService.startSession(dto, req.user);
}
@Post('action')
@HttpCode(HttpStatus.OK)
submitAction(
@Body() dto: TurnActionDto,
@Req() req: Request & { user: User },
) {
return this.turnCombatService.submitAction(
dto.sessionId,
{ type: dto.type, spellId: dto.spellId, itemId: dto.itemId },
req.user.id,
);
}
@Get('session/:sessionId')
getSession(
@Param('sessionId') sessionId: string,
@Req() req: Request & { user: User },
) {
return this.turnCombatService.getSession(sessionId, req.user.id);
}
// ---------- Dao & Sorts ----------
@Get('spells')
getAllSpells() {
return this.spellSystem.getAllSpells();
}
@Get('spells/unlocked')
async getUnlockedSpells(@Req() req: Request & { user: User }) {
const character = await this.getCharacter(req.user.id);
return this.spellSystem.getUnlockedSpells(character.id);
}
@Post('spells/unlock')
@HttpCode(HttpStatus.OK)
async unlockSpell(
@Body() dto: UnlockSpellDto,
@Req() req: Request & { user: User },
) {
const character = await this.getCharacter(req.user.id);
return this.spellSystem.unlockSpell(character.id, dto.spellId);
}
@Get('dao')
async getDaoPaths(@Req() req: Request & { user: User }) {
const character = await this.getCharacter(req.user.id);
return this.spellSystem.getDaoPaths(character.id);
}
@Post('dao/choose')
@HttpCode(HttpStatus.OK)
async chooseDaoPath(
@Body() dto: ChooseDaoPathDto,
@Req() req: Request & { user: User },
) {
const character = await this.getCharacter(req.user.id);
return this.spellSystem.choosePrimaryPath(character.id, dto.path);
}
// ---------- Helper ----------
private async getCharacter(userId: string): Promise<Character> {
const character = await this.characterRepo.findOne({
where: { userId },
});
if (!character) {
throw new Error('Aucun personnage trouve');
}
return character;
}
}