437 lines
14 KiB
TypeScript
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);
|
|
}
|
|
}
|