Feat : add most token effects

This commit is contained in:
2024-07-14 00:05:47 +02:00
parent 22836f7611
commit af2965edea
7 changed files with 206 additions and 33 deletions
+94 -6
View File
@@ -28,8 +28,11 @@ 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 } from 'src/cards/schemas/cards.types';
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')
@@ -228,18 +231,27 @@ export class TurnController {
throw new HttpException('Token not found', HttpStatus.NOT_FOUND);
}
const targetPlayer = findPlayer(useTokenDto.playerId, game);
const enemyTargetToken = [
const enemyTargetTokens = [
CardEffects.STUN,
CardEffects.CONTROL_OPPOSING_CHAMPION_PASSIVE,
];
const marketTokens = [
CardEffects.STACK_NEXT_ACTION_BOUGHT,
CardEffects.STACK_NEXT_CARD_BOUGHT,
];
let targetCard: Card;
if (enemyTargetToken.includes(token.effect)) {
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),
@@ -249,6 +261,82 @@ export class TurnController {
if (!targetCard) {
throw new HttpException('Card not found', HttpStatus.NOT_FOUND);
}
await this.turnService.useToken(game, currentPlayer, token, targetCard);
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);
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.player, 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,
);
}
}
}
+6
View File
@@ -0,0 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
export class DamageChampionDTO {
@ApiProperty()
player: string;
}
+6
View File
@@ -0,0 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
export class DamagePlayerDTO {
@ApiProperty()
amount: number;
}
+4 -1
View File
@@ -309,7 +309,7 @@ export const tokenMappings = {
eventEmitter: EventEmitter2,
session: mongoose.mongo.ClientSession,
) => {
if (card.cardType != CardType.CHAMPION) {
if (card.cardType != CardType.ACTION) {
throw new HttpException(
'Cannot stack : Card is not an action',
HttpStatus.BAD_REQUEST,
@@ -322,6 +322,7 @@ export const tokenMappings = {
player.stack,
session,
);
await gameService.fillMarket(game);
},
[CardEffects.STACK_NEXT_CARD_BOUGHT]: async (
game: Document<Game> & Game,
@@ -345,6 +346,7 @@ export const tokenMappings = {
player.stack,
session,
);
await gameService.fillMarket(game);
},
[CardEffects.PLAY_NEXT_CARD_BOUGHT]: async (
game: Document<Game> & Game,
@@ -368,6 +370,7 @@ export const tokenMappings = {
player.board,
session,
);
await gameService.fillMarket(game);
},
[CardEffects.DRAW_AND_DISCARD]: async (
game: Document<Game> & Game,
+30 -1
View File
@@ -6,7 +6,7 @@ import {
Injectable,
} from '@nestjs/common';
import { InjectModel, InjectConnection } from '@nestjs/mongoose';
import { Container, Game, Player } from '../schemas/game.schema';
import { Container, Game, Player, Team } from '../schemas/game.schema';
import mongoose, { Document, Model } from 'mongoose';
import { Card } from 'src/cards/schemas/cards.schema';
import { EventEmitter2 } from '@nestjs/event-emitter';
@@ -248,4 +248,33 @@ export class GameService {
await game.save({ session });
}
@WithTransaction
async killTeam(
game: Document<Game> & Game,
team: Team,
session?: mongoose.mongo.ClientSession,
) {
const index = game.teams.findIndex((t) => t._id.equals(team._id));
game.teams.splice(index, 1);
if (game.teams.length <= 1) {
await this.endGame(game, session);
}
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'killTeam',
teamId: team._id,
});
await game.save({ session });
}
@WithTransaction
async endGame(
game: Document<Game> & Game,
session?: mongoose.mongo.ClientSession,
) {
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'endGame',
});
await game.deleteOne({ session });
}
}
+34 -2
View File
@@ -5,7 +5,7 @@ import {
Inject,
Injectable,
} from '@nestjs/common';
import { Container, Game, Player } from '../schemas/game.schema';
import { Container, Game, Player, Team } from '../schemas/game.schema';
import mongoose, { Document, Model } from 'mongoose';
import { Card } from 'src/cards/schemas/cards.schema';
import { EventEmitter2 } from '@nestjs/event-emitter';
@@ -16,7 +16,7 @@ import {
CardType,
EffectType,
} from 'src/cards/schemas/cards.types';
import { WithTransaction } from '../utils';
import { getPlayerTeam, WithTransaction } from '../utils';
import {
conditionsMappings,
effectMappings,
@@ -275,4 +275,36 @@ export class TurnService {
});
await game.save({ session });
}
@WithTransaction
async damagePlayer(
game: Document<Game> & Game,
amount: number,
player: Player,
targetPlayer: Player,
session?: mongoose.mongo.ClientSession,
) {
const team = getPlayerTeam(game, targetPlayer);
team.health -= amount;
game.currentTurn.damage -= amount;
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'currentTurn.updateDamage',
damage: game.currentTurn.damage,
operation: -amount,
});
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'team.updateHealth',
team: team._id,
health: team.health,
operation: -amount,
});
if (team.health <= 0) {
this.gamesService.killTeam(game, team, session);
}
await game.save({ session });
}
}
+32 -23
View File
@@ -57,6 +57,11 @@ export function isInTeam(team: Team, player: Player) {
return false;
}
export function getPlayerTeam(game: Game, player: Player) {
return game.teams.find((t) =>
t.players.some((p) => p._id.equals(player._id)),
);
}
export function WithTransaction(
target: any,
propertyKey: string,
@@ -66,30 +71,11 @@ export function WithTransaction(
descriptor.value = function (...args: any[]) {
return new Promise(async (resolve, reject) => {
if (args.at(-1) instanceof mongoose.mongo.ClientSession) {
const data = await originalFunc.apply(this, args).catch((e) => {
if (e instanceof HttpException) {
throw e;
}
reject(
new HttpException(
'Another request is processing',
HttpStatus.TOO_MANY_REQUESTS,
),
);
});
resolve(data);
return data;
} else {
await this.connection
.transaction(async (session) => {
const data = await originalFunc.apply(this, [...args, session]);
resolve(data);
return data;
})
.catch((e) => {
try {
if (args.at(-1) instanceof mongoose.mongo.ClientSession) {
const data = await originalFunc.apply(this, args).catch((e) => {
if (e instanceof HttpException) {
reject(e);
throw e;
}
reject(
new HttpException(
@@ -98,6 +84,29 @@ export function WithTransaction(
),
);
});
resolve(data);
return data;
} else {
await this.connection
.transaction(async (session) => {
const data = await originalFunc.apply(this, [...args, session]);
resolve(data);
return data;
})
.catch((e) => {
if (e instanceof HttpException) {
reject(e);
}
reject(
new HttpException(
'Another request is processing',
HttpStatus.TOO_MANY_REQUESTS,
),
);
});
}
} catch (e) {
reject(e);
}
});
};