184 lines
5.6 KiB
TypeScript
184 lines
5.6 KiB
TypeScript
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 { ReadContainerDto, ReadGameDto } from '../dto/read-games.dto';
|
|
import { User } from 'src/users/schemas/user.entity';
|
|
import { Card } from 'src/cards/schemas/cards.schema';
|
|
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';
|
|
|
|
@Injectable()
|
|
export class LobbyService {
|
|
constructor(
|
|
@InjectModel(Game.name) private gameModel: Model<Game>,
|
|
@InjectModel(Card.name) private cardModel: Model<Card>,
|
|
private readonly gamesService: GameService,
|
|
private eventEmitter: EventEmitter2,
|
|
) {}
|
|
|
|
async create(owner: User): Promise<DatabaseObjectDto> {
|
|
const createdGame = new this.gameModel();
|
|
createdGame.owner = owner;
|
|
createdGame.packs = [
|
|
'pack1_base_deck',
|
|
'pack1_base_necros',
|
|
'pack1_base_wild',
|
|
'pack1_base_imperial',
|
|
'pack1_base_guild',
|
|
];
|
|
createdGame.teams.push({
|
|
players: [{ user: owner } as Player],
|
|
} as Team);
|
|
|
|
const gamedata = await createdGame.save();
|
|
const dto = new ReadGameDto(gamedata);
|
|
this.eventEmitter.emit('sse.lobby', { type: 'create', ...dto });
|
|
|
|
return dto;
|
|
}
|
|
|
|
async findAll(): Promise<ReadGameDto[]> {
|
|
const games = await this.gameModel.find().exec();
|
|
return await Promise.all(
|
|
games.map(async (e) => {
|
|
return await new ReadGameDto(e);
|
|
}),
|
|
);
|
|
}
|
|
|
|
async findByOwner(_id: string): Promise<ReadGameDto> {
|
|
const game = await this.gameModel.findOne({ owner: _id }).exec();
|
|
if (!game) {
|
|
return null;
|
|
}
|
|
return new ReadGameDto(game);
|
|
}
|
|
|
|
async update(game: Document<Game> & Game, updateGameDto: UpdateGameDto) {
|
|
game.packs = updateGameDto.packs;
|
|
const packs = await this.cardModel.distinct('pack');
|
|
if (updateGameDto.packs.every((e) => packs.includes(e))) {
|
|
await game.save();
|
|
this.eventEmitter.emit('sse.lobby', {
|
|
type: 'update',
|
|
_id: game._id,
|
|
...updateGameDto,
|
|
});
|
|
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
|
type: 'update',
|
|
...updateGameDto,
|
|
});
|
|
return;
|
|
}
|
|
throw new HttpException('Pack not found', HttpStatus.NOT_FOUND);
|
|
}
|
|
|
|
async remove(game: Document<Game> & Game) {
|
|
this.eventEmitter.emit('sse.lobby', {
|
|
_id: game._id,
|
|
});
|
|
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
|
type: 'delete',
|
|
});
|
|
return await game.deleteOne();
|
|
}
|
|
|
|
async start(game: Document<Game> & Game) {
|
|
const random = Math.floor(Math.random() * game.teams.length);
|
|
game.currentTurn = game.teams[random];
|
|
await game.save();
|
|
game.started = true;
|
|
this.eventEmitter.emit('sse.lobby', {
|
|
type: 'start',
|
|
_id: game._id,
|
|
});
|
|
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
|
type: 'start',
|
|
game: new ReadGameDto(game),
|
|
});
|
|
let playerIndex = 0;
|
|
await Promise.all(
|
|
game.teams.map((team) =>
|
|
Promise.all(
|
|
team.players.map(async (player) => {
|
|
player.stack.cards = await this.cardModel
|
|
.find({
|
|
pack: { $in: game.packs },
|
|
role: { $in: [player.class, player.race] },
|
|
playerIndex,
|
|
})
|
|
.exec();
|
|
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
|
type: 'populateContainer',
|
|
container: new ReadContainerDto(player.stack, true),
|
|
});
|
|
this.gamesService.shuffleContainer(player.stack, game);
|
|
playerIndex++;
|
|
}),
|
|
),
|
|
),
|
|
);
|
|
|
|
game.fireGems.cards = await this.cardModel.find({
|
|
pack: { $in: game.packs },
|
|
role: CardRole.FIRE_GEM,
|
|
});
|
|
|
|
game.marketStack.cards = await this.cardModel.find({
|
|
pack: { $in: game.packs },
|
|
role: CardRole.MARKET,
|
|
});
|
|
|
|
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
|
type: 'populateContainer',
|
|
container: new ReadContainerDto(game.fireGems),
|
|
});
|
|
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
|
|
type: 'populateContainer',
|
|
container: new ReadContainerDto(game.marketStack, true),
|
|
});
|
|
|
|
await this.gamesService.shuffleContainer(game.marketStack, game);
|
|
await game.save();
|
|
}
|
|
|
|
async changeStartingDeck(
|
|
data: StartingDeckDto,
|
|
player: Document<Player> & Player,
|
|
game: Document<Game> & Game,
|
|
) {
|
|
const packs = await this.cardModel
|
|
.find({ pack: { $in: game.packs } })
|
|
.distinct('role');
|
|
|
|
if (
|
|
!Object.values<string>(CardRole).includes(data.class) ||
|
|
!Object.values<string>(CardRole).includes(data.race) ||
|
|
data.race == CardRole.FIRE_GEM ||
|
|
data.race == CardRole.MARKET ||
|
|
data.class == CardRole.FIRE_GEM ||
|
|
data.class == CardRole.MARKET
|
|
) {
|
|
throw new HttpException('Invalid race or class', HttpStatus.NOT_FOUND);
|
|
}
|
|
if (
|
|
packs.includes(data.class as CardRole) &&
|
|
packs.includes(data.race as CardRole)
|
|
) {
|
|
player.class = data.class;
|
|
player.race = data.race;
|
|
await game.save();
|
|
this.eventEmitter.emit('sse.game.changeStartingDeck', {
|
|
_id: player._id,
|
|
...data,
|
|
});
|
|
return;
|
|
}
|
|
throw new HttpException('Pack not found', HttpStatus.NOT_FOUND);
|
|
}
|
|
}
|