Feat : implement per for effects

This commit is contained in:
2024-07-07 18:26:47 +02:00
parent 8b5c38a28c
commit a3907716cd
6 changed files with 212 additions and 28 deletions
+37
View File
@@ -0,0 +1,37 @@
import { DatabaseObjectDto } from 'src/games/dto/database-object.dto';
import { Effect, EffectCondition } from '../schemas/effect.schema';
import { CardEffects, CardFaction, Per } from '../schemas/cards.types';
import { Card } from '../schemas/cards.schema';
export class ReadEffectDto extends DatabaseObjectDto {
constructor(effect: Effect) {
super(effect);
this.effect = effect.effect;
this.amount = effect.amount;
this.times = effect.times;
this.subEffects = effect.subEffects;
this.condition = effect.condition;
this.per = effect.per;
this.mutex = effect.mutex;
this.card = effect.card._id.toHexString();
this.faction = effect.faction;
}
effect: CardEffects;
amount: number;
times: number;
subEffects: Effect[];
condition?: EffectCondition;
per?: Per;
mutex?: string;
card: string;
faction?: CardFaction;
}
+1 -3
View File
@@ -108,9 +108,7 @@ export enum EffectType {
CHAMPION_ACTION = 'champion_action', CHAMPION_ACTION = 'champion_action',
SUICIDE = 'suicide', SUICIDE = 'suicide',
FACTION_COMBO = 'faction_combo', FACTION_COMBO = 'faction_combo',
}
export enum EffectTypeSpecial {
CHAMPION_AMOUNT = 'champion_amount', CHAMPION_AMOUNT = 'champion_amount',
TOTAL_DAMAGE = 'total_damage', TOTAL_DAMAGE = 'total_damage',
CARD_AMOUNT = 'card_amount', CARD_AMOUNT = 'card_amount',
@@ -118,7 +116,7 @@ export enum EffectTypeSpecial {
} }
export type EffectTypeVerbose = { export type EffectTypeVerbose = {
id: EffectType | EffectTypeSpecial; id: EffectType;
faction?: string; faction?: string;
amount?: number; amount?: number;
card_id?: number; card_id?: number;
+3 -9
View File
@@ -1,18 +1,12 @@
import mongoose, { Types } from 'mongoose'; import mongoose, { Types } from 'mongoose';
import { import { CardEffects, CardFaction, EffectType, Per } from './cards.types';
CardEffects,
CardFaction,
EffectType,
EffectTypeSpecial,
Per,
} from './cards.types';
import { Card } from './cards.schema'; import { Card } from './cards.schema';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
// @Schema() // @Schema()
export class EffectCondition { export class EffectCondition {
@Prop({ type: String, enum: { ...EffectType, ...EffectTypeSpecial } }) @Prop({ type: String, enum: EffectType })
effectId: EffectType | EffectTypeSpecial; effectId: EffectType;
@Prop({ type: Number }) @Prop({ type: Number })
amount: number; amount: number;
+94 -3
View File
@@ -2,10 +2,11 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
import { Game, Player } from '../schemas/game.schema'; import { Game, Player } from '../schemas/game.schema';
import mongoose, { Document } from 'mongoose'; import mongoose, { Document } from 'mongoose';
import { Card } from 'src/cards/schemas/cards.schema'; import { Card } from 'src/cards/schemas/cards.schema';
import { CardEffects, EffectType } from 'src/cards/schemas/cards.types'; import { CardEffects, EffectType, Per } from 'src/cards/schemas/cards.types';
import { Effect } from 'src/cards/schemas/effect.schema'; import { Effect, EffectCondition } from 'src/cards/schemas/effect.schema';
import { TurnService } from './turn.service'; import { TurnService } from './turn.service';
import { GameService } from './games.service'; import { GameService } from './games.service';
import { ReadEffectDto } from 'src/cards/dto/effect.dto';
export function isAutoActivable(effect: Effect) { export function isAutoActivable(effect: Effect) {
if ( if (
@@ -30,7 +31,7 @@ async function addToken(
game.currentTurn.tokens.push(effect); game.currentTurn.tokens.push(effect);
eventEmitter.emit('sse.game.' + game._id.toHexString(), { eventEmitter.emit('sse.game.' + game._id.toHexString(), {
type: 'currentTurn.addToken', type: 'currentTurn.addToken',
token: effect, token: new ReadEffectDto(effect),
}); });
} }
@@ -151,3 +152,93 @@ export const effectMappings = {
session: mongoose.mongo.ClientSession, session: mongoose.mongo.ClientSession,
) => Promise<void> ) => Promise<void>
>; >;
export const conditionsMappings = {
[EffectType.CHAMPION_ACTION]: async () => true,
[EffectType.FACTION_COMBO]: async (
game: Document<Game> & Game,
player: Player,
card: Card,
condition: EffectCondition,
) => {
return game.currentTurn.cardsLocked.some(
(c) => c.faction == condition.faction && !c._id.equals(card._id),
);
},
[EffectType.SUICIDE]: async (
game: Document<Game> & Game,
player: Player,
card: Card,
condition: EffectCondition,
gameService: GameService,
session: mongoose.mongo.ClientSession,
) => {
await gameService.destroyCard(card, player.board, game, session);
return true;
},
// [EffectTypeSpecial.ACTION_AMOUNT]: {},
// [EffectTypeSpecial.CARD_AMOUNT]: {},
// [EffectTypeSpecial.CHAMPION_AMOUNT]: {},
// [EffectTypeSpecial.TOTAL_DAMAGE]: {},
} as Record<
string,
(
game: Document<Game> & Game,
player: Player,
card: Card,
condition: EffectCondition,
gameService: GameService,
session: mongoose.mongo.ClientSession,
) => Promise<boolean>
>;
export const perMapping = {
[Per.CARD_OF_SAME_FACTION]: async (
game: Document<Game> & Game,
card: Card,
) => {
return game.currentTurn.cardsLocked.filter((c) => c.faction == card.faction)
.length;
},
[Per.OTHER_CARD_OF_SAME_FACTION]: async (
game: Document<Game> & Game,
card: Card,
) => {
return game.currentTurn.cardsLocked.filter(
(c) => c.faction == card.faction && !c._id.equals(card._id),
).length;
},
[Per.CHAMPION]: async (game: Document<Game> & Game) => {
return game.currentTurn.cardsLocked.filter((c) => c.defense).length;
},
[Per.CHAMPION_OF_SAME_FACTION]: async (
game: Document<Game> & Game,
card: Card,
) => {
return game.currentTurn.cardsLocked.filter(
(c) => c.faction == card.faction && c.defense,
).length;
},
[Per.OTHER_CHAMPION]: async (game: Document<Game> & Game, card: Card) => {
return game.currentTurn.cardsLocked.filter(
(c) => !c._id.equals(card._id) && c.defense,
).length;
},
[Per.OTHER_GUARD]: async (game: Document<Game> & Game, card: Card) => {
return game.currentTurn.cardsLocked.filter(
(c) => !c._id.equals(card._id) && c.guard,
).length;
},
[Per.STUNNED_CHAMPION]: async () => {
console.error('per_stunned_champion not implemented');
return 0;
},
[Per.OTHER_KNIFE_PLAYED]: async (game: Document<Game> & Game, card: Card) => {
return game.currentTurn.cardsLocked.filter(
(c) => !c._id.equals(card._id) && c.cardId == 80,
).length;
},
} as Record<
string,
(game: Document<Game> & Game, card: Card) => Promise<number>
>;
+24
View File
@@ -12,6 +12,7 @@ import { Card } from 'src/cards/schemas/cards.schema';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { WithTransaction, getCurrentTeam, shuffle } from '../utils'; import { WithTransaction, getCurrentTeam, shuffle } from '../utils';
import { TurnService } from './turn.service'; import { TurnService } from './turn.service';
import { CardRole, CardType } from 'src/cards/schemas/cards.types';
@Injectable() @Injectable()
export class GameService { export class GameService {
@@ -224,4 +225,27 @@ export class GameService {
cards: shallow, 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 });
}
} }
+53 -13
View File
@@ -12,7 +12,12 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
import { Effect } from 'src/cards/schemas/effect.schema'; import { Effect } from 'src/cards/schemas/effect.schema';
import { CardEffects, EffectType } from 'src/cards/schemas/cards.types'; import { CardEffects, EffectType } from 'src/cards/schemas/cards.types';
import { WithTransaction } from '../utils'; import { WithTransaction } from '../utils';
import { effectMappings, isAutoActivable } from './effect.functions'; import {
conditionsMappings,
effectMappings,
isAutoActivable,
perMapping,
} from './effect.functions';
import { InjectConnection, InjectModel } from '@nestjs/mongoose'; import { InjectConnection, InjectModel } from '@nestjs/mongoose';
import { GameService } from './games.service'; import { GameService } from './games.service';
@@ -55,14 +60,37 @@ export class TurnService {
session?: mongoose.mongo.ClientSession, session?: mongoose.mongo.ClientSession,
) { ) {
const effectRunner = effectMappings[effect.effect]; const effectRunner = effectMappings[effect.effect];
if (!effectMappings[effect.effect]) { if (!effectRunner) {
console.warn(`Effect ${effect.effect} not implemented`);
return;
throw new HttpException( throw new HttpException(
`Effect ${effect.effect} not implemented`, `Effect ${effect.effect} not implemented`,
HttpStatus.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(); const effectUUID = effect._id.toHexString();
if (game.currentTurn.lockedEffects.some((e) => e._id.equals(effectUUID))) { if (game.currentTurn.lockedEffects.some((e) => e._id.equals(effectUUID))) {
console.error(`Effect ${effectUUID} already used this turn`); console.error(`Effect ${effectUUID} already used this turn`);
@@ -75,6 +103,9 @@ export class TurnService {
type: 'lockEffect', type: 'lockEffect',
effect: effectUUID, effect: effectUUID,
}); });
// Process effect mutexes
if (effect.mutex) { if (effect.mutex) {
for (const e of card.effects) { for (const e of card.effects) {
if (e.mutex == effect.mutex) { if (e.mutex == effect.mutex) {
@@ -86,16 +117,25 @@ export class TurnService {
} }
} }
} }
// Process effect Per
game.currentTurn.lockedEffects.push(effect); game.currentTurn.lockedEffects.push(effect);
await effectRunner( let times = 1;
game, if (effect.per) {
effect, times = await perMapping[effect.per](game, card);
player, }
card, for (let t = 0; t < times; t++) {
this.gamesService, await effectRunner(
this.eventEmitter, game,
session, effect,
); player,
card,
this.gamesService,
this.eventEmitter,
session,
);
}
await game.save({ session }); await game.save({ session });
} }