WIP : before-cqrs
This commit is contained in:
Vendored
+16
-4
@@ -5,10 +5,22 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"command": "npm run start:dev -b swc",
|
"name": "Launch via NPM",
|
||||||
"name": "Start Nest",
|
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal"
|
"runtimeArgs": [
|
||||||
},
|
"run",
|
||||||
|
"start:dev",
|
||||||
|
"-b",
|
||||||
|
"swc"
|
||||||
|
],
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"type": "node",
|
||||||
|
"console": "internalConsole",
|
||||||
|
"outputCapture": "std",
|
||||||
|
"internalConsoleOptions": "openOnSessionStart"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Generated
+10
@@ -19,6 +19,7 @@
|
|||||||
"connect-mongo": "^5.1.0",
|
"connect-mongo": "^5.1.0",
|
||||||
"express-session": "^1.18.0",
|
"express-session": "^1.18.0",
|
||||||
"mongoose": "^8.4.1",
|
"mongoose": "^8.4.1",
|
||||||
|
"mongoose-autopopulate": "^1.1.0",
|
||||||
"reflect-metadata": "^0.2.1",
|
"reflect-metadata": "^0.2.1",
|
||||||
"rxjs": "^7.8.1"
|
"rxjs": "^7.8.1"
|
||||||
},
|
},
|
||||||
@@ -7587,6 +7588,15 @@
|
|||||||
"url": "https://opencollective.com/mongoose"
|
"url": "https://opencollective.com/mongoose"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mongoose-autopopulate": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongoose-autopopulate/-/mongoose-autopopulate-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-nTlTMlu1fLQ1bmJT7ILKbZmPGt2fHErLO4UJwzMDsHSigjtUYz0l3nvFhg511QkOkZcKBRzOnPn3DmmLIUENzg==",
|
||||||
|
"license": "Apache 2.0",
|
||||||
|
"peerDependencies": {
|
||||||
|
"mongoose": "6.x || 7.x || 8.0.0-rc0 || 8.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mongoose/node_modules/ms": {
|
"node_modules/mongoose/node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
"connect-mongo": "^5.1.0",
|
"connect-mongo": "^5.1.0",
|
||||||
"express-session": "^1.18.0",
|
"express-session": "^1.18.0",
|
||||||
"mongoose": "^8.4.1",
|
"mongoose": "^8.4.1",
|
||||||
|
"mongoose-autopopulate": "^1.1.0",
|
||||||
"reflect-metadata": "^0.2.1",
|
"reflect-metadata": "^0.2.1",
|
||||||
"rxjs": "^7.8.1"
|
"rxjs": "^7.8.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import { Controller, Get } from '@nestjs/common';
|
import { Controller, Put } from '@nestjs/common';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
|
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
@ApiTags('admin')
|
||||||
@Controller()
|
@Controller()
|
||||||
export class AppController {
|
export class AppController {
|
||||||
constructor(private readonly appService: AppService) {}
|
constructor(private readonly appService: AppService) {}
|
||||||
|
|
||||||
@Get()
|
@Put('loadCards')
|
||||||
getHello(): string {
|
@ApiOperation({ summary: 'Load cards into the database' })
|
||||||
return this.appService.getHello();
|
loadCards(): void {
|
||||||
|
this.appService.loadCards();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { AppService } from './app.service';
|
|||||||
import { GamesModule } from './games/games.module';
|
import { GamesModule } from './games/games.module';
|
||||||
import { UsersModule } from './users/users.module';
|
import { UsersModule } from './users/users.module';
|
||||||
import { CardsService } from './cards/cards.service';
|
import { CardsService } from './cards/cards.service';
|
||||||
|
import { CardsModule } from './cards/cards.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -16,7 +17,13 @@ import { CardsService } from './cards/cards.service';
|
|||||||
username: process.env.MONGO_USER,
|
username: process.env.MONGO_USER,
|
||||||
},
|
},
|
||||||
dbName: 'heron',
|
dbName: 'heron',
|
||||||
|
connectionFactory: (connection) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
connection.plugin(require('mongoose-autopopulate'));
|
||||||
|
return connection;
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
|
CardsModule,
|
||||||
GamesModule,
|
GamesModule,
|
||||||
UsersModule,
|
UsersModule,
|
||||||
],
|
],
|
||||||
|
|||||||
+4
-2
@@ -1,8 +1,10 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { CardsService } from './cards/cards.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppService {
|
export class AppService {
|
||||||
getHello(): string {
|
constructor(private readonly cardService: CardsService) {}
|
||||||
return 'Hello World!';
|
loadCards(): void {
|
||||||
|
this.cardService.loadCards();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,24 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { MongooseModule } from '@nestjs/mongoose';
|
import { MongooseModule } from '@nestjs/mongoose';
|
||||||
import { Card, CardSchema } from './schemas/cards.schema';
|
import { Card, CardSchema } from './schemas/cards.schema';
|
||||||
|
import { CardsService } from './cards.service';
|
||||||
|
// import {
|
||||||
|
// Effect,
|
||||||
|
// EffectCondition,
|
||||||
|
// EffectConditionSchema,
|
||||||
|
// EffectSchema,
|
||||||
|
// } from './schemas/effect.schema';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
MongooseModule.forFeature([{ name: Card.name, schema: CardSchema }]),
|
MongooseModule.forFeature([
|
||||||
|
// { name: Effect.name, schema: EffectSchema },
|
||||||
|
// { name: EffectCondition.name, schema: EffectConditionSchema },
|
||||||
|
{ name: Card.name, schema: CardSchema },
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
exports: [MongooseModule],
|
providers: [CardsService],
|
||||||
|
exports: [MongooseModule, CardsService],
|
||||||
})
|
})
|
||||||
export class CardsModule {
|
export class CardsModule {
|
||||||
schema = CardSchema;
|
schema = CardSchema;
|
||||||
|
|||||||
+118
-3
@@ -1,11 +1,26 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
|
import { Effect } from './schemas/effect.schema';
|
||||||
|
import { Card } from './schemas/cards.schema';
|
||||||
|
import { Model } from 'mongoose';
|
||||||
|
import { InjectModel } from '@nestjs/mongoose';
|
||||||
|
import {
|
||||||
|
CardFaction,
|
||||||
|
CardParser,
|
||||||
|
CardRole,
|
||||||
|
EffectParser,
|
||||||
|
EffectType,
|
||||||
|
EffectTypeVerbose,
|
||||||
|
} from './schemas/cards.types';
|
||||||
|
import { EffectCondition } from './schemas/effect.schema';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CardsService {
|
export class CardsService {
|
||||||
|
constructor(@InjectModel(Card.name) private cardModel: Model<Card>) {}
|
||||||
|
|
||||||
async loadCards(): Promise<void> {
|
async loadCards(): Promise<void> {
|
||||||
const cardPath = path.join(path.resolve(), 'cards');
|
const cardPath = path.join(path.resolve(), 'src', 'json');
|
||||||
const files = await fs.readdir(cardPath);
|
const files = await fs.readdir(cardPath);
|
||||||
const processed = await Promise.all(
|
const processed = await Promise.all(
|
||||||
files.map(async (f) => {
|
files.map(async (f) => {
|
||||||
@@ -20,7 +35,107 @@ export class CardsService {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const packs = Object.fromEntries(processed);
|
await Promise.all(
|
||||||
console.log(packs);
|
Object.entries(processed).map(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
async ([_, data]: [string, [string, CardParser[]]]) => {
|
||||||
|
await Promise.all(
|
||||||
|
data[1].map((c) => {
|
||||||
|
if (c.role == CardRole.FIRE_GEM || c.role == CardRole.MARKET) {
|
||||||
|
for (let i = 0; i < (c.init_amount ?? 1); i++) {
|
||||||
|
this.createCard(c, data[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let j = 0; j < 4; j++) {
|
||||||
|
for (let i = 0; i < (c.init_amount ?? 1); i++) {
|
||||||
|
this.createCard(c, data[0], j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createCard(
|
||||||
|
json: CardParser,
|
||||||
|
pack: string,
|
||||||
|
playerIndex?: number,
|
||||||
|
) {
|
||||||
|
const createdCard = await this.cardModel.create({
|
||||||
|
cardId: json.id,
|
||||||
|
role: json.role,
|
||||||
|
cardType: json.card_type,
|
||||||
|
faction: json.faction,
|
||||||
|
name: json.name,
|
||||||
|
cost: json.cost,
|
||||||
|
defense: json.defense,
|
||||||
|
guard: json.guard,
|
||||||
|
pack: pack,
|
||||||
|
playerIndex,
|
||||||
|
});
|
||||||
|
if (json.effects) {
|
||||||
|
createdCard.effects = await this.createEffects(json.effects, createdCard);
|
||||||
|
}
|
||||||
|
createdCard.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createEffects(
|
||||||
|
json: EffectParser[],
|
||||||
|
card: Card,
|
||||||
|
): Promise<Effect[]> {
|
||||||
|
return await Promise.all(
|
||||||
|
json.map(async (e) => {
|
||||||
|
const createdEffect = {
|
||||||
|
card: card,
|
||||||
|
effect: e.effect,
|
||||||
|
amount: e.amount,
|
||||||
|
times: e.times ?? 1,
|
||||||
|
per: e.per,
|
||||||
|
faction: e.faction,
|
||||||
|
} as Effect;
|
||||||
|
if (e.sub_effects) {
|
||||||
|
createdEffect.subEffects = await this.createEffects(
|
||||||
|
e.sub_effects,
|
||||||
|
card,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
createdEffect.subEffects = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.effect_type) {
|
||||||
|
createdEffect.condition = await this.createEffectCondition(
|
||||||
|
e.effect_type,
|
||||||
|
card,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return createdEffect;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createEffectCondition(
|
||||||
|
json: EffectType | EffectTypeVerbose,
|
||||||
|
card: Card,
|
||||||
|
): Promise<EffectCondition> {
|
||||||
|
const createdCondition = {} as EffectCondition;
|
||||||
|
if (typeof json === 'string') {
|
||||||
|
createdCondition.effectId = json;
|
||||||
|
if (json == EffectType.FACTION_COMBO) {
|
||||||
|
createdCondition.faction = card.faction;
|
||||||
|
}
|
||||||
|
createdCondition.amount = 1;
|
||||||
|
} else {
|
||||||
|
createdCondition.effectId = json.id;
|
||||||
|
createdCondition.amount = json.amount ?? 1;
|
||||||
|
createdCondition.cardId = json.card_id;
|
||||||
|
if (json.id == EffectType.FACTION_COMBO) {
|
||||||
|
createdCondition.faction = (json.faction ??
|
||||||
|
card.faction) as CardFaction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return createdCondition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,42 @@
|
|||||||
import { Schema, SchemaFactory } from '@nestjs/mongoose';
|
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||||
import { CardFaction, CardRole, CardType, EffectParser } from './cards.types';
|
import { CardFaction, CardRole, CardType } from './cards.types';
|
||||||
|
import { Types } from 'mongoose';
|
||||||
|
import { Effect } from './effect.schema';
|
||||||
|
|
||||||
@Schema()
|
@Schema()
|
||||||
export class Card {
|
export class Card extends Types.ObjectId {
|
||||||
|
@Prop({ type: Number })
|
||||||
cardId: number;
|
cardId: number;
|
||||||
|
|
||||||
|
@Prop({ type: String, enum: CardRole })
|
||||||
role: CardRole;
|
role: CardRole;
|
||||||
|
|
||||||
|
@Prop()
|
||||||
|
playerIndex?: number;
|
||||||
|
|
||||||
|
@Prop({ type: String, enum: CardType })
|
||||||
cardType: CardType;
|
cardType: CardType;
|
||||||
|
|
||||||
|
@Prop({ type: String, enum: CardFaction })
|
||||||
faction: CardFaction;
|
faction: CardFaction;
|
||||||
|
|
||||||
|
@Prop({ type: String })
|
||||||
name: string;
|
name: string;
|
||||||
initAmount: number;
|
|
||||||
|
@Prop()
|
||||||
cost?: number;
|
cost?: number;
|
||||||
|
|
||||||
|
@Prop()
|
||||||
defense?: number;
|
defense?: number;
|
||||||
|
|
||||||
|
@Prop()
|
||||||
guard?: boolean;
|
guard?: boolean;
|
||||||
effects: EffectParser[];
|
|
||||||
|
@Prop({ type: [Effect], default: [], autopopulate: true })
|
||||||
|
effects: Effect[];
|
||||||
|
|
||||||
|
@Prop()
|
||||||
|
pack: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CardSchema = SchemaFactory.createForClass(Card);
|
export const CardSchema = SchemaFactory.createForClass(Card);
|
||||||
|
|||||||
@@ -5,6 +5,21 @@ export enum CardRole {
|
|||||||
PERSONAL = 'personal',
|
PERSONAL = 'personal',
|
||||||
MARKET = 'market',
|
MARKET = 'market',
|
||||||
FIRE_GEM = 'fire_gem',
|
FIRE_GEM = 'fire_gem',
|
||||||
|
|
||||||
|
HUNTER = 'hunter',
|
||||||
|
TRAVELER = 'traveler',
|
||||||
|
CLERIC = 'cleric',
|
||||||
|
FIGHTER = 'fighter',
|
||||||
|
RANGER = 'ranger',
|
||||||
|
THIEF = 'thief',
|
||||||
|
WIZARD = 'wizard',
|
||||||
|
|
||||||
|
HALF_DEMON = 'half-demon',
|
||||||
|
DWARF = 'dwarf',
|
||||||
|
ELF = 'elf',
|
||||||
|
OGRE = 'ogre',
|
||||||
|
ORC = 'orc',
|
||||||
|
SMALLFOLK = 'smallfolk',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,6 +48,7 @@ export enum CardFaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum CardEffects {
|
export enum CardEffects {
|
||||||
|
//Base
|
||||||
GOLD = 'gold',
|
GOLD = 'gold',
|
||||||
DAMAGE = 'damage',
|
DAMAGE = 'damage',
|
||||||
HEAL = 'heal',
|
HEAL = 'heal',
|
||||||
@@ -47,14 +63,45 @@ export enum CardEffects {
|
|||||||
STACK_NEXT_ACTION_BOUGHT = 'stack_next_action_bought',
|
STACK_NEXT_ACTION_BOUGHT = 'stack_next_action_bought',
|
||||||
STACK_NEXT_CARD_BOUGHT = 'stack_next_card_bought',
|
STACK_NEXT_CARD_BOUGHT = 'stack_next_card_bought',
|
||||||
PLAY_NEXT_CARD_BOUGHT = 'play_next_card_bought',
|
PLAY_NEXT_CARD_BOUGHT = 'play_next_card_bought',
|
||||||
|
|
||||||
|
//DLCs
|
||||||
|
BUY_FOR_FREE = 'buy_for_free',
|
||||||
|
|
||||||
|
//Journeys
|
||||||
|
DAMAGE_ALL_CHAMPIONS = 'damage_all_champions',
|
||||||
|
|
||||||
|
//Journeys: Hunters
|
||||||
|
DISCARD_X_AND_DRAW_X = 'discard_x_and_draw_x',
|
||||||
|
PREPARE_ANOTHER_CHAMPION = 'prepare_another_champion',
|
||||||
|
//Journey: Travelers
|
||||||
|
PREPARE_ALL_CHAMPIONS = 'prepare_all_champions',
|
||||||
|
CHEAPER_CHAMPION = 'cheaper_champion',
|
||||||
|
CHEAPER_CHAMPIONS_PASSIVE = 'cheaper_champions_passive',
|
||||||
|
CHEAPER_ACTION = 'cheaper_action',
|
||||||
|
CONTROL_OPPOSING_CHAMPION_PASSIVE = 'control_opposing_champion_passive',
|
||||||
|
|
||||||
|
RESTACK_DISCARDED_ACTION = 'restack_discarded_action',
|
||||||
|
|
||||||
|
//Ancestry
|
||||||
|
KEEP_IN_HAND = 'keep_in_hand',
|
||||||
|
BUY_GEM_FOR_FREE = 'buy_gem_for_free',
|
||||||
|
CHEAPER_SKILLS_PASSIVE = 'cheaper_skills_passive',
|
||||||
|
CHEAPER_CARD_IF_HIGHER_PRICE = 'cheaper_card_if_higher_price',
|
||||||
|
PICK_FACTION = 'pick_faction',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Per {
|
export enum Per {
|
||||||
|
//Base
|
||||||
CHAMPION = 'champion',
|
CHAMPION = 'champion',
|
||||||
OTHER_CHAMPION = 'other_champion',
|
OTHER_CHAMPION = 'other_champion',
|
||||||
OTHER_GUARD = 'other_guard',
|
OTHER_GUARD = 'other_guard',
|
||||||
CARD_OF_SAME_FACTION = 'card_of_same_faction',
|
CARD_OF_SAME_FACTION = 'card_of_same_faction',
|
||||||
OTHER_CARD_OF_SAME_FACTION = 'other_card_of_same_faction',
|
OTHER_CARD_OF_SAME_FACTION = 'other_card_of_same_faction',
|
||||||
|
|
||||||
|
//Journey: Travelers
|
||||||
|
STUNNED_CHAMPION = 'stunned_champion',
|
||||||
|
CHAMPION_OF_SAME_FACTION = 'champion_of_same_faction',
|
||||||
|
OTHER_KNIFE_PLAYED = 'other_knife_played',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EffectType {
|
export enum EffectType {
|
||||||
@@ -85,6 +132,7 @@ export type EffectParser = {
|
|||||||
effect_type?: EffectType | EffectTypeVerbose;
|
effect_type?: EffectType | EffectTypeVerbose;
|
||||||
per?: Per;
|
per?: Per;
|
||||||
mutex?: number;
|
mutex?: number;
|
||||||
|
faction?: CardFaction;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CardParser = {
|
export type CardParser = {
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import mongoose, { Types } from 'mongoose';
|
||||||
|
import {
|
||||||
|
CardEffects,
|
||||||
|
CardFaction,
|
||||||
|
EffectType,
|
||||||
|
EffectTypeSpecial,
|
||||||
|
Per,
|
||||||
|
} from './cards.types';
|
||||||
|
import { Card } from './cards.schema';
|
||||||
|
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||||
|
|
||||||
|
// @Schema()
|
||||||
|
export class EffectCondition {
|
||||||
|
@Prop({ type: String, enum: { ...EffectType, ...EffectTypeSpecial } })
|
||||||
|
effectId: EffectType | EffectTypeSpecial;
|
||||||
|
|
||||||
|
@Prop({ type: Number })
|
||||||
|
amount: number;
|
||||||
|
|
||||||
|
@Prop({ type: Number })
|
||||||
|
cardId?: number;
|
||||||
|
|
||||||
|
@Prop({ type: String, enum: CardFaction })
|
||||||
|
faction?: CardFaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
// export const EffectConditionSchema =
|
||||||
|
// SchemaFactory.createForClass(EffectCondition);
|
||||||
|
|
||||||
|
@Schema()
|
||||||
|
export class Effect extends Types.ObjectId {
|
||||||
|
@Prop({ type: String, enum: CardEffects })
|
||||||
|
effect: CardEffects;
|
||||||
|
|
||||||
|
@Prop({ type: Number, default: 1 })
|
||||||
|
amount!: number;
|
||||||
|
|
||||||
|
@Prop({ type: Number, default: 1 })
|
||||||
|
times!: number;
|
||||||
|
|
||||||
|
// @Prop({ type: [Effect], default: [], autopopulate: true })
|
||||||
|
subEffects: Effect[];
|
||||||
|
|
||||||
|
@Prop({ type: EffectCondition, autopopulate: true })
|
||||||
|
condition?: EffectCondition;
|
||||||
|
|
||||||
|
@Prop({ type: String, enum: Per })
|
||||||
|
per?: Per;
|
||||||
|
|
||||||
|
// @Prop({
|
||||||
|
// type: [
|
||||||
|
// {
|
||||||
|
// type: mongoose.Schema.Types.ObjectId,
|
||||||
|
// ref: 'Effect',
|
||||||
|
// autopopulate: true,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// })
|
||||||
|
mutex?: Effect[];
|
||||||
|
|
||||||
|
// @Prop({
|
||||||
|
// type: mongoose.Schema.Types.ObjectId,
|
||||||
|
// ref: 'Card',
|
||||||
|
// autopopulate: true,
|
||||||
|
// })
|
||||||
|
card: Card;
|
||||||
|
|
||||||
|
@Prop({ type: String, enum: CardFaction })
|
||||||
|
faction?: CardFaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EffectSchema = SchemaFactory.createForClass(Effect);
|
||||||
|
EffectSchema.add({
|
||||||
|
card: {
|
||||||
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
|
ref: 'Card',
|
||||||
|
autopopulate: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
EffectSchema.add({
|
||||||
|
mutex: {
|
||||||
|
type: [
|
||||||
|
{
|
||||||
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
|
ref: 'Effect',
|
||||||
|
autopopulate: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { GamesController } from './games.controller';
|
import { GamesController } from './games.controller';
|
||||||
import { GamesService } from './games.service';
|
import { GamesService } from '../services/games.lobby.service';
|
||||||
|
|
||||||
describe('GamesController', () => {
|
describe('GamesController', () => {
|
||||||
let controller: GamesController;
|
let controller: GamesController;
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Param,
|
||||||
|
Delete,
|
||||||
|
UseGuards,
|
||||||
|
Session,
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
|
Patch,
|
||||||
|
Body,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { GamesService } from '../services/games.lobby.service';
|
||||||
|
import { AuthGuard } from '../guards/auth.guard';
|
||||||
|
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
|
import { ReadGameDto } from '../dto/read-games.dto';
|
||||||
|
import { GameGuard } from '../guards/game.guard';
|
||||||
|
import { UpdateGameDto } from '../dto/update-game.dto';
|
||||||
|
|
||||||
|
@ApiTags('lobby')
|
||||||
|
@Controller('games')
|
||||||
|
export class GamesController {
|
||||||
|
constructor(private readonly gamesService: GamesService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: 'Create game' })
|
||||||
|
@ApiResponse({
|
||||||
|
status: 201,
|
||||||
|
description: 'Game successfully created.',
|
||||||
|
type: ReadGameDto,
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 400,
|
||||||
|
description: 'You can only create one game at a time',
|
||||||
|
})
|
||||||
|
@ApiResponse({ status: 403, description: 'Forbidden.' })
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
async create(@Session() session: Record<string, any>) {
|
||||||
|
const games = await this.gamesService.findByOwner(session.user._id);
|
||||||
|
if (games) {
|
||||||
|
throw new HttpException(
|
||||||
|
'You can only create one game at a time',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this.gamesService.create(session.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@ApiOperation({ summary: 'List all games' })
|
||||||
|
findAll() {
|
||||||
|
const games = this.gamesService.findAll();
|
||||||
|
return games;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@ApiOperation({ summary: 'Get game information' })
|
||||||
|
@UseGuards(GameGuard)
|
||||||
|
async findOne(@Param('id') id: string) {
|
||||||
|
const game = await this.gamesService.findById(id);
|
||||||
|
return game;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
@ApiOperation({ summary: 'Edit game settings' })
|
||||||
|
@UseGuards(AuthGuard, GameGuard)
|
||||||
|
async update(
|
||||||
|
@Session() session: Record<string, any>,
|
||||||
|
@Param('id') id: string,
|
||||||
|
@Body() updateGameDto: UpdateGameDto,
|
||||||
|
) {
|
||||||
|
const game = await this.gamesService.findById(id);
|
||||||
|
if (game.owner._id.toString() != session.user._id) {
|
||||||
|
throw new HttpException(
|
||||||
|
'You can only edit games you own',
|
||||||
|
HttpStatus.FORBIDDEN,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this.gamesService.update(id, updateGameDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
@ApiOperation({ summary: 'Delete game' })
|
||||||
|
@UseGuards(AuthGuard, GameGuard)
|
||||||
|
async remove(
|
||||||
|
@Session() session: Record<string, any>,
|
||||||
|
@Param('id') id: string,
|
||||||
|
) {
|
||||||
|
const game = await this.gamesService.findById(id);
|
||||||
|
if (game.owner._id.toString() != session.user._id) {
|
||||||
|
throw new HttpException(
|
||||||
|
'You can only delete games you own',
|
||||||
|
HttpStatus.FORBIDDEN,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const deleted = await this.gamesService.remove(id);
|
||||||
|
if (deleted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post(':id/start')
|
||||||
|
@ApiOperation({ summary: 'Start game' })
|
||||||
|
@ApiResponse({
|
||||||
|
status: 201,
|
||||||
|
description: 'Game successfully started.',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 400,
|
||||||
|
description: 'Bad Request',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 403,
|
||||||
|
description: 'You can only start games you own',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard, GameGuard)
|
||||||
|
async start(
|
||||||
|
@Session() session: Record<string, any>,
|
||||||
|
@Param('id') id: string,
|
||||||
|
) {
|
||||||
|
const game = await this.gamesService.findById(id);
|
||||||
|
if (game.started) {
|
||||||
|
throw new HttpException('Game already started', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
if (game.teams.length < 2 && !process.env.DEV_MODE) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Not enough teams to start the game',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (game.owner._id.toString() != session.user._id) {
|
||||||
|
throw new HttpException(
|
||||||
|
'You can only start games you own',
|
||||||
|
HttpStatus.FORBIDDEN,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.gamesService.start(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,57 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { DatabaseObjectDto } from './database-object.dto';
|
import { DatabaseObjectDto } from './database-object.dto';
|
||||||
import { Container, Team } from '../schemas/game.schema';
|
import { Game, Player, Team } from '../schemas/game.schema';
|
||||||
import { User } from 'src/users/schemas/user.entity';
|
import { Card } from 'src/cards/schemas/cards.schema';
|
||||||
|
|
||||||
|
export class ReadPlayerDto extends DatabaseObjectDto {
|
||||||
|
constructor(
|
||||||
|
data: Player & ConstructorParameters<typeof DatabaseObjectDto>[0],
|
||||||
|
) {
|
||||||
|
super(data);
|
||||||
|
this.user = new ReadUserDto(data.user);
|
||||||
|
this.board = new ReadContainerDto(data.board);
|
||||||
|
this.discard = new ReadContainerDto(data.discard);
|
||||||
|
this.hand = new ReadContainerDto(data.hand, true);
|
||||||
|
this.stack = new ReadContainerDto(data.stack, true);
|
||||||
|
}
|
||||||
|
user: ReadUserDto;
|
||||||
|
|
||||||
|
board: ReadContainerDto;
|
||||||
|
|
||||||
|
hand: ReadContainerDto;
|
||||||
|
|
||||||
|
stack: ReadContainerDto;
|
||||||
|
|
||||||
|
discard: ReadContainerDto;
|
||||||
|
}
|
||||||
|
|
||||||
export class ReadTeamDto extends DatabaseObjectDto {
|
export class ReadTeamDto extends DatabaseObjectDto {
|
||||||
constructor(data: ConstructorParameters<typeof DatabaseObjectDto>[0]) {
|
constructor(data: Team & ConstructorParameters<typeof DatabaseObjectDto>[0]) {
|
||||||
super(data);
|
super(data);
|
||||||
|
this.health = data.health;
|
||||||
|
this.players = data.players.map((e) => {
|
||||||
|
return new ReadPlayerDto(e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
health: number;
|
||||||
|
players: ReadPlayerDto[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ReadContainerDto extends DatabaseObjectDto {
|
export class ReadContainerDto extends DatabaseObjectDto {
|
||||||
constructor(data: ConstructorParameters<typeof DatabaseObjectDto>[0]) {
|
constructor(
|
||||||
|
data: { cards: Card[] } & ConstructorParameters<
|
||||||
|
typeof DatabaseObjectDto
|
||||||
|
>[0],
|
||||||
|
hidden = false,
|
||||||
|
) {
|
||||||
super(data);
|
super(data);
|
||||||
|
if (hidden) {
|
||||||
|
this.cards = data.cards.map(() => null);
|
||||||
|
} else {
|
||||||
|
this.cards = data.cards.map((e) => e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
cards: Card[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ReadUserDto extends DatabaseObjectDto {
|
export class ReadUserDto extends DatabaseObjectDto {
|
||||||
@@ -34,29 +73,20 @@ export class ReadUserDto extends DatabaseObjectDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ReadGameDto extends DatabaseObjectDto {
|
export class ReadGameDto extends DatabaseObjectDto {
|
||||||
constructor(
|
constructor(data: Game & ConstructorParameters<typeof DatabaseObjectDto>[0]) {
|
||||||
data: {
|
|
||||||
teams: Team[];
|
|
||||||
market: Container;
|
|
||||||
marketStack: Container;
|
|
||||||
fireGems: Container;
|
|
||||||
currentTurn?: Team;
|
|
||||||
started: boolean;
|
|
||||||
ended: boolean;
|
|
||||||
owner: User;
|
|
||||||
} & ConstructorParameters<typeof DatabaseObjectDto>[0],
|
|
||||||
) {
|
|
||||||
super(data);
|
super(data);
|
||||||
this.teams = data.teams.map((e) => new ReadTeamDto(e));
|
const obj = data;
|
||||||
this.market = new ReadContainerDto(data.market);
|
this.teams = obj.teams.map((e) => new ReadTeamDto(e));
|
||||||
this.marketStack = new ReadContainerDto(data.marketStack);
|
this.market = new ReadContainerDto(obj.market);
|
||||||
this.fireGems = new ReadContainerDto(data.fireGems);
|
this.marketStack = new ReadContainerDto(obj.marketStack, true);
|
||||||
this.started = data.started;
|
this.fireGems = new ReadContainerDto(obj.fireGems);
|
||||||
this.ended = data.ended;
|
this.started = obj.started;
|
||||||
this.owner = new ReadUserDto(data.owner);
|
this.ended = obj.ended;
|
||||||
|
this.owner = new ReadUserDto(obj.owner);
|
||||||
|
this.packs = obj.packs;
|
||||||
|
|
||||||
if (data.currentTurn) {
|
if (obj.currentTurn) {
|
||||||
this.currentTurn = new ReadTeamDto(data.currentTurn);
|
this.currentTurn = new ReadTeamDto(obj.currentTurn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,4 +113,7 @@ export class ReadGameDto extends DatabaseObjectDto {
|
|||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
owner: ReadUserDto;
|
owner: ReadUserDto;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
packs: string[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
export class UpdateGameDto {}
|
export class UpdateGameDto {
|
||||||
|
packs: string[];
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
import {
|
|
||||||
Controller,
|
|
||||||
Get,
|
|
||||||
Post,
|
|
||||||
Param,
|
|
||||||
Delete,
|
|
||||||
UseGuards,
|
|
||||||
Session,
|
|
||||||
HttpException,
|
|
||||||
HttpStatus,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { GamesService } from './games.service';
|
|
||||||
import { AuthGuard } from './guards/auth.guard';
|
|
||||||
import { ApiOperation, ApiResponse } from '@nestjs/swagger';
|
|
||||||
import { DatabaseObjectDto } from './dto/database-object.dto';
|
|
||||||
|
|
||||||
@Controller('games')
|
|
||||||
export class GamesController {
|
|
||||||
constructor(private readonly gamesService: GamesService) {}
|
|
||||||
|
|
||||||
@Post()
|
|
||||||
@ApiOperation({ summary: 'Create game' })
|
|
||||||
@ApiResponse({
|
|
||||||
status: 201,
|
|
||||||
description: 'Game successfully created.',
|
|
||||||
type: DatabaseObjectDto,
|
|
||||||
})
|
|
||||||
@ApiResponse({
|
|
||||||
status: 400,
|
|
||||||
description: 'You can only create one game at a time',
|
|
||||||
})
|
|
||||||
@ApiResponse({ status: 403, description: 'Forbidden.' })
|
|
||||||
@UseGuards(AuthGuard)
|
|
||||||
async create(@Session() session: Record<string, any>) {
|
|
||||||
const games = await this.gamesService.findByOwner(session.user._id);
|
|
||||||
if (games.length > 0) {
|
|
||||||
throw new HttpException(
|
|
||||||
'You can only create one game at a time',
|
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return this.gamesService.create(session.user);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get()
|
|
||||||
@ApiOperation({ summary: 'List all games' })
|
|
||||||
findAll() {
|
|
||||||
const games = this.gamesService.findAll();
|
|
||||||
return games;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get(':id')
|
|
||||||
findOne(@Param('id') id: string) {
|
|
||||||
return this.gamesService.findById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Patch(':id')
|
|
||||||
// update(@Param('id') id: string, @Body() updateGameDto: UpdateGameDto) {
|
|
||||||
// return this.gamesService.update(+id, updateGameDto);
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Delete(':id')
|
|
||||||
remove(@Param('id') id: string) {
|
|
||||||
return this.gamesService.remove(+id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { GamesService } from './games.service';
|
import { GamesService } from './services/games.lobby.service';
|
||||||
import { GamesController } from './games.controller';
|
import { GamesController } from './controllers/games.controller';
|
||||||
import { Game, GameSchema } from './schemas/game.schema';
|
import { Game, GameSchema } from './schemas/game.schema';
|
||||||
import { MongooseModule } from '@nestjs/mongoose';
|
import { MongooseModule } from '@nestjs/mongoose';
|
||||||
import { UsersModule } from 'src/users/users.module';
|
import { UsersModule } from 'src/users/users.module';
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { DatabaseObjectDto } from './dto/database-object.dto';
|
|
||||||
import { InjectModel } from '@nestjs/mongoose';
|
|
||||||
import { Game } from './schemas/game.schema';
|
|
||||||
import { Model } from 'mongoose';
|
|
||||||
import { ReadGameDto } from './dto/read-games.dto';
|
|
||||||
import { User } from 'src/users/schemas/user.entity';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class GamesService {
|
|
||||||
constructor(@InjectModel(Game.name) private gameModel: Model<Game>) {}
|
|
||||||
|
|
||||||
async create(owner: User): Promise<DatabaseObjectDto> {
|
|
||||||
const createdGame = new this.gameModel();
|
|
||||||
createdGame.owner = owner;
|
|
||||||
const gamedata = await createdGame.save();
|
|
||||||
return new DatabaseObjectDto(gamedata);
|
|
||||||
}
|
|
||||||
|
|
||||||
async findAll(): Promise<ReadGameDto[]> {
|
|
||||||
const games = await this.gameModel.find().exec();
|
|
||||||
return games.map((e) => new ReadGameDto(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
findById(id: string) {
|
|
||||||
return this.gameModel.findById(id).exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
findByOwner(_id: string) {
|
|
||||||
return this.gameModel.find({ owner: _id }).exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
// update(id: number, updateGameDto: UpdateGameDto) {
|
|
||||||
// return `This action updates a #${id} game`;
|
|
||||||
// }
|
|
||||||
|
|
||||||
remove(id: number) {
|
|
||||||
return `This action removes a #${id} game`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import {
|
||||||
|
Injectable,
|
||||||
|
CanActivate,
|
||||||
|
ExecutionContext,
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Request } from 'express';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { GamesService } from '../services/games.lobby.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GameGuard implements CanActivate {
|
||||||
|
constructor(private readonly gamesService: GamesService) {}
|
||||||
|
|
||||||
|
canActivate(
|
||||||
|
context: ExecutionContext,
|
||||||
|
): boolean | Promise<boolean> | Observable<boolean> {
|
||||||
|
const http = context.switchToHttp();
|
||||||
|
const request = http.getRequest<Request>();
|
||||||
|
return (async () => {
|
||||||
|
const game = await this.gamesService.findById(request.params.id);
|
||||||
|
if (!game) {
|
||||||
|
throw new HttpException('Game not found', HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
if (!(request.session as Record<string, any>).user) {
|
||||||
|
throw new HttpException('Please log in', HttpStatus.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,34 +5,71 @@ import { User } from 'src/users/schemas/user.entity';
|
|||||||
|
|
||||||
export type GameDocument = HydratedDocument<Game>;
|
export type GameDocument = HydratedDocument<Game>;
|
||||||
|
|
||||||
@Schema()
|
|
||||||
export class Team extends Types.ObjectId {}
|
|
||||||
|
|
||||||
export const TeamSchema = SchemaFactory.createForClass(Team);
|
|
||||||
|
|
||||||
@Schema()
|
@Schema()
|
||||||
export class Container extends Types.ObjectId {
|
export class Container extends Types.ObjectId {
|
||||||
@Prop([{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }])
|
@Prop({
|
||||||
|
type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Card' }],
|
||||||
|
autopopulate: true,
|
||||||
|
})
|
||||||
cards: Card[];
|
cards: Card[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ContainerSchema = SchemaFactory.createForClass(Container);
|
export const ContainerSchema = SchemaFactory.createForClass(Container);
|
||||||
|
|
||||||
|
@Schema()
|
||||||
|
export class Player extends Types.ObjectId {
|
||||||
|
@Prop({
|
||||||
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
|
ref: 'User',
|
||||||
|
autopopulate: true,
|
||||||
|
})
|
||||||
|
user: User;
|
||||||
|
|
||||||
|
@Prop({ type: ContainerSchema, default: {}, autopopulate: true })
|
||||||
|
board!: Container;
|
||||||
|
|
||||||
|
@Prop({ type: ContainerSchema, default: {}, autopopulate: true })
|
||||||
|
hand!: Container;
|
||||||
|
|
||||||
|
@Prop({ type: ContainerSchema, default: {}, autopopulate: true })
|
||||||
|
stack!: Container;
|
||||||
|
|
||||||
|
@Prop({ type: ContainerSchema, default: {}, autopopulate: true })
|
||||||
|
discard!: Container;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PlayerSchema = SchemaFactory.createForClass(Player);
|
||||||
|
|
||||||
|
@Schema()
|
||||||
|
export class Team extends Types.ObjectId {
|
||||||
|
@Prop({ type: Number, default: 50 })
|
||||||
|
health: number;
|
||||||
|
|
||||||
|
@Prop({ type: [{ type: Player }] })
|
||||||
|
players: Player[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TeamSchema = SchemaFactory.createForClass(Team);
|
||||||
|
|
||||||
@Schema()
|
@Schema()
|
||||||
export class Game extends Types.ObjectId {
|
export class Game extends Types.ObjectId {
|
||||||
@Prop([Team])
|
@Prop({ type: [{ type: Team, autopopulate: true }] })
|
||||||
teams: Team[];
|
teams: Team[];
|
||||||
|
|
||||||
@Prop({ type: ContainerSchema, default: {} })
|
@Prop({ type: ContainerSchema, default: {}, autopopulate: true })
|
||||||
market: Container;
|
market: Container;
|
||||||
|
|
||||||
@Prop({ type: ContainerSchema, default: {} })
|
@Prop({ type: ContainerSchema, default: {}, autopopulate: true })
|
||||||
marketStack: Container;
|
marketStack: Container;
|
||||||
|
|
||||||
@Prop({ type: ContainerSchema, default: {} })
|
@Prop({ type: ContainerSchema, default: {}, autopopulate: true })
|
||||||
fireGems: Container;
|
fireGems: Container;
|
||||||
|
|
||||||
@Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'Game.teams' })
|
@Prop({
|
||||||
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
|
ref: 'Team',
|
||||||
|
autopopulate: true,
|
||||||
|
})
|
||||||
currentTurn?: Team;
|
currentTurn?: Team;
|
||||||
|
|
||||||
@Prop({ default: false })
|
@Prop({ default: false })
|
||||||
@@ -41,8 +78,15 @@ export class Game extends Types.ObjectId {
|
|||||||
@Prop({ default: false })
|
@Prop({ default: false })
|
||||||
ended: boolean;
|
ended: boolean;
|
||||||
|
|
||||||
@Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User' })
|
@Prop({
|
||||||
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
|
ref: 'User',
|
||||||
|
autopopulate: true,
|
||||||
|
})
|
||||||
owner: User;
|
owner: User;
|
||||||
|
|
||||||
|
@Prop({ type: [String], default: [] })
|
||||||
|
packs: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GameSchema = SchemaFactory.createForClass(Game);
|
export const GameSchema = SchemaFactory.createForClass(Game);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { GamesService } from './games.service';
|
import { GamesService } from './games.lobby.service';
|
||||||
|
|
||||||
describe('GamesService', () => {
|
describe('GamesService', () => {
|
||||||
let service: GamesService;
|
let service: GamesService;
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { DatabaseObjectDto } from '../dto/database-object.dto';
|
||||||
|
import { InjectModel } from '@nestjs/mongoose';
|
||||||
|
import { Game, Player, Team } from '../schemas/game.schema';
|
||||||
|
import { Model } from 'mongoose';
|
||||||
|
import { ReadGameDto } from '../dto/read-games.dto';
|
||||||
|
import { User } from 'src/users/schemas/user.entity';
|
||||||
|
import { Card } from 'src/cards/schemas/cards.schema';
|
||||||
|
import { CardRole } from 'src/cards/schemas/cards.types';
|
||||||
|
import { UpdateGameDto } from '../dto/update-game.dto';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GamesService {
|
||||||
|
constructor(
|
||||||
|
@InjectModel(Game.name) private gameModel: Model<Game>,
|
||||||
|
@InjectModel(Card.name) private cardModel: Model<Card>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async create(owner: User): Promise<DatabaseObjectDto> {
|
||||||
|
const createdGame = new this.gameModel();
|
||||||
|
createdGame.owner = owner;
|
||||||
|
createdGame.packs = [
|
||||||
|
'pack1_base_deck',
|
||||||
|
'pack1_base_necros',
|
||||||
|
'pack1_base_wild',
|
||||||
|
'pack1_base_imperial',
|
||||||
|
'pack1_base_guild',
|
||||||
|
];
|
||||||
|
createdGame.teams.push({
|
||||||
|
players: [{ user: owner } as Player],
|
||||||
|
} as Team);
|
||||||
|
|
||||||
|
const gamedata = await createdGame.save();
|
||||||
|
return new ReadGameDto(gamedata);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll(): Promise<ReadGameDto[]> {
|
||||||
|
const games = await this.gameModel.find().exec();
|
||||||
|
return await Promise.all(
|
||||||
|
games.map(async (e) => {
|
||||||
|
return await new ReadGameDto(e);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findById(id: string) {
|
||||||
|
const game = await this.gameModel.findById(id).exec();
|
||||||
|
if (!game) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ReadGameDto(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByOwner(_id: string): Promise<ReadGameDto> {
|
||||||
|
const game = await this.gameModel.findOne({ owner: _id }).exec();
|
||||||
|
if (!game) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ReadGameDto(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, updateGameDto: UpdateGameDto) {
|
||||||
|
const game = await this.gameModel.findById(id).exec();
|
||||||
|
if (!game) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
game.packs = updateGameDto.packs;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string) {
|
||||||
|
return await this.gameModel.findByIdAndDelete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(id: string) {
|
||||||
|
const game = await this.gameModel.findById(id).exec();
|
||||||
|
if (!game) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
game.fireGems.cards = await this.cardModel.find({
|
||||||
|
pack: { $in: game.packs },
|
||||||
|
role: CardRole.FIRE_GEM,
|
||||||
|
});
|
||||||
|
|
||||||
|
game.marketStack.cards = await this.cardModel.find({
|
||||||
|
pack: { $in: game.packs },
|
||||||
|
role: CardRole.MARKET,
|
||||||
|
});
|
||||||
|
|
||||||
|
let playerIndex = 0;
|
||||||
|
await Promise.all(
|
||||||
|
game.teams.map((team) =>
|
||||||
|
Promise.all(
|
||||||
|
team.players.map(async (player) => {
|
||||||
|
player.stack.cards = await this.cardModel
|
||||||
|
.find({
|
||||||
|
pack: { $in: game.packs },
|
||||||
|
role: CardRole.PERSONAL,
|
||||||
|
playerIndex,
|
||||||
|
})
|
||||||
|
.exec();
|
||||||
|
playerIndex++;
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
game.started = true;
|
||||||
|
await game.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user