feat: card lock
This commit is contained in:
@@ -21,5 +21,13 @@ module.exports = {
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'warn', // or "error"
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
caughtErrorsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -13,32 +13,14 @@ import { LobbyService } from '../services/games.lobby.service';
|
||||
import { AuthGuard } from '../guards/auth.guard';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { StartingDeckDto } from '../dto/update-game.dto';
|
||||
import { Game, Player } from '../schemas/game.schema';
|
||||
import { Game } from '../schemas/game.schema';
|
||||
import { Document } from 'mongoose';
|
||||
import { GamePipe } from '../pipe/game.pipe';
|
||||
import { GamePipe, GameStartedPipe } from '../pipe/game.pipe';
|
||||
|
||||
import { ReadUserDto } from '../dto/read-games.dto';
|
||||
import { Observable, fromEvent, map } from 'rxjs';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { GameService } from '../services/games.service';
|
||||
|
||||
function getPlayer(session: Record<string, any>, game: Game) {
|
||||
const user = session.user as ReadUserDto;
|
||||
if (!user) {
|
||||
throw new HttpException('Please log in', HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
for (const team of game.teams) {
|
||||
for (const player of team.players) {
|
||||
if (player.user._id.toHexString() == user._id) {
|
||||
return player as Document<Player> & Player;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new HttpException(
|
||||
'You are not part of this game',
|
||||
HttpStatus.FORBIDDEN,
|
||||
);
|
||||
}
|
||||
import { getCurrentTeam, getPlayer, isInTeam } from '../utils';
|
||||
|
||||
@ApiTags('game')
|
||||
@Controller('games/:id')
|
||||
@@ -72,18 +54,19 @@ export class GamesController {
|
||||
@UseGuards(AuthGuard)
|
||||
async endTurn(
|
||||
@Param('id') id: string,
|
||||
@Param('id', GamePipe) game: Document<Game> & Game,
|
||||
@Param('id', GameStartedPipe) game: Document<Game> & Game,
|
||||
@Session() session: Record<string, string>,
|
||||
) {
|
||||
if (!game.started || !game.currentTurn) {
|
||||
throw new HttpException('Game not started', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
const player = getPlayer(session, game);
|
||||
const currentTurn = game.teams.find(
|
||||
(t) => t._id.toHexString() == game.currentTurn,
|
||||
);
|
||||
const currentTeam = getCurrentTeam(game);
|
||||
const currentPlayer = getPlayer(session, game);
|
||||
|
||||
await this.gameService.endTurn(game, player._id.toHexString());
|
||||
if (!isInTeam(currentTeam, currentPlayer)) {
|
||||
throw new HttpException(
|
||||
'It is not your turn to play',
|
||||
HttpStatus.FORBIDDEN,
|
||||
);
|
||||
}
|
||||
await this.gameService.endTurn(game);
|
||||
}
|
||||
|
||||
@Sse('/subscribe')
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
Controller,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Param,
|
||||
Post,
|
||||
Session,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { AuthGuard } from '../guards/auth.guard';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { Game } 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 } from '../utils';
|
||||
|
||||
@ApiTags('turn')
|
||||
@Controller('games/:gameId/turn')
|
||||
export class TurnController {
|
||||
constructor(
|
||||
private readonly turnService: TurnService,
|
||||
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,
|
||||
@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.cardsLocked.some((c) => c._id.toHexString() == cardId)
|
||||
) {
|
||||
throw new HttpException('Card already locked', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
this.turnService.lockCard(game, currentPlayer, cardId);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { DatabaseObjectDto } from './database-object.dto';
|
||||
import { Game, Player, Team } from '../schemas/game.schema';
|
||||
import { Game, Player, PlayingTurn, Team } from '../schemas/game.schema';
|
||||
import { Card } from 'src/cards/schemas/cards.schema';
|
||||
|
||||
export class ReadPlayerDto extends DatabaseObjectDto {
|
||||
@@ -54,9 +54,11 @@ export class ReadContainerDto extends DatabaseObjectDto {
|
||||
// if (hidden) {
|
||||
// this.cards = data.cards.map(() => null);
|
||||
// } else {
|
||||
this.id = data._id.toHexString();
|
||||
this.cards = data.cards;
|
||||
// }
|
||||
}
|
||||
id: string;
|
||||
cards: Card[];
|
||||
}
|
||||
|
||||
@@ -85,6 +87,31 @@ export class ReadUserDto extends DatabaseObjectDto {
|
||||
avatar: string;
|
||||
}
|
||||
|
||||
class ReadPlayingTeamDto {
|
||||
constructor(currentTurn: PlayingTurn) {
|
||||
this.currentTeam = currentTurn.currentTeam;
|
||||
this.cardsLocked = currentTurn.cardsLocked.map((c) => c._id.toHexString());
|
||||
this.effectsUsed = currentTurn.effectsUsed;
|
||||
this.damage = currentTurn.damage;
|
||||
this.gold = currentTurn.gold;
|
||||
}
|
||||
|
||||
@ApiProperty()
|
||||
currentTeam: string;
|
||||
|
||||
@ApiProperty()
|
||||
cardsLocked: string[];
|
||||
|
||||
@ApiProperty()
|
||||
effectsUsed: string[];
|
||||
|
||||
@ApiProperty()
|
||||
damage: number;
|
||||
|
||||
@ApiProperty()
|
||||
gold: number;
|
||||
}
|
||||
|
||||
export class ReadGameDto extends DatabaseObjectDto {
|
||||
constructor(data: Game & ConstructorParameters<typeof DatabaseObjectDto>[0]) {
|
||||
super(data);
|
||||
@@ -97,7 +124,7 @@ export class ReadGameDto extends DatabaseObjectDto {
|
||||
this.ended = obj.ended;
|
||||
this.owner = new ReadUserDto(obj.owner);
|
||||
this.packs = obj.packs;
|
||||
this.currentTurn = obj.currentTurn;
|
||||
this.currentTurn = new ReadPlayingTeamDto(obj.currentTurn);
|
||||
}
|
||||
|
||||
@ApiProperty()
|
||||
@@ -113,7 +140,7 @@ export class ReadGameDto extends DatabaseObjectDto {
|
||||
fireGems: ReadContainerDto;
|
||||
|
||||
@ApiProperty()
|
||||
currentTurn?: string;
|
||||
currentTurn: ReadPlayingTeamDto;
|
||||
|
||||
@ApiProperty()
|
||||
started: boolean;
|
||||
|
||||
@@ -7,6 +7,8 @@ import { UsersModule } from 'src/users/users.module';
|
||||
import { CardsModule } from 'src/cards/cards.module';
|
||||
import { GamesController } from './controllers/games.controller';
|
||||
import { GameService } from './services/games.service';
|
||||
import { TurnService } from './services/turn.service';
|
||||
import { TurnController } from './controllers/turn.controller';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -14,7 +16,7 @@ import { GameService } from './services/games.service';
|
||||
UsersModule,
|
||||
MongooseModule.forFeature([{ name: Game.name, schema: GameSchema }]),
|
||||
],
|
||||
controllers: [LobbyController, GamesController],
|
||||
providers: [LobbyService, GameService],
|
||||
controllers: [LobbyController, GamesController, TurnController],
|
||||
providers: [LobbyService, GameService, TurnService],
|
||||
})
|
||||
export class GamesModule {}
|
||||
|
||||
@@ -20,3 +20,22 @@ export class GamePipe implements PipeTransform {
|
||||
return game;
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class GameStartedPipe implements PipeTransform {
|
||||
constructor(@InjectModel(Game.name) private gameModel: Model<Game>) {}
|
||||
|
||||
async transform(value: any) {
|
||||
const game = await this.gameModel.findById(value);
|
||||
if (!game) {
|
||||
throw new HttpException('Game not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
if (!game.started) {
|
||||
throw new HttpException(
|
||||
'The game must be started',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
return game;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,28 @@ export class Container extends Types.ObjectId {
|
||||
|
||||
export const ContainerSchema = SchemaFactory.createForClass(Container);
|
||||
|
||||
@Schema()
|
||||
export class PlayingTurn extends Types.ObjectId {
|
||||
@Prop()
|
||||
currentTeam?: string;
|
||||
|
||||
@Prop({
|
||||
type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Card' }],
|
||||
autopopulate: true,
|
||||
default: [],
|
||||
})
|
||||
cardsLocked: Card[];
|
||||
|
||||
@Prop({ type: [String], default: [] })
|
||||
effectsUsed: string[];
|
||||
|
||||
@Prop({ default: 0 })
|
||||
damage: number;
|
||||
|
||||
@Prop({ default: 0 })
|
||||
gold: number;
|
||||
}
|
||||
|
||||
@Schema()
|
||||
export class Player extends Types.ObjectId {
|
||||
@Prop({
|
||||
@@ -72,8 +94,8 @@ export class Game extends Types.ObjectId {
|
||||
@Prop({ type: ContainerSchema, default: {}, autopopulate: true })
|
||||
fireGems: Container;
|
||||
|
||||
@Prop()
|
||||
currentTurn?: string;
|
||||
@Prop({ type: PlayingTurn, default: {}, autopopulate: true })
|
||||
currentTurn: PlayingTurn;
|
||||
|
||||
@Prop({ default: false })
|
||||
started: boolean;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { Game, Player } from '../schemas/game.schema';
|
||||
import { Document } from 'mongoose';
|
||||
import { Card } from 'src/cards/schemas/cards.schema';
|
||||
import { CardEffects } from 'src/cards/schemas/cards.types';
|
||||
import { Effect } from 'src/cards/schemas/effect.schema';
|
||||
|
||||
export const effectMappings = {
|
||||
[CardEffects.GOLD]: (
|
||||
game: Document<Game> & Game,
|
||||
effect: Effect,
|
||||
_player: Player,
|
||||
_card: Card,
|
||||
eventEmitter: EventEmitter2,
|
||||
) => {
|
||||
game.currentTurn.gold += effect.amount;
|
||||
eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
||||
type: 'currentTurn.updateGold',
|
||||
gold: game.currentTurn.gold,
|
||||
operation: effect.amount,
|
||||
});
|
||||
},
|
||||
} as Record<
|
||||
CardEffects,
|
||||
(
|
||||
game: Game,
|
||||
effect: Effect,
|
||||
player: Player,
|
||||
card: Card,
|
||||
eventEmitter: EventEmitter2,
|
||||
) => void
|
||||
>;
|
||||
@@ -2,7 +2,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { DatabaseObjectDto } from '../dto/database-object.dto';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Game, Player, Team } from '../schemas/game.schema';
|
||||
import { Document, Model } from 'mongoose';
|
||||
import mongoose, { Document, Model } from 'mongoose';
|
||||
import { ReadContainerDto, ReadGameDto } from '../dto/read-games.dto';
|
||||
import { User } from 'src/users/schemas/user.entity';
|
||||
import { Card } from 'src/cards/schemas/cards.schema';
|
||||
@@ -10,6 +10,7 @@ import { CardRole } from 'src/cards/schemas/cards.types';
|
||||
import { StartingDeckDto, UpdateGameDto } from '../dto/update-game.dto';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { GameService } from './games.service';
|
||||
import { WithTransaction } from '../utils';
|
||||
|
||||
@Injectable()
|
||||
export class LobbyService {
|
||||
@@ -88,10 +89,14 @@ export class LobbyService {
|
||||
return await game.deleteOne();
|
||||
}
|
||||
|
||||
async start(game: Document<Game> & Game) {
|
||||
@WithTransaction
|
||||
async start(
|
||||
game: Document<Game> & Game,
|
||||
session?: mongoose.mongo.ClientSession,
|
||||
) {
|
||||
const random = Math.floor(Math.random() * game.teams.length);
|
||||
game.currentTurn = game.teams[random]._id.toHexString();
|
||||
await game.save();
|
||||
game.currentTurn.currentTeam = game.teams[random]._id.toHexString();
|
||||
await game.save({ session });
|
||||
game.started = true;
|
||||
this.eventEmitter.emit('sse.lobby', {
|
||||
type: 'start',
|
||||
@@ -116,7 +121,7 @@ export class LobbyService {
|
||||
type: 'populateContainer',
|
||||
container: new ReadContainerDto(player.stack, true),
|
||||
});
|
||||
await this.gamesService.shuffleContainer(player.stack, game);
|
||||
await this.gamesService.shuffleContainer(player.stack, game, session);
|
||||
playerIndex++;
|
||||
}
|
||||
}
|
||||
@@ -140,19 +145,33 @@ export class LobbyService {
|
||||
container: new ReadContainerDto(game.marketStack, true),
|
||||
});
|
||||
|
||||
await this.gamesService.shuffleContainer(game.marketStack, game);
|
||||
await this.gamesService.fillMarket(game);
|
||||
await this.gamesService.shuffleContainer(game.marketStack, game, session);
|
||||
await this.gamesService.fillMarket(game, session);
|
||||
for (const team of game.teams) {
|
||||
for (const player of team.players) {
|
||||
if (team._id.toHexString() == game.currentTurn) {
|
||||
await this.gamesService.drawToHand(3, player, game);
|
||||
if (team._id.toHexString() == game.currentTurn.currentTeam) {
|
||||
await this.gamesService.drawToHand(3, player, game, session);
|
||||
} else {
|
||||
await this.gamesService.drawToHand(5, player, game);
|
||||
await this.gamesService.drawToHand(5, player, game, session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await game.save();
|
||||
const currentTeam = game.teams.find(
|
||||
(t) => t._id.toHexString() == game.currentTurn.currentTeam,
|
||||
);
|
||||
|
||||
for (const player of currentTeam.players) {
|
||||
await this.gamesService.distributeCards(
|
||||
player.hand.cards,
|
||||
player.hand,
|
||||
player.board,
|
||||
game,
|
||||
session,
|
||||
);
|
||||
}
|
||||
|
||||
await game.save({ session });
|
||||
}
|
||||
|
||||
async changeStartingDeck(
|
||||
|
||||
@@ -4,47 +4,7 @@ import { Container, Game, Player } from '../schemas/game.schema';
|
||||
import mongoose, { Document, Model } from 'mongoose';
|
||||
import { Card } from 'src/cards/schemas/cards.schema';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
|
||||
function shuffle(a) {
|
||||
let j, x, i;
|
||||
for (i = a.length - 1; i > 0; i--) {
|
||||
j = Math.floor(Math.random() * (i + 1));
|
||||
x = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = x;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function WithTransaction(
|
||||
target: any,
|
||||
propertyKey: string,
|
||||
descriptor: PropertyDescriptor,
|
||||
) {
|
||||
const originalFunc = descriptor.value;
|
||||
|
||||
descriptor.value = async function (...args: any[]) {
|
||||
if (args.at(-1) instanceof mongoose.mongo.ClientSession) {
|
||||
return originalFunc.apply(this, args);
|
||||
} else {
|
||||
await this.connection
|
||||
.transaction(async (session) => {
|
||||
return originalFunc.apply(this, [...args, session]);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e instanceof HttpException) {
|
||||
throw e;
|
||||
}
|
||||
throw new HttpException(
|
||||
'Another request is processing',
|
||||
HttpStatus.TOO_MANY_REQUESTS,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
import { WithTransaction, getCurrentTeam, shuffle } from '../utils';
|
||||
|
||||
@Injectable()
|
||||
export class GameService {
|
||||
@@ -59,21 +19,11 @@ export class GameService {
|
||||
@WithTransaction
|
||||
async endTurn(
|
||||
game: Document<Game> & Game,
|
||||
currentPlayerId: string,
|
||||
session?: mongoose.mongo.ClientSession,
|
||||
) {
|
||||
let currentTurn = game.teams.find(
|
||||
(t) => t._id.toHexString() == game.currentTurn,
|
||||
);
|
||||
if (
|
||||
!currentTurn.players.some((p) => p._id.toHexString() == currentPlayerId)
|
||||
) {
|
||||
throw new HttpException(
|
||||
'It is not your turn to play',
|
||||
HttpStatus.FORBIDDEN,
|
||||
);
|
||||
}
|
||||
for (const player of currentTurn.players) {
|
||||
let currentTeam = getCurrentTeam(game);
|
||||
|
||||
for (const player of currentTeam.players) {
|
||||
await this.distributeCards(
|
||||
player.board.cards.filter((c) => !c.defense),
|
||||
player.board,
|
||||
@@ -84,20 +34,23 @@ export class GameService {
|
||||
await this.drawToHand(5, player, game, session);
|
||||
}
|
||||
|
||||
const index = game.teams.indexOf(currentTurn);
|
||||
game.currentTurn =
|
||||
const index = game.teams.indexOf(currentTeam);
|
||||
game.currentTurn.currentTeam =
|
||||
game.teams[(index + 1) % game.teams.length]._id.toHexString();
|
||||
|
||||
game.currentTurn.cardsLocked = [];
|
||||
game.currentTurn.effectsUsed = [];
|
||||
game.currentTurn.damage = 0;
|
||||
game.currentTurn.gold = 0;
|
||||
|
||||
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
||||
type: 'endTurn',
|
||||
currentTurn: game.currentTurn,
|
||||
});
|
||||
|
||||
currentTurn = game.teams.find(
|
||||
(t) => t._id.toHexString() == game.currentTurn,
|
||||
);
|
||||
currentTeam = getCurrentTeam(game);
|
||||
|
||||
for (const player of currentTurn.players) {
|
||||
for (const player of currentTeam.players) {
|
||||
await this.distributeCards(
|
||||
player.hand.cards,
|
||||
player.hand,
|
||||
@@ -108,6 +61,7 @@ export class GameService {
|
||||
}
|
||||
await game.save({ session });
|
||||
}
|
||||
|
||||
@WithTransaction
|
||||
async drawToBoard(
|
||||
amount,
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { Game, Player } from '../schemas/game.schema';
|
||||
import mongoose, { Document, Model } from 'mongoose';
|
||||
import { Card } from 'src/cards/schemas/cards.schema';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { Effect } from 'src/cards/schemas/effect.schema';
|
||||
import { CardEffects, EffectType } from 'src/cards/schemas/cards.types';
|
||||
import { WithTransaction } from '../utils';
|
||||
import { effectMappings } from './effect.functions';
|
||||
import { InjectConnection, InjectModel } from '@nestjs/mongoose';
|
||||
|
||||
@Injectable()
|
||||
export class TurnService {
|
||||
constructor(
|
||||
private eventEmitter: EventEmitter2,
|
||||
@InjectModel(Game.name) private gameModel: Model<Game>,
|
||||
@InjectConnection() private readonly connection: mongoose.Connection,
|
||||
) {}
|
||||
|
||||
@WithTransaction
|
||||
async lockCard(
|
||||
game: Document<Game> & Game,
|
||||
player: Player,
|
||||
cardId: string,
|
||||
session?: mongoose.mongo.ClientSession,
|
||||
) {
|
||||
const card = player.board.cards.find((c) => c._id.toHexString() == cardId);
|
||||
if (!card) {
|
||||
throw new HttpException(
|
||||
'Card not found in player board',
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
game.currentTurn.cardsLocked.push(card);
|
||||
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
||||
type: 'lockCard',
|
||||
card: card._id.toHexString(),
|
||||
});
|
||||
for (const effect of card.effects) {
|
||||
if (
|
||||
[, CardEffects.HEAL, CardEffects.GOLD, CardEffects.DAMAGE].includes(
|
||||
effect.effect,
|
||||
) &&
|
||||
!effect.mutex &&
|
||||
!effect.per &&
|
||||
(!effect.condition ||
|
||||
effect.condition.effectId == EffectType.CHAMPION_ACTION)
|
||||
) {
|
||||
await this.useEffect(game, player, effect, card, session);
|
||||
}
|
||||
}
|
||||
await game.save({ session });
|
||||
}
|
||||
|
||||
@WithTransaction
|
||||
async useEffect(
|
||||
game: Document<Game> & Game,
|
||||
player: Player,
|
||||
effect: Effect,
|
||||
card: Card,
|
||||
session?: mongoose.mongo.ClientSession,
|
||||
) {
|
||||
const effectRunner = effectMappings[effect.effect];
|
||||
if (!effectMappings[effect.effect]) {
|
||||
console.warn(`Effect ${effect.effect} not implemented`);
|
||||
return;
|
||||
throw new HttpException(
|
||||
`Effect ${effect.effect} not implemented`,
|
||||
HttpStatus.NOT_IMPLEMENTED,
|
||||
);
|
||||
}
|
||||
const effectUUID = effect._id.toHexString();
|
||||
if (game.currentTurn.effectsUsed.includes(effectUUID)) {
|
||||
console.error(`Effect ${effectUUID} already used this turn`);
|
||||
throw new HttpException(
|
||||
`Effect ${effectUUID} already used this turn`,
|
||||
HttpStatus.CONFLICT,
|
||||
);
|
||||
}
|
||||
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
||||
type: 'useEffect',
|
||||
effect: effectUUID,
|
||||
});
|
||||
game.currentTurn.effectsUsed.push(effectUUID);
|
||||
await effectRunner(game, effect, player, card, this.eventEmitter);
|
||||
await game.save({ session });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import { HttpStatus } from '@nestjs/common/enums';
|
||||
import { HttpException } from '@nestjs/common/exceptions';
|
||||
import mongoose, { Document } from 'mongoose';
|
||||
import { Game, Player, Team } from './schemas/game.schema';
|
||||
import { ReadUserDto } from './dto/read-games.dto';
|
||||
|
||||
export function shuffle(a) {
|
||||
let j, x, i;
|
||||
for (i = a.length - 1; i > 0; i--) {
|
||||
j = Math.floor(Math.random() * (i + 1));
|
||||
x = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = x;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
export function getCurrentTeam(game: Document<Game> & Game) {
|
||||
return game.teams.find(
|
||||
(t) => t._id.toHexString() == game.currentTurn.currentTeam,
|
||||
);
|
||||
}
|
||||
|
||||
export function getPlayer(session: Record<string, any>, game: Game) {
|
||||
const user = session.user as ReadUserDto;
|
||||
if (!user) {
|
||||
throw new HttpException('Please log in', HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
for (const team of game.teams) {
|
||||
for (const player of team.players) {
|
||||
if (player.user._id.toHexString() == user._id) {
|
||||
return player as Document<Player> & Player;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new HttpException(
|
||||
'You are not part of this game',
|
||||
HttpStatus.FORBIDDEN,
|
||||
);
|
||||
}
|
||||
|
||||
export function isInTeam(team: Team, player: Player) {
|
||||
if (
|
||||
team.players.some((p) => p._id.toHexString() == player._id.toHexString())
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function WithTransaction(
|
||||
target: any,
|
||||
propertyKey: string,
|
||||
descriptor: PropertyDescriptor,
|
||||
) {
|
||||
const originalFunc = descriptor.value;
|
||||
|
||||
descriptor.value = async function (...args: any[]) {
|
||||
if (args.at(-1) instanceof mongoose.mongo.ClientSession) {
|
||||
return originalFunc.apply(this, args);
|
||||
} else {
|
||||
await this.connection
|
||||
.transaction(async (session) => {
|
||||
return originalFunc.apply(this, [...args, session]);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e instanceof HttpException) {
|
||||
throw e;
|
||||
}
|
||||
throw new HttpException(
|
||||
'Another request is processing',
|
||||
HttpStatus.TOO_MANY_REQUESTS,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
Reference in New Issue
Block a user