Feat : implement per for effects
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
>;
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user