Feat : add ability to use tokens
This commit is contained in:
@@ -16,7 +16,13 @@ import { GameStartedPipe } from '../pipe/game.pipe';
|
|||||||
|
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { TurnService } from '../services/turn.service';
|
import { TurnService } from '../services/turn.service';
|
||||||
import { getPlayer, getCurrentTeam, isInTeam, findPlayer } from '../utils';
|
import {
|
||||||
|
getPlayer,
|
||||||
|
getCurrentTeam,
|
||||||
|
isInTeam,
|
||||||
|
findUser,
|
||||||
|
findPlayer,
|
||||||
|
} from '../utils';
|
||||||
import { CardPipe } from '../pipe/card.pipe';
|
import { CardPipe } from '../pipe/card.pipe';
|
||||||
import { Card } from 'src/cards/schemas/cards.schema';
|
import { Card } from 'src/cards/schemas/cards.schema';
|
||||||
import { EffectPipe } from '../pipe/effect.pipe';
|
import { EffectPipe } from '../pipe/effect.pipe';
|
||||||
@@ -92,7 +98,12 @@ export class TurnController {
|
|||||||
if (game.currentTurn.gold < card.cost) {
|
if (game.currentTurn.gold < card.cost) {
|
||||||
throw new HttpException('Not enough gold', HttpStatus.BAD_REQUEST);
|
throw new HttpException('Not enough gold', HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
await this.turnService.buyGeneric(game, currentPlayer, card, game.market);
|
await this.turnService.buyGeneric(
|
||||||
|
game,
|
||||||
|
card,
|
||||||
|
game.market,
|
||||||
|
currentPlayer.discard,
|
||||||
|
);
|
||||||
await this.gameService.fillMarket(game);
|
await this.gameService.fillMarket(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,9 +129,9 @@ export class TurnController {
|
|||||||
const targetCard = game.fireGems.cards[0];
|
const targetCard = game.fireGems.cards[0];
|
||||||
await this.turnService.buyGeneric(
|
await this.turnService.buyGeneric(
|
||||||
game,
|
game,
|
||||||
currentPlayer,
|
|
||||||
targetCard,
|
targetCard,
|
||||||
game.fireGems,
|
game.fireGems,
|
||||||
|
currentPlayer.discard,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,32 +234,21 @@ export class TurnController {
|
|||||||
CardEffects.STUN,
|
CardEffects.STUN,
|
||||||
CardEffects.CONTROL_OPPOSING_CHAMPION_PASSIVE,
|
CardEffects.CONTROL_OPPOSING_CHAMPION_PASSIVE,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let targetCard: Card;
|
||||||
if (enemyTargetToken.includes(token.effect)) {
|
if (enemyTargetToken.includes(token.effect)) {
|
||||||
const targetCard = targetPlayer.board.cards.find((c) =>
|
targetCard = targetPlayer.board.cards.find((c) =>
|
||||||
c._id.equals(useTokenDto.cardId),
|
c._id.equals(useTokenDto.cardId),
|
||||||
);
|
);
|
||||||
if (!targetCard) {
|
|
||||||
throw new HttpException('Card not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
await this.turnService.useTokenOnEnemyBoard(
|
|
||||||
game,
|
|
||||||
targetPlayer,
|
|
||||||
token,
|
|
||||||
targetCard,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
const targetCard = currentPlayer.board.cards.find((c) =>
|
targetCard = currentPlayer.board.cards.find((c) =>
|
||||||
c._id.equals(useTokenDto.cardId),
|
c._id.equals(useTokenDto.cardId),
|
||||||
);
|
);
|
||||||
if (!targetCard) {
|
|
||||||
throw new HttpException('Card not found', HttpStatus.NOT_FOUND);
|
|
||||||
}
|
|
||||||
await this.turnService.useTokenOnEnemyBoard(
|
|
||||||
game,
|
|
||||||
currentPlayer,
|
|
||||||
token,
|
|
||||||
targetCard,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!targetCard) {
|
||||||
|
throw new HttpException('Card not found', HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
await this.turnService.useToken(game, currentPlayer, token, targetCard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
export class UseTokenDTO {
|
export class UseTokenDTO {
|
||||||
playerId: string;
|
@ApiProperty()
|
||||||
|
playerId?: string;
|
||||||
|
@ApiProperty()
|
||||||
cardId: string;
|
cardId: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import mongoose, { Document } from 'mongoose';
|
|||||||
import { Card } from 'src/cards/schemas/cards.schema';
|
import { Card } from 'src/cards/schemas/cards.schema';
|
||||||
import {
|
import {
|
||||||
CardEffects,
|
CardEffects,
|
||||||
|
CardRole,
|
||||||
CardType,
|
CardType,
|
||||||
EffectType,
|
EffectType,
|
||||||
Per,
|
Per,
|
||||||
@@ -12,6 +13,7 @@ import { Effect, EffectCondition } from 'src/cards/schemas/effect.schema';
|
|||||||
import { TurnService } from './turn.service';
|
import { TurnService } from './turn.service';
|
||||||
import { GameService } from './games.service';
|
import { GameService } from './games.service';
|
||||||
import { ReadEffectDto } from 'src/cards/dto/effect.dto';
|
import { ReadEffectDto } from 'src/cards/dto/effect.dto';
|
||||||
|
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||||
|
|
||||||
export function isAutoActivable(effect: Effect) {
|
export function isAutoActivable(effect: Effect) {
|
||||||
if (
|
if (
|
||||||
@@ -177,11 +179,15 @@ export const tokenMappings = {
|
|||||||
player: Player,
|
player: Player,
|
||||||
card: Card,
|
card: Card,
|
||||||
gameService: GameService,
|
gameService: GameService,
|
||||||
|
turnService: TurnService,
|
||||||
eventEmitter: EventEmitter2,
|
eventEmitter: EventEmitter2,
|
||||||
_session: mongoose.mongo.ClientSession,
|
_session: mongoose.mongo.ClientSession,
|
||||||
) => {
|
) => {
|
||||||
if (card.cardType !== CardType.CHAMPION) {
|
if (card.cardType !== CardType.CHAMPION) {
|
||||||
throw new Error('Cannot Prepare : Card is not a champion');
|
throw new HttpException(
|
||||||
|
'Cannot Prepare : Card is not a champion',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const targetEffects = card.effects.filter(
|
const targetEffects = card.effects.filter(
|
||||||
(e) => e.condition.effectId == EffectType.CHAMPION_ACTION,
|
(e) => e.condition.effectId == EffectType.CHAMPION_ACTION,
|
||||||
@@ -192,7 +198,10 @@ export const tokenMappings = {
|
|||||||
game.currentTurn.lockedEffects.some((ee) => ee._id.equals(e._id)),
|
game.currentTurn.lockedEffects.some((ee) => ee._id.equals(e._id)),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
throw new Error('Cannot Prepare : champion effect not locked');
|
throw new HttpException(
|
||||||
|
'Cannot Prepare : champion effect not locked',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const e of targetEffects) {
|
for (const e of targetEffects) {
|
||||||
@@ -202,20 +211,190 @@ export const tokenMappings = {
|
|||||||
game.currentTurn.lockedEffects.splice(index, 1);
|
game.currentTurn.lockedEffects.splice(index, 1);
|
||||||
eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
||||||
type: 'unlockEffect',
|
type: 'unlockEffect',
|
||||||
effect: effect._id.toHexString(),
|
effect: e._id.toHexString(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[CardEffects.STUN]: addToken,
|
[CardEffects.STUN]: async (
|
||||||
[CardEffects.SACRIFICE]: addToken,
|
game: Document<Game> & Game,
|
||||||
[CardEffects.DRAW_AND_DISCARD]: addToken,
|
effect: Effect,
|
||||||
[CardEffects.MAKE_DISCARD]: addToken,
|
player: Player,
|
||||||
[CardEffects.RESTACK_DISCARDED_CHAMPION]: addToken,
|
card: Card,
|
||||||
[CardEffects.RESTACK_DISCARDED_CARD]: addToken,
|
gameService: GameService,
|
||||||
[CardEffects.STACK_NEXT_ACTION_BOUGHT]: addToken,
|
turnService: TurnService,
|
||||||
[CardEffects.STACK_NEXT_CARD_BOUGHT]: addToken,
|
eventEmitter: EventEmitter2,
|
||||||
[CardEffects.PLAY_NEXT_CARD_BOUGHT]: addToken,
|
session: mongoose.mongo.ClientSession,
|
||||||
};
|
) => {
|
||||||
|
if (card.cardType !== CardType.CHAMPION) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Cannot Stun : Card is not a champion',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await gameService.distributeCards(
|
||||||
|
[card],
|
||||||
|
player.board,
|
||||||
|
player.discard,
|
||||||
|
game,
|
||||||
|
session,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[CardEffects.SACRIFICE]: async (
|
||||||
|
game: Document<Game> & Game,
|
||||||
|
effect: Effect,
|
||||||
|
player: Player,
|
||||||
|
card: Card,
|
||||||
|
gameService: GameService,
|
||||||
|
turnService: TurnService,
|
||||||
|
eventEmitter: EventEmitter2,
|
||||||
|
session: mongoose.mongo.ClientSession,
|
||||||
|
) => {
|
||||||
|
if (game.currentTurn.cardsLocked.some((c) => c._id.equals(card._id))) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Cannot destroy : Card is locked',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await gameService.destroyCard(card, player.board, game, session);
|
||||||
|
},
|
||||||
|
[CardEffects.RESTACK_DISCARDED_CHAMPION]: async (
|
||||||
|
game: Document<Game> & Game,
|
||||||
|
effect: Effect,
|
||||||
|
player: Player,
|
||||||
|
card: Card,
|
||||||
|
gameService: GameService,
|
||||||
|
turnService: TurnService,
|
||||||
|
eventEmitter: EventEmitter2,
|
||||||
|
session: mongoose.mongo.ClientSession,
|
||||||
|
) => {
|
||||||
|
if (card.cardType != CardType.CHAMPION) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Cannot Restack : Card is not a champion',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await gameService.distributeCards(
|
||||||
|
[card],
|
||||||
|
player.discard,
|
||||||
|
player.stack,
|
||||||
|
game,
|
||||||
|
session,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[CardEffects.RESTACK_DISCARDED_CARD]: async (
|
||||||
|
game: Document<Game> & Game,
|
||||||
|
effect: Effect,
|
||||||
|
player: Player,
|
||||||
|
card: Card,
|
||||||
|
gameService: GameService,
|
||||||
|
turnService: TurnService,
|
||||||
|
eventEmitter: EventEmitter2,
|
||||||
|
session: mongoose.mongo.ClientSession,
|
||||||
|
) => {
|
||||||
|
await gameService.distributeCards(
|
||||||
|
[card],
|
||||||
|
player.discard,
|
||||||
|
player.stack,
|
||||||
|
game,
|
||||||
|
session,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[CardEffects.STACK_NEXT_ACTION_BOUGHT]: async (
|
||||||
|
game: Document<Game> & Game,
|
||||||
|
effect: Effect,
|
||||||
|
player: Player,
|
||||||
|
card: Card,
|
||||||
|
gameService: GameService,
|
||||||
|
turnService: TurnService,
|
||||||
|
eventEmitter: EventEmitter2,
|
||||||
|
session: mongoose.mongo.ClientSession,
|
||||||
|
) => {
|
||||||
|
if (card.cardType != CardType.CHAMPION) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Cannot stack : Card is not an action',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await turnService.buyGeneric(
|
||||||
|
game,
|
||||||
|
card,
|
||||||
|
game.market,
|
||||||
|
player.stack,
|
||||||
|
session,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[CardEffects.STACK_NEXT_CARD_BOUGHT]: async (
|
||||||
|
game: Document<Game> & Game,
|
||||||
|
effect: Effect,
|
||||||
|
player: Player,
|
||||||
|
card: Card,
|
||||||
|
gameService: GameService,
|
||||||
|
turnService: TurnService,
|
||||||
|
eventEmitter: EventEmitter2,
|
||||||
|
session: mongoose.mongo.ClientSession,
|
||||||
|
) => {
|
||||||
|
let fromContainer = game.market;
|
||||||
|
if (game.fireGems.cards.some((c) => c._id.equals(card._id))) {
|
||||||
|
fromContainer = game.fireGems;
|
||||||
|
}
|
||||||
|
|
||||||
|
await turnService.buyGeneric(
|
||||||
|
game,
|
||||||
|
card,
|
||||||
|
fromContainer,
|
||||||
|
player.stack,
|
||||||
|
session,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[CardEffects.PLAY_NEXT_CARD_BOUGHT]: async (
|
||||||
|
game: Document<Game> & Game,
|
||||||
|
effect: Effect,
|
||||||
|
player: Player,
|
||||||
|
card: Card,
|
||||||
|
gameService: GameService,
|
||||||
|
turnService: TurnService,
|
||||||
|
eventEmitter: EventEmitter2,
|
||||||
|
session: mongoose.mongo.ClientSession,
|
||||||
|
) => {
|
||||||
|
let fromContainer = game.market;
|
||||||
|
if (game.fireGems.cards.some((c) => c._id.equals(card._id))) {
|
||||||
|
fromContainer = game.fireGems;
|
||||||
|
}
|
||||||
|
|
||||||
|
await turnService.buyGeneric(
|
||||||
|
game,
|
||||||
|
card,
|
||||||
|
fromContainer,
|
||||||
|
player.board,
|
||||||
|
session,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[CardEffects.DRAW_AND_DISCARD]: async (
|
||||||
|
game: Document<Game> & Game,
|
||||||
|
effect: Effect,
|
||||||
|
player: Player,
|
||||||
|
card: Card,
|
||||||
|
gameService: GameService,
|
||||||
|
turnService: TurnService,
|
||||||
|
eventEmitter: EventEmitter2,
|
||||||
|
session: mongoose.mongo.ClientSession,
|
||||||
|
) => {
|
||||||
|
await gameService.drawToBoard(1, player, game, session);
|
||||||
|
await player.discardMarkers++;
|
||||||
|
},
|
||||||
|
} as Record<
|
||||||
|
CardEffects,
|
||||||
|
(
|
||||||
|
game: Game,
|
||||||
|
effect: Effect,
|
||||||
|
player: Player,
|
||||||
|
card: Card,
|
||||||
|
gameService: GameService,
|
||||||
|
turnService: TurnService,
|
||||||
|
eventEmitter: EventEmitter2,
|
||||||
|
session: mongoose.mongo.ClientSession,
|
||||||
|
) => Promise<void>
|
||||||
|
>;
|
||||||
|
|
||||||
export const conditionsMappings = {
|
export const conditionsMappings = {
|
||||||
[EffectType.CHAMPION_ACTION]: async () => true,
|
[EffectType.CHAMPION_ACTION]: async () => true,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
isAutoActivable,
|
isAutoActivable,
|
||||||
perMapping,
|
perMapping,
|
||||||
removeToken,
|
removeToken,
|
||||||
|
tokenMappings,
|
||||||
} from './effect.functions';
|
} from './effect.functions';
|
||||||
import { InjectConnection, InjectModel } from '@nestjs/mongoose';
|
import { InjectConnection, InjectModel } from '@nestjs/mongoose';
|
||||||
import { GameService } from './games.service';
|
import { GameService } from './games.service';
|
||||||
@@ -148,9 +149,9 @@ export class TurnService {
|
|||||||
@WithTransaction
|
@WithTransaction
|
||||||
async buyGeneric(
|
async buyGeneric(
|
||||||
game: Document<Game> & Game,
|
game: Document<Game> & Game,
|
||||||
player: Player,
|
|
||||||
card: Card,
|
card: Card,
|
||||||
fromContainer: Container,
|
fromContainer: Container,
|
||||||
|
toContainer: Container,
|
||||||
session?: mongoose.mongo.ClientSession,
|
session?: mongoose.mongo.ClientSession,
|
||||||
) {
|
) {
|
||||||
if (!card.cost) {
|
if (!card.cost) {
|
||||||
@@ -198,13 +199,19 @@ export class TurnService {
|
|||||||
if (game.currentTurn.gold < cost) {
|
if (game.currentTurn.gold < cost) {
|
||||||
throw new HttpException('Not enough gold', HttpStatus.BAD_REQUEST);
|
throw new HttpException('Not enough gold', HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetContainer = player.discard;
|
|
||||||
// if (targetChangingToken) {
|
// if (targetChangingToken) {
|
||||||
// targetContainer = player.stack;
|
// targetContainer = player.stack;
|
||||||
// removeToken(game, targetChangingToken, this.eventEmitter);
|
// removeToken(game, targetChangingToken, this.eventEmitter);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
await this.gamesService.distributeCards(
|
||||||
|
[card],
|
||||||
|
fromContainer,
|
||||||
|
toContainer,
|
||||||
|
game,
|
||||||
|
session,
|
||||||
|
);
|
||||||
|
|
||||||
if (cost != 0) {
|
if (cost != 0) {
|
||||||
game.currentTurn.gold -= cost;
|
game.currentTurn.gold -= cost;
|
||||||
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
||||||
@@ -214,13 +221,6 @@ export class TurnService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.gamesService.distributeCards(
|
|
||||||
[card],
|
|
||||||
fromContainer,
|
|
||||||
targetContainer,
|
|
||||||
game,
|
|
||||||
session,
|
|
||||||
);
|
|
||||||
await game.save({ session });
|
await game.save({ session });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,19 +246,33 @@ export class TurnService {
|
|||||||
@WithTransaction
|
@WithTransaction
|
||||||
async useToken(
|
async useToken(
|
||||||
game: Document<Game> & Game,
|
game: Document<Game> & Game,
|
||||||
|
player: Player,
|
||||||
token: Effect,
|
token: Effect,
|
||||||
target: Card,
|
target: Card,
|
||||||
session?: mongoose.mongo.ClientSession,
|
session?: mongoose.mongo.ClientSession,
|
||||||
) {
|
) {
|
||||||
return;
|
const handler = tokenMappings[token.effect];
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error('Effect not implemented');
|
||||||
|
}
|
||||||
|
await handler(
|
||||||
|
game,
|
||||||
|
token,
|
||||||
|
player,
|
||||||
|
target,
|
||||||
|
this.gamesService,
|
||||||
|
this,
|
||||||
|
this.eventEmitter,
|
||||||
|
session,
|
||||||
|
);
|
||||||
|
const index = game.currentTurn.tokens.findIndex((t) =>
|
||||||
|
t._id.equals(token._id),
|
||||||
|
);
|
||||||
|
game.currentTurn.tokens.splice(index, 1);
|
||||||
|
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
||||||
|
type: 'currentTurn.useToken',
|
||||||
|
tokenId: token._id.toHexString(),
|
||||||
|
});
|
||||||
|
await game.save({ session });
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithTransaction
|
|
||||||
async useTokenOnEnemyBoard(
|
|
||||||
game: Document<Game> & Game,
|
|
||||||
targetPlayer: Player,
|
|
||||||
token: Effect,
|
|
||||||
target: Card,
|
|
||||||
session?: mongoose.mongo.ClientSession,
|
|
||||||
) {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-2
@@ -25,13 +25,24 @@ export function getPlayer(session: Record<string, any>, game: Game) {
|
|||||||
throw new HttpException('Please log in', HttpStatus.UNAUTHORIZED);
|
throw new HttpException('Please log in', HttpStatus.UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
return findPlayer(user._id, game);
|
return findUser(user._id, game);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findUser(playerId: string, game: Game) {
|
||||||
|
for (const team of game.teams) {
|
||||||
|
for (const player of team.players) {
|
||||||
|
if (player.user._id.equals(playerId)) {
|
||||||
|
return player as Document<Player> & Player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new HttpException('User not found in game', HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findPlayer(playerId: string, game: Game) {
|
export function findPlayer(playerId: string, game: Game) {
|
||||||
for (const team of game.teams) {
|
for (const team of game.teams) {
|
||||||
for (const player of team.players) {
|
for (const player of team.players) {
|
||||||
if (player.user._id.equals(playerId)) {
|
if (player._id.equals(playerId)) {
|
||||||
return player as Document<Player> & Player;
|
return player as Document<Player> & Player;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user