Files
back-ts/src/games/services/games.service.ts
T
2024-07-14 14:49:24 +02:00

302 lines
7.7 KiB
TypeScript

import {
forwardRef,
HttpException,
HttpStatus,
Inject,
Injectable,
} from '@nestjs/common';
import { InjectModel, InjectConnection } from '@nestjs/mongoose';
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';
import { WithTransaction, getCurrentTeam, shuffle } from '../utils';
import { TurnService } from './turn.service';
import { CardRole, CardType } from 'src/cards/schemas/cards.types';
@Injectable()
export class GameService {
constructor(
@Inject(forwardRef(() => TurnService))
private readonly turnService: TurnService,
@InjectModel(Game.name) private gameModel: Model<Game>,
@InjectModel(Card.name) private cardModel: Model<Card>,
@InjectConnection() private readonly connection: mongoose.Connection,
private eventEmitter: EventEmitter2,
) {}
@WithTransaction
async endTurn(
game: Document<Game> & Game,
session?: mongoose.mongo.ClientSession,
) {
let currentTeam = getCurrentTeam(game);
for (const player of currentTeam.players) {
if (player.discardMarkers > 0) {
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'player.removeDiscardMarker',
operation: -player.discardMarkers,
discardMarkers: 0,
playerId: player._id.toHexString(),
});
player.discardMarkers = 0;
}
if (player.fuckUMarkers > 0) {
player.fuckUMarkers--;
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'player.removeFuckUMarker',
operation: -player.fuckUMarkers,
fuckUMarkers: 0,
playerId: player._id.toHexString(),
});
player.fuckUMarkers = 0;
}
await this.distributeCards(
player.board.cards.filter((c) => !c.defense),
player.board,
player.discard,
game,
session,
);
await this.drawToHand(5, player, game, session);
}
const index = game.teams.indexOf(currentTeam);
game.currentTurn.currentTeam =
game.teams[(index + 1) % game.teams.length]._id.toHexString();
game.currentTurn.cardsLocked = [];
game.currentTurn.lockedEffects = [];
game.currentTurn.tokens = [];
game.currentTurn.damage = 0;
game.currentTurn.gold = 0;
await game.save({ session });
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'endTurn',
currentTurn: game.currentTurn,
});
currentTeam = getCurrentTeam(game);
for (const player of currentTeam.players) {
for (const card of player.board.cards) {
if (card.defense) {
await this.turnService.lockCard(game, player, card, session);
}
}
}
for (const player of currentTeam.players) {
await this.distributeCards(
player.hand.cards,
player.hand,
player.board,
game,
session,
);
}
await game.save({ session });
}
@WithTransaction
async drawToBoard(
amount,
player: Player,
game: Document<Game> & Game,
session?: mongoose.mongo.ClientSession,
) {
await this.drawCards(
amount,
player.stack,
player.board,
player.discard,
game,
session,
);
}
@WithTransaction
async drawToHand(
amount,
player: Player,
game: Document<Game> & Game,
session?: mongoose.mongo.ClientSession,
) {
await this.drawCards(
amount,
player.stack,
player.hand,
player.discard,
game,
session,
);
}
@WithTransaction
private async drawCards(
amount: number,
from: Container,
to: Container,
discard: Container,
game: Document<Game> & Game,
session?: mongoose.mongo.ClientSession,
) {
if (amount <= from.cards.length) {
await this.distributeCards(
from.cards.slice(0, amount),
from,
to,
game,
session,
);
await game.save({ session });
return;
} else {
const toDraw = from.cards.length;
await this.distributeCards(
from.cards.slice(0, toDraw),
from,
to,
game,
session,
);
if (discard.cards.length == 0) {
await game.save({ session });
return;
}
// restack all discarded
await this.distributeCards(discard.cards, discard, from, game, session);
await this.shuffleContainer(from, game, session);
await this.drawCards(amount - toDraw, from, to, discard, game, session);
}
}
@WithTransaction
async fillMarket(
game: Document<Game> & Game,
session?: mongoose.mongo.ClientSession,
) {
if (game.market.cards.length < 5) {
const amount = Math.min(
game.marketStack.cards.length,
5 - game.market.cards.length,
);
await this.distributeCards(
game.marketStack.cards.slice(0, amount),
game.marketStack,
game.market,
game,
session,
);
}
const missingIndexes = game.market.cards.reduce(function (a, e, i) {
if (e === null) a.push(i);
return a;
}, []);
if (missingIndexes.length > 0) {
console.error('not implemented');
}
}
@WithTransaction
async shuffleContainer(
container: Container,
game: Document<Game> & Game,
session?: mongoose.mongo.ClientSession,
) {
container.cards = shuffle(container.cards);
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'shuffleContainer',
container: container._id.toHexString(),
});
await game.save({ session });
}
@WithTransaction
async distributeCards(
cards: Card[],
from: Container,
to: Container,
game: Document<Game> & Game,
session?: mongoose.mongo.ClientSession,
) {
if (cards.some((c) => !from.cards.some((cc) => cc._id.equals(c._id)))) {
throw new HttpException(
'Card not found in container',
HttpStatus.NOT_FOUND,
);
}
const shallow = [...cards];
for (const c of shallow) {
const index = from.cards.findIndex((cc) => cc?._id.equals(c._id));
from.cards.splice(index, 1);
to.cards.push(c);
}
await game.save({ session });
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'distributeCards',
from: from._id.toHexString(),
to: to._id.toHexString(),
cards: shallow,
});
}
@WithTransaction
async destroyCard(
card: Card,
from: Container,
game: Document<Game> & Game,
session?: mongoose.mongo.ClientSession,
) {
if (card.role == CardRole.FIRE_GEM) {
await this.distributeCards([card], from, game.fireGems, game, session);
return;
}
const index = from.cards.findIndex((cc) => cc?._id.equals(card._id));
from.cards.splice(index, 1);
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'destroyCard',
from: from._id.toHexString(),
card: card._id,
});
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 });
}
}