Files
back-ts/src/games/controllers/turn.controller.ts
T
2024-07-14 14:49:24 +02:00

437 lines
14 KiB
TypeScript

import {
Body,
Controller,
HttpException,
HttpStatus,
Param,
Post,
Session,
UseGuards,
} from '@nestjs/common';
import { AuthGuard } from '../guards/auth.guard';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { Game, Player } from '../schemas/game.schema';
import { Document, Model } from 'mongoose';
import { GameStartedPipe } from '../pipe/game.pipe';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { TurnService } from '../services/turn.service';
import {
getPlayer,
getCurrentTeam,
isInTeam,
findUser,
findPlayer,
} from '../utils';
import { CardPipe } from '../pipe/card.pipe';
import { Card } from 'src/cards/schemas/cards.schema';
import { EffectPipe } from '../pipe/effect.pipe';
import { Effect } from 'src/cards/schemas/effect.schema';
import { GameService } from '../services/games.service';
import { CardEffects, CardType } from 'src/cards/schemas/cards.types';
import { UseTokenDTO } from '../dto/use-token-dto';
import { DamagePlayerDTO } from '../dto/damage-player-dto';
import { DamageChampionDTO } from '../dto/damage-champion-dto';
import { equal } from 'assert';
@ApiTags('turn')
@Controller('games/:gameId/turn')
export class TurnController {
constructor(
private readonly turnService: TurnService,
private readonly gameService: GameService,
private eventEmitter: EventEmitter2,
) {}
@Post('lockCard/:cardId')
@ApiOperation({ summary: 'Lock a card' })
@UseGuards(AuthGuard)
async lockCard(
@Param('gameId') _gameId: string,
@Param('cardId') _cardId: string,
@Param('gameId', GameStartedPipe) game: Document<Game> & Game,
@Param('cardId', CardPipe) card: Document<Card> & Card,
@Session() session: Record<string, string>,
) {
const currentPlayer = getPlayer(session, game);
const currentTeam = getCurrentTeam(game);
if (!isInTeam(currentTeam, currentPlayer)) {
throw new HttpException(
'It is not your turn to play',
HttpStatus.FORBIDDEN,
);
}
if (currentPlayer.discardMarkers || currentPlayer.fuckUMarkers) {
throw new HttpException(
'You cannot lock a card while having discard markers',
HttpStatus.BAD_REQUEST,
);
}
if (game.currentTurn.cardsLocked.some((c) => c._id.equals(card._id))) {
throw new HttpException('Card already locked', HttpStatus.BAD_REQUEST);
}
if (!currentPlayer.board.cards.some((c) => c._id.equals(card._id))) {
throw new HttpException(
'Card not found in player board',
HttpStatus.NOT_FOUND,
);
}
await this.turnService.lockCard(game, currentPlayer, card);
}
@Post('buyCard/:cardId')
@ApiOperation({ summary: 'Buys a card from the market' })
@UseGuards(AuthGuard)
async buyCard(
@Param('gameId') _gameId: string,
@Param('cardId') _cardId: string,
@Param('gameId', GameStartedPipe) game: Document<Game> & Game,
@Param('cardId', CardPipe) card: Document<Card> & Card,
@Session() session: Record<string, string>,
) {
const currentPlayer = getPlayer(session, game);
const currentTeam = getCurrentTeam(game);
if (!isInTeam(currentTeam, currentPlayer)) {
throw new HttpException(
'It is not your turn to play',
HttpStatus.FORBIDDEN,
);
}
if (currentPlayer.discardMarkers || currentPlayer.fuckUMarkers) {
throw new HttpException(
'You cannot buy a card while having discard markers',
HttpStatus.BAD_REQUEST,
);
}
if (!game.market.cards.some((c) => c._id.equals(card._id))) {
throw new HttpException(
'Card not in current game market',
HttpStatus.NOT_FOUND,
);
}
if (game.currentTurn.gold < card.cost) {
throw new HttpException('Not enough gold', HttpStatus.BAD_REQUEST);
}
await this.turnService.buyGeneric(
game,
card,
game.market,
currentPlayer.discard,
);
await this.gameService.fillMarket(game);
}
@Post('buyGem')
@ApiOperation({ summary: 'Buys a gem from the market' })
@UseGuards(AuthGuard)
async buyGem(
@Param('gameId') _gameId: string,
@Param('gameId', GameStartedPipe) game: Document<Game> & Game,
@Session() session: Record<string, string>,
) {
const currentPlayer = getPlayer(session, game);
const currentTeam = getCurrentTeam(game);
if (!isInTeam(currentTeam, currentPlayer)) {
throw new HttpException(
'It is not your turn to play',
HttpStatus.FORBIDDEN,
);
}
if (currentPlayer.discardMarkers || currentPlayer.fuckUMarkers) {
throw new HttpException(
'You cannot buy a card while having discard markers',
HttpStatus.BAD_REQUEST,
);
}
if (game.fireGems.cards.length < 1) {
throw new HttpException('No Gems left', HttpStatus.NOT_FOUND);
}
const targetCard = game.fireGems.cards[0];
await this.turnService.buyGeneric(
game,
targetCard,
game.fireGems,
currentPlayer.discard,
);
}
@Post('lockEffect/:effectId')
@ApiOperation({ summary: 'Lock an effect' })
@UseGuards(AuthGuard)
async lockEffect(
@Param('gameId') _gameId: string,
@Param('effectId') _effectId: string,
@Param('gameId', GameStartedPipe) game: Document<Game> & Game,
@Param('effectId', EffectPipe) effect: Document<Effect> & Effect,
@Session() session: Record<string, string>,
) {
const currentPlayer = getPlayer(session, game);
const currentTeam = getCurrentTeam(game);
if (!isInTeam(currentTeam, currentPlayer)) {
throw new HttpException(
'It is not your turn to play',
HttpStatus.FORBIDDEN,
);
}
if (currentPlayer.discardMarkers || currentPlayer.fuckUMarkers) {
throw new HttpException(
'You cannot lock an effect while having discard markers',
HttpStatus.BAD_REQUEST,
);
}
if (game.currentTurn.lockedEffects.some((e) => e._id.equals(effect._id))) {
throw new HttpException('Effect already locked', HttpStatus.BAD_REQUEST);
}
const card = (await effect.populate('card')).card;
if (!currentPlayer.board.cards.some((c) => c._id.equals(card._id))) {
throw new HttpException(
'Card not found in player board',
HttpStatus.NOT_FOUND,
);
}
if (!game.currentTurn.cardsLocked.some((c) => c._id.equals(card._id))) {
throw new HttpException('Card not locked', HttpStatus.BAD_REQUEST);
}
await this.turnService.lockEffect(game, currentPlayer, effect, card);
}
@Post('makeDiscard/:playerId')
@ApiOperation({ summary: 'Use a token to make discard a player' })
@UseGuards(AuthGuard)
async makeDiscard(
@Param('gameId') _gameId: string,
@Param('playerId') _playerId: string,
@Param('gameId', GameStartedPipe) game: Document<Game> & Game,
@Session() session: Record<string, string>,
) {
const currentPlayer = getPlayer(session, game);
const currentTeam = getCurrentTeam(game);
if (!isInTeam(currentTeam, currentPlayer)) {
throw new HttpException(
'It is not your turn to play',
HttpStatus.FORBIDDEN,
);
}
const targetPlayer = findPlayer(_playerId, game);
if (isInTeam(currentTeam, targetPlayer)) {
throw new HttpException(
'You cannot target players in the same team as yours',
HttpStatus.FORBIDDEN,
);
}
const targetToken = game.currentTurn.tokens.find(
(t) => t.effect == CardEffects.MAKE_DISCARD,
);
if (!targetToken) {
throw new HttpException('Token not found', HttpStatus.NOT_FOUND);
}
await this.turnService.makeDiscard(game, targetToken, targetPlayer);
const index = game.currentTurn.tokens.findIndex((t) =>
t._id.equals(targetToken._id),
);
game.currentTurn.tokens.splice(index, 1);
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'currentTurn.useToken',
tokenId: targetToken._id.toHexString(),
});
}
@Post('useToken/:tokenId')
@ApiOperation({ summary: 'Use a token' })
@UseGuards(AuthGuard)
async useToken(
@Param('gameId') _gameId: string,
@Param('tokenId') _tokenId: string,
@Param('gameId', GameStartedPipe) game: Document<Game> & Game,
@Param('tokenId', EffectPipe) token: Document<Effect> & Effect,
@Body() useTokenDto: UseTokenDTO,
@Session() session: Record<string, string>,
) {
const currentPlayer = getPlayer(session, game);
const currentTeam = getCurrentTeam(game);
if (!isInTeam(currentTeam, currentPlayer)) {
throw new HttpException(
'It is not your turn to play',
HttpStatus.FORBIDDEN,
);
}
if (!game.currentTurn.tokens.some((t) => t._id.equals(token._id))) {
throw new HttpException('Token not found', HttpStatus.NOT_FOUND);
}
const enemyTargetTokens = [
CardEffects.STUN,
CardEffects.CONTROL_OPPOSING_CHAMPION_PASSIVE,
];
const marketTokens = [
CardEffects.STACK_NEXT_ACTION_BOUGHT,
CardEffects.STACK_NEXT_CARD_BOUGHT,
CardEffects.PLAY_NEXT_CARD_BOUGHT,
];
let targetCard: Card;
let targetPlayer = currentPlayer;
if (enemyTargetTokens.includes(token.effect)) {
targetPlayer = findPlayer(useTokenDto.playerId, game);
targetCard = targetPlayer.board.cards.find((c) =>
c._id.equals(useTokenDto.cardId),
);
} else if (marketTokens.includes(token.effect)) {
targetCard =
game.market.cards.find((c) => c._id.equals(useTokenDto.cardId)) ??
game.fireGems.cards.find((c) => c._id.equals(useTokenDto.cardId));
} else {
targetCard = currentPlayer.board.cards.find((c) =>
c._id.equals(useTokenDto.cardId),
);
}
if (token.effect != CardEffects.DRAW_AND_DISCARD) {
if (currentPlayer.discardMarkers || currentPlayer.fuckUMarkers) {
throw new HttpException(
'You cannot use a token while having discard markers',
HttpStatus.BAD_REQUEST,
);
}
if (!targetCard) {
throw new HttpException('Card not found', HttpStatus.NOT_FOUND);
}
}
await this.turnService.useToken(game, targetPlayer, token, targetCard);
}
@Post('damagePlayer/:playerId')
@ApiOperation({ summary: 'Consume damage points to damage a player' })
@UseGuards(AuthGuard)
async damagePlayer(
@Param('gameId') _gameId: string,
@Param('playerId') playerId: string,
@Param('gameId', GameStartedPipe) game: Document<Game> & Game,
@Body() damagePlayerDTO: DamagePlayerDTO,
@Session() session: Record<string, string>,
) {
const currentPlayer = getPlayer(session, game);
const currentTeam = getCurrentTeam(game);
if (!isInTeam(currentTeam, currentPlayer)) {
throw new HttpException(
'It is not your turn to play',
HttpStatus.FORBIDDEN,
);
}
if (damagePlayerDTO.amount > game.currentTurn.damage) {
throw new HttpException(
'You cannot deal more damage than what your current damage points',
HttpStatus.BAD_REQUEST,
);
}
const targetPlayer = findPlayer(playerId, game);
if (targetPlayer.board.cards.some((c) => c.guard)) {
throw new HttpException(
'You cannot target this player while there is a guard',
HttpStatus.BAD_REQUEST,
);
}
await this.turnService.damagePlayer(
game,
damagePlayerDTO.amount,
currentPlayer,
targetPlayer,
);
}
@Post('damageChampion/:cardId')
@ApiOperation({ summary: 'Consume damage points to stun a champion' })
@UseGuards(AuthGuard)
async damageChampion(
@Param('gameId') _gameId: string,
@Param('cardId') _cardId: string,
@Param('gameId', GameStartedPipe) game: Document<Game> & Game,
@Param('cardId', CardPipe) card: Document<Card> & Card,
@Body() damageChampion: DamageChampionDTO,
@Session() session: Record<string, string>,
) {
const currentPlayer = getPlayer(session, game);
const currentTeam = getCurrentTeam(game);
if (!isInTeam(currentTeam, currentPlayer)) {
throw new HttpException(
'It is not your turn to play',
HttpStatus.FORBIDDEN,
);
}
if (card.defense > game.currentTurn.damage) {
throw new HttpException(
'You cannot deal more damage than what your current damage points',
HttpStatus.BAD_REQUEST,
);
}
const targetPlayer = findPlayer(damageChampion.playerId, game);
if (!targetPlayer) {
throw new HttpException('Player not found', HttpStatus.NOT_FOUND);
}
if (card.cardType != CardType.CHAMPION) {
throw new HttpException('Card is not a champion', HttpStatus.BAD_REQUEST);
}
if (!targetPlayer.board.cards.some((c) => c._id.equals(card._id))) {
throw new HttpException(
'Champion not found in player board',
HttpStatus.NOT_FOUND,
);
}
if (!card.guard) {
if (targetPlayer.board.cards.some((c) => c.guard)) {
throw new HttpException(
'You cannot target this champion while there is a guard',
HttpStatus.BAD_REQUEST,
);
}
}
await this.turnService.damageChampion(game, targetPlayer, card);
}
@Post('discardCard/:cardId')
@ApiOperation({
summary: 'Discard a card if you have fuckU or discard markers',
})
@UseGuards(AuthGuard)
async discardCard(
@Param('gameId') _gameId: string,
@Param('cardId') _cardId: string,
@Param('gameId', GameStartedPipe) game: Document<Game> & Game,
@Param('cardId', CardPipe) card: Document<Card> & Card,
@Session() session: Record<string, string>,
) {
const currentPlayer = getPlayer(session, game);
const currentTeam = getCurrentTeam(game);
if (!isInTeam(currentTeam, currentPlayer)) {
throw new HttpException(
'It is not your turn to play',
HttpStatus.FORBIDDEN,
);
}
if (currentPlayer.discardMarkers == 0 && currentPlayer.fuckUMarkers == 0) {
throw new HttpException(
'You do not have any discard or fuckU markers',
HttpStatus.BAD_REQUEST,
);
}
if (!currentPlayer.board.cards.some((c) => c._id.equals(_cardId))) {
throw new HttpException('Card not found in board', HttpStatus.NOT_FOUND);
}
await this.turnService.discardCard(game, currentPlayer, card);
}
}