Files
back-ts/src/games/services/turn.service.ts
T
legonzaur 9b83bc5a99
release-tag / release-image (push) Successful in 39s
Fix : rampage
2024-08-02 08:52:23 +02:00

395 lines
11 KiB
TypeScript

import {
forwardRef,
HttpException,
HttpStatus,
Inject,
Injectable,
} from '@nestjs/common';
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 { Effect } from 'src/cards/schemas/effect.schema';
import {
CardEffects,
CardRole,
CardType,
EffectType,
} from 'src/cards/schemas/cards.types';
import { getPlayerTeam, WithTransaction } from '../utils';
import {
conditionsMappings,
effectMappings,
isAutoActivable,
perMapping,
removeToken,
tokenMappings,
} from './effect.functions';
import { InjectConnection, InjectModel } from '@nestjs/mongoose';
import { GameService } from './games.service';
@Injectable()
export class TurnService {
constructor(
private eventEmitter: EventEmitter2,
@Inject(forwardRef(() => GameService))
private readonly gamesService: GameService,
@InjectModel(Game.name) private gameModel: Model<Game>,
@InjectConnection() private readonly connection: mongoose.Connection,
) {}
@WithTransaction
async lockCard(
game: Document<Game> & Game,
player: Player,
card: Card,
session?: mongoose.mongo.ClientSession,
) {
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 (isAutoActivable(effect)) {
await this.lockEffect(game, player, effect, card, session);
}
}
await game.save({ session });
}
@WithTransaction
async lockEffect(
game: Document<Game> & Game,
player: Player,
effect: Effect,
card: Card,
session?: mongoose.mongo.ClientSession,
) {
const effectRunner = effectMappings[effect.effect];
if (!effectRunner) {
throw new HttpException(
`Effect ${effect.effect} not implemented`,
HttpStatus.NOT_IMPLEMENTED,
);
}
// Process effect conditions
if (effect.condition) {
const effectCondition = conditionsMappings[effect.condition.effectId];
if (!effectCondition) {
throw new HttpException(
`Condition ${effect.condition.effectId} not implemented`,
HttpStatus.NOT_IMPLEMENTED,
);
}
const conditionSuccess = await effectCondition(
game,
player,
card,
effect.condition,
this.gamesService,
session,
);
if (!conditionSuccess) {
throw new HttpException(
`Condition ${effect.condition.effectId} not met`,
HttpStatus.BAD_REQUEST,
);
}
}
const effectUUID = effect._id.toHexString();
if (game.currentTurn.lockedEffects.some((e) => e._id.equals(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: 'lockEffect',
effect: effectUUID,
});
// Process effect mutexes
if (effect.mutex) {
for (const e of card.effects) {
if (e.mutex == effect.mutex) {
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'lockEffect',
effect: e._id.toHexString(),
});
game.currentTurn.lockedEffects.push(e);
}
}
}
// Process effect Per
game.currentTurn.lockedEffects.push(effect);
let times = 1;
if (effect.per) {
times = await perMapping[effect.per](game, card);
}
for (let t = 0; t < times; t++) {
await effectRunner(
game,
effect,
player,
card,
this.gamesService,
this.eventEmitter,
session,
);
}
await game.save({ session });
}
@WithTransaction
async buyGeneric(
game: Document<Game> & Game,
card: Card,
fromContainer: Container,
toContainer: Container,
session?: mongoose.mongo.ClientSession,
) {
if (!card.cost) {
throw new TypeError('Card cost is null or undefined');
}
// const targetChangingEffects = [CardEffects.STACK_NEXT_CARD_BOUGHT];
// if (card.cardType == CardType.ACTION) {
// targetChangingEffects.push(CardEffects.STACK_NEXT_ACTION_BOUGHT);
// }
// const targetChangingToken = game.currentTurn.tokens.find((e) =>
// targetChangingEffects.includes(e.effect),
// );
// const forFreeEffects = [CardEffects.BUY_FOR_FREE];
// if (card.role == CardRole.FIRE_GEM) {
// forFreeEffects.push(CardEffects.BUY_GEM_FOR_FREE);
// }
// const forFreeToken = game.currentTurn.tokens.find((e) =>
// forFreeEffects.includes(e.effect),
// );
// const reducedPriceEffects = [];
// if (card.cardType == CardType.ACTION) {
// reducedPriceEffects.push(CardEffects.CHEAPER_ACTION);
// }
// if (card.cardType == CardType.CHAMPION) {
// reducedPriceEffects.push(CardEffects.CHEAPER_CHAMPION);
// reducedPriceEffects.push(CardEffects.CHEAPER_CHAMPIONS_PASSIVE);
// }
// // if(card.cost)
// CardEffects.CHEAPER_CARD_IF_HIGHER_PRICE
// const reducedPriceToken = game.currentTurn.tokens.find((e) =>
// forFreeEffects.includes(e.effect),
// );
const cost = card.cost;
// if (forFreeToken) {
// cost = 0;
// removeToken(game, targetChangingToken, this.eventEmitter);
// }
if (game.currentTurn.gold < cost) {
throw new HttpException('Not enough gold', HttpStatus.BAD_REQUEST);
}
// if (targetChangingToken) {
// targetContainer = player.stack;
// removeToken(game, targetChangingToken, this.eventEmitter);
// }
await this.gamesService.distributeCards(
[card],
fromContainer,
toContainer,
game,
session,
);
if (cost != 0) {
game.currentTurn.gold -= cost;
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'currentTurn.updateGold',
gold: game.currentTurn.gold,
operation: -cost,
});
}
await game.save({ session });
}
@WithTransaction
async makeDiscard(
game: Document<Game> & Game,
token: Effect,
target: Player,
session?: mongoose.mongo.ClientSession,
) {
target.fuckUMarkers++;
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'player.addFuckUMarker',
operation: 1,
fuckUMarkers: target.fuckUMarkers,
playerId: target._id.toHexString(),
});
const index = game.currentTurn.tokens.findIndex((t) =>
t._id.equals(token._id),
);
if (index == -1) {
throw new Error('Token not found');
}
game.currentTurn.tokens.splice(index, 1);
await game.save({ session });
}
@WithTransaction
async useToken(
game: Document<Game> & Game,
player: Player,
token: Effect,
target: Card,
session?: mongoose.mongo.ClientSession,
) {
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,
);
if (
token.effect != CardEffects.DRAW_AND_DISCARD ||
(token.effect == CardEffects.DRAW_AND_DISCARD &&
token.times <= game.currentTurn.drawAndDiscardCurrentAmount)
) {
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 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,
});
await game.save({ session });
if (team.health <= 0) {
await this.gamesService.killTeam(game, team, session);
}
}
@WithTransaction
async damageChampion(
game: Document<Game> & Game,
targetPlayer: Player,
targetCard: Card,
session?: mongoose.mongo.ClientSession,
) {
game.currentTurn.damage -= targetCard.defense;
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'currentTurn.updateDamage',
damage: game.currentTurn.damage,
operation: -targetCard.defense,
});
await this.gamesService.distributeCards(
[targetCard],
targetPlayer.board,
targetPlayer.discard,
game,
session,
);
await game.save({ session });
}
@WithTransaction
async discardCard(
game: Document<Game> & Game,
player: Player,
card: Card,
session?: mongoose.mongo.ClientSession,
) {
await this.gamesService.distributeCards(
[card],
player.board,
player.discard,
game,
);
if (player.discardMarkers > 0) {
player.discardMarkers--;
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'player.removeDiscardMarker',
operation: -1,
discardMarkers: player.discardMarkers,
playerId: player._id.toHexString(),
});
} else if (player.fuckUMarkers > 0) {
player.fuckUMarkers--;
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'player.removeFuckUMarker',
operation: -1,
fuckUMarkers: player.fuckUMarkers,
playerId: player._id.toHexString(),
});
}
game.currentTurn.drawAndDiscardCurrentAmount = 0;
if (game.currentTurn.drawAndDiscardCurrentEffect) {
const tokenIndex = game.currentTurn.tokens.findIndex((t) =>
t._id.equals(game.currentTurn.drawAndDiscardCurrentEffect._id),
);
game.currentTurn.tokens.splice(tokenIndex, 1);
this.eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'currentTurn.useToken',
tokenId: game.currentTurn.drawAndDiscardCurrentEffect._id.toHexString(),
});
game.currentTurn.drawAndDiscardCurrentEffect = null;
}
await game.save({ session });
}
}