395 lines
11 KiB
TypeScript
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 });
|
|
}
|
|
}
|