diff --git a/apps/shelter-client/src/components/ActivityLogs.tsx b/apps/shelter-client/src/components/ActivityLogs.tsx index 5041bce..88fef46 100644 --- a/apps/shelter-client/src/components/ActivityLogs.tsx +++ b/apps/shelter-client/src/components/ActivityLogs.tsx @@ -1,5 +1,5 @@ import '../styles/ActivityLogs.scss'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { RootState } from '../redux/store'; import { getActivityLogsByLobbyId } from '../api/requests'; @@ -14,6 +14,7 @@ const ActivityLogs = () => { const user = useSelector((state: RootState) => state.user); const app = useSelector((state: RootState) => state.app); const lobby = useSelector((state: RootState) => state.lobby); + const activityLogsRef = useRef(null); // LOCAL STATE const updateState = (newState: Partial): void => @@ -22,6 +23,10 @@ const ActivityLogs = () => { activityLogs: [], }); + if (activityLogsRef.current) { + activityLogsRef.current.scrollTop = activityLogsRef.current.scrollHeight; + } + useEffect(() => { handleGetActivityLogs(); }, []); @@ -39,7 +44,7 @@ const ActivityLogs = () => {

Activity Logs

-
+
{state.activityLogs.map((data: { payload: any; createdAt: string }) => { return (
diff --git a/apps/shelter-client/src/components/Chat.tsx b/apps/shelter-client/src/components/Chat.tsx index 8faf0d7..4bac92f 100644 --- a/apps/shelter-client/src/components/Chat.tsx +++ b/apps/shelter-client/src/components/Chat.tsx @@ -158,6 +158,8 @@ const Chat: FC = () => {
= ({ }; }, [isOpened, onClose, type]); - const isTypeNotifications = type === 'notifications'; return ( -
+
{children} {isOpened && (
{(type === 'account' || type === 'login') && ( -
{text}
+
{text}
)} {type === 'notifications' && ( -
{text}
+
{text}
)} {list.map((item, index) => ( -
+
- {state.isDetailsOpened ? ( + {state.isDetailsOpened && (
{charList.map((char: charType, index: any) => { @@ -389,14 +403,22 @@ const RoomPage = () => {
); })}
- ) : null} + )}
); })} @@ -408,11 +430,17 @@ const RoomPage = () => { const handleGameStart = () => { sm.emit({ event: ClientEvents.GameStart, + data: {}, + }); + }; + const handleEndTurn = () => { + sm.emit({ + event: ClientEvents.GameEndTurn, data: { - maxClients: state.maxClients, - isPrivate: state.isPrivateLobby, + userId: user.userId, }, }); + updateState({ uRemainedChars: 2 }); }; return ( @@ -420,7 +448,20 @@ const RoomPage = () => { {!state.kickedPlayers.includes(user.userId) || !lobby.hasFinished ? state.actionTip : 'You are kicked!'} - {!lobby.hasStarted || lobby.hasFinished ? ( + {!state.kickedPlayers.includes(user.userId) && + state.uRemainedChars === 0 && + lobby.currentStage! % 2 === 1 && + !lobby.hasFinished && ( +
+
+
+ +
+
+ )} + {(!lobby.hasStarted || lobby.hasFinished) && (
@@ -433,7 +474,7 @@ const RoomPage = () => { )}
- ) : null} + )}
); }; @@ -536,6 +577,42 @@ const RoomPage = () => { />
+
+
+

Turn on the timer

+

+ Players should end thair turns before the time limit to + avoid random :D +

+
+
+ +
+

Maximum players

@@ -545,11 +622,15 @@ const RoomPage = () => { > { - const lobby = this.lobbyManager.createLobby(data.maxClients); + const lobby = this.lobbyManager.createLobby( + data.maxClients, + this.databaseService, + this.activityLogsService, + ); // data.player.socketId = client.id // Cannot set properties of undefined (setting 'socketId') lobby.addClient(client); @@ -80,7 +84,7 @@ export class GameGateway const context = { key: lobby.id, organizatorId: data.organizatorId, - settings: { maxClients: data.maxClients, isPrivate: true }, + settings: { maxClients: data.maxClients, isPrivate: true, timer: 0 }, }; await this.databaseService.createLobby(context); @@ -95,19 +99,23 @@ export class GameGateway @SubscribeMessage(ClientEvents.LobbyUpdate) async onLobbyUpdate(client: AuthenticatedSocket, data: any): Promise { - let isPrivate, maxClients; + let isPrivate, maxClients, timer; if (data.isPrivate !== null || data.isPrivate !== undefined) { - client.data.lobby.instance.isPrivate = data.isPrivate; + client.data.lobby.isPrivate = data.isPrivate; isPrivate = data.isPrivate; } if (data.maxClients !== null || data.maxClients !== undefined) { - client.data.lobby.instance.maxClients = data.maxClients; + client.data.lobby.maxClients = data.maxClients; maxClients = data.maxClients; } + if (data.maxClients !== null || data.maxClients !== undefined) { + client.data.lobby.timer = data.timer; + timer = data.timer; + } - // store lobby in database + // update lobby in database await this.databaseService.updateLobbyByKey(data.key, { - settings: { isPrivate, maxClients }, + settings: { isPrivate, maxClients, timer }, }); return { @@ -144,14 +152,6 @@ export class GameGateway } client.data.lobby.instance.voteKick(data, client); - - // create activity log - await this.activityLogsService.createActivityLog({ - userId: data.userId, - lobbyId: client.data.lobby.id, - action: constants.voteKick, - payload: data, - }); } @SubscribeMessage(ClientEvents.GameUseSpecialCard) @@ -167,14 +167,6 @@ export class GameGateway } client.data.lobby.instance.useSpecialCard(data, client); - - // create activity log - await this.activityLogsService.createActivityLog({ - userId: data.userId, - lobbyId: client.data.lobby.id, - action: constants.useSpecialCard, - payload: data, - }); } @SubscribeMessage(ClientEvents.GameRevealChar) @@ -187,13 +179,10 @@ export class GameGateway } client.data.lobby.instance.revealChar(data, client); + } - // create activity log - await this.activityLogsService.createActivityLog({ - userId: data.userId, - lobbyId: client.data.lobby.id, - action: constants.revealChar, - payload: data, - }); + @SubscribeMessage(ClientEvents.GameEndTurn) + onEndTurn(client: AuthenticatedSocket, data: any): void { + client.data.lobby.instance.endTurn(data, client); } } diff --git a/apps/shelter-gateway/src/game/game.module.ts b/apps/shelter-gateway/src/game/game.module.ts index 0dcae96..a4e584b 100644 --- a/apps/shelter-gateway/src/game/game.module.ts +++ b/apps/shelter-gateway/src/game/game.module.ts @@ -1,10 +1,15 @@ import { Module } from '@nestjs/common'; // import { GameGateway } from './game.gateway'; import { LobbyManager } from './lobby/lobby.manager'; +import { DatabaseModule } from '@app/common'; +import { ActivityLogsModule } from '../activityLogs/activity-logs.module'; @Module({ + imports: [ActivityLogsModule], providers: [ // GameGateway, + DatabaseModule, + ActivityLogsModule, LobbyManager, ], }) diff --git a/apps/shelter-gateway/src/game/instance/instance.ts b/apps/shelter-gateway/src/game/instance/instance.ts index 3489e5d..d0342d3 100644 --- a/apps/shelter-gateway/src/game/instance/instance.ts +++ b/apps/shelter-gateway/src/game/instance/instance.ts @@ -1,16 +1,18 @@ /* eslint-disable prettier/prettier */ -import { Lobby } from '../lobby/lobby'; -import { AuthenticatedSocket } from '../types'; -import { ServerPayloads } from '../utils/ServerPayloads'; -import { ServerEvents } from '../utils/ServerEvents'; -import { constants } from '@app/common'; import { generateFromCharacteristics, getRandomIndex, countOccurrences, getKeysWithHighestValue, + isset } from 'helpers'; +import { Lobby } from '../lobby/lobby'; +import { AuthenticatedSocket } from '../types'; +import { ServerPayloads } from '../utils/ServerPayloads'; +import { ServerEvents } from '../utils/ServerEvents'; +import { constants } from '@app/common'; + export class Instance { public hasStarted: boolean = false; @@ -30,20 +32,29 @@ export class Instance { private charsRevealedCount: number = 0; private readonly charOpenLimit: number = 2; // per 1 player on every stage - constructor(private readonly lobby: Lobby) { } + constructor( + private readonly lobby: Lobby, + ) { } - public async triggerStart( - data: { isPrivate: boolean; maxClients: number; organizatorId: string }, - client: AuthenticatedSocket, - ): Promise { + public async triggerStart(data: any, client: AuthenticatedSocket): Promise { if (this.hasStarted) { return; } + if (this.lobby.clients.size < 2) { + return this.lobby.dispatchToLobby( + ServerEvents.GameMessage, + { + color: 'blue', + message: 'Find a friend to play :D', + }, + ); + } // update lobby's settings - this.lobby.isPrivate = data.isPrivate; - // TODO: this.lobby.maxClients = data.maxClients; - // TODO: this.lobby.isTimerOn = data.isTimerOn; + const lobbydb = await this.lobby.databaseService.getLobbyByKeyOrNull(client.data.lobby.id); + this.lobby.isPrivate = lobbydb.settings.isPrivate; + this.lobby.maxClients = lobbydb.settings.maxClients; + this.lobby.timer = lobbydb.settings.timer; // set random characteristics this.hasStarted = true; @@ -103,9 +114,9 @@ export class Instance { ); } - public revealChar(data: any, client: AuthenticatedSocket): void { + public async revealChar(data: any, client: AuthenticatedSocket): Promise { const { char, userId } = data; - if (!this.hasStarted) { + if (!this.hasStarted || this.hasFinished) { return; } if (this.revealPlayerId !== userId) { @@ -117,17 +128,20 @@ export class Instance { return; } // open chars only on reveal stages - if (this.currentStage % 2 === 0) { + if (this.currentStage % 2 === 0 || !isset(this.currentStage)) { return; } const uCharList = this.characteristics[userId]; + const uCharsRevealed = uCharList.filter((char: { isRevealed: boolean }) => char.isRevealed === true); + + // check if user not reveales one char multiple times + if (uCharsRevealed.map(c => c.text).includes(char.text)) { + return; + } // check if user not reveales more chars then limited - let uCharsRevealed: number = uCharList.filter( - (char: { isRevealed: boolean }) => char.isRevealed === true, - ).length; - if (uCharsRevealed >= Math.ceil(this.currentStage / 2) * this.charOpenLimit) { + if (uCharsRevealed.length >= Math.ceil(this.currentStage / 2) * this.charOpenLimit) { return; } @@ -137,42 +151,72 @@ export class Instance { ).isRevealed = true; this.characteristics[userId] = uCharList; this.charsRevealedCount = this.charsRevealedCount + 1; - uCharsRevealed = uCharsRevealed + 1 - /* check if user revealed all possible characteristics and - choose next player that can reveal chars */ - if (uCharsRevealed === Math.ceil(this.currentStage / 2) * this.charOpenLimit) { - const chooseNextToReveal = (revealPlayerId, attempt = 0) => { - const totalPlayers = this.players.length; - if (attempt >= totalPlayers) return null; // Base case to prevent infinite recursion + // create activity log + await this.lobby.activityLogsService.createActivityLog({ + userId: data.userId, + lobbyId: client.data.lobby.id, + action: constants.revealChar, + payload: data, + }); - const currentIndex = this.players.findIndex(p => p.userId === revealPlayerId); - const nextIndex = (currentIndex + 1) % totalPlayers; // When reaching the end of the player list, the search wraps around to the beginning - const revealPlayer = this.players[nextIndex]; + this.lobby.dispatchLobbyState(); + this.lobby.dispatchToLobby( + ServerEvents.GameMessage, + { + color: 'blue', + message: 'Characteristic is opened!', + }, + ); + } - if (revealPlayer.isKicked) { - // If the next player is kicked, recursively search for the next - return chooseNextToReveal(revealPlayer.userId, attempt + 1); - } - return revealPlayer.userId; - }; + /** + * Logic: By default player has endTurn=false. + * Player revealed all remaining characteristics: endTurn=true. + * If all players endTurn=true -> transit to the next stage + * and update endTurn=false for all. + * + * @param data + * @param client + * @returns Promise + */ + public async endTurn(data: any, client: AuthenticatedSocket): Promise { + const { userId } = data; - this.revealPlayerId = chooseNextToReveal(this.revealPlayerId); + if (!this.hasStarted || this.hasFinished) { + return; + } + // kicked player can not end turn + const isKicked = this.players.find(player => player.userId === userId).isKicked === true; + if (isKicked) { + return; + } + // end turn only on reveal stages + if (this.currentStage % 2 === 0 || !isset(this.currentStage)) { + return; + } + // only reveal player can end turn + if (this.revealPlayerId !== userId) { + return; } - // transit to the next stage - const allRevealsOnCurrentStage = - this.charsRevealedCount >= this.charOpenLimit * (this.players.filter(_ => _.isKicked !== true).length); - - if (allRevealsOnCurrentStage) { - this.transitNextStage() - this.lobby.dispatchToLobby( - ServerEvents.GameMessage, - { - color: 'blue', - message: `Stage ${this.currentStage} is started!`, - }, - ); + // update endTurn + this.players.find(player => player.userId === userId).endTurn = true; + + // choose next player + this.chooseNextToReveal(data, client) + + /* Check if all the players ended the turn and if all reveals on current stage. + Transit to the next stage, endTurn=false for all */ + const kicked = this.players.filter(player => player.isKicked); + const allEnded = this.players.filter(_ => _.endTurn).length === this.players.length - kicked.length; + const allRevealsOnCurrentStage = this.charsRevealedCount >= this.charOpenLimit * (this.players.filter(_ => _.isKicked !== true).length); + console.debug('[allRevealsOnCurrentStage]: ', allRevealsOnCurrentStage); + if (allEnded) { + this.transitNextStage(data, client) + this.players.forEach(player => { + player.endTurn = false; + }); } this.lobby.dispatchLobbyState(); @@ -180,7 +224,7 @@ export class Instance { ServerEvents.GameMessage, { color: 'blue', - message: 'Characteristic is opened!', + message: 'Player has finished his turn!', }, ); } @@ -188,6 +232,10 @@ export class Instance { public async voteKick(data: any, client: AuthenticatedSocket): Promise { const { userId, contestantId } = data; + if (!this.hasStarted || this.hasFinished) { + return; + } + // kicked player can not vote const isKicked = this.players.find(player => player.userId === userId).isKicked === true; if (isKicked) { @@ -195,7 +243,7 @@ export class Instance { } // vote only on kick stages - if (this.currentStage % 2 === 1) { + if (this.currentStage % 2 === 1 || !isset(this.currentStage)) { return; } @@ -206,6 +254,14 @@ export class Instance { this.voteKickList = [...this.voteKickList, { userId, contestantId }] + // create activity log + await this.lobby.activityLogsService.createActivityLog({ + userId: data.userId, + lobbyId: client.data.lobby.id, + action: constants.voteKick, + payload: data, + }); + // calc votes and kick player if (this.voteKickList.length >= this.players.filter(_ => _.isKicked !== true).length) { const contestantIds = this.voteKickList.map((_: { contestantId: any; }) => _.contestantId); @@ -224,6 +280,20 @@ export class Instance { this.kickedPlayers = [...this.kickedPlayers, keysWithHighestValue[0]] kickedPlayer = this.players.find(player => player.userId === keysWithHighestValue[0]) } + + // choose next player if reveal one is kicked + if (this.revealPlayerId === kickedPlayer.userId) { + this.chooseNextToReveal(data, client) + } + + // create activity log + await this.lobby.activityLogsService.createActivityLog({ + userId: data.userId, + lobbyId: client.data.lobby.id, + action: constants.playerKicked, + payload: { userId: kickedPlayer.userId }, // kicked player id + }); + this.lobby.dispatchToLobby( ServerEvents.GameMessage, { @@ -232,13 +302,6 @@ export class Instance { }, ); - // create activity log - // await this.activityLogsService.createActivityLog({ - // userId: data.userId, - // lobbyId: client.data.lobby.id, - // action: constants.playerKicked, - // payload: data, - // }); this.charsRevealedCount = 0 // clear round char counter this.voteKickList = []; // clear the list after kick @@ -250,7 +313,7 @@ export class Instance { return; } - this.transitNextStage(); + this.transitNextStage(data, client); this.lobby.dispatchLobbyState(); return; } @@ -267,7 +330,7 @@ export class Instance { ); } - public useSpecialCard(data: any, client: AuthenticatedSocket): void { + public async useSpecialCard(data: any, client: AuthenticatedSocket): Promise { const { specialCard, userId, contestantId = null } = data; if (!this.hasStarted || this.hasFinished) { return; @@ -288,6 +351,14 @@ export class Instance { this.applyChanges(specialCard.id, userId, contestantId) this.specialCards[userId].find(card => card.type === specialCard.type).isUsed = true; + // create activity log + await this.lobby.activityLogsService.createActivityLog({ + userId: data.userId, + lobbyId: client.data.lobby.id, + action: constants.useSpecialCard, + payload: data, + }); + const user = this.players.find(player => player.userId === userId); this.lobby.dispatchLobbyState(); this.lobby.dispatchToLobby( @@ -303,7 +374,32 @@ export class Instance { this.lobby.dispatchToLobby(ServerEvents.ChatMessage, data); } - private transitNextStage(): void { + /* check if user revealed all possible characteristics and + choose next player that can reveal chars */ + private chooseNextToReveal(data: any, client: AuthenticatedSocket): void { + const uCharList = this.characteristics[data.userId]; + const uCharsRevealed = uCharList.filter((char: { isRevealed: boolean }) => char.isRevealed === true); + if (uCharsRevealed.length >= Math.ceil(this.currentStage / 2) * this.charOpenLimit) { + const chooseNextToReveal = (revealPlayerId, attempt = 0) => { + const totalPlayers = this.players.length; + if (attempt >= totalPlayers) return null; // Base case to prevent infinite recursion + + const currentIndex = this.players.findIndex(p => p.userId === revealPlayerId); + const revealPlayer = this.players[currentIndex + 1] || this.players[0]; + console.debug('[chooseNextToReveal] revealPlayer: ', revealPlayer); + + if (revealPlayer.isKicked) { + // If the next player is kicked, recursively search for the next + return chooseNextToReveal(revealPlayer.userId, attempt + 1); + } + return revealPlayer.userId; + }; + + this.revealPlayerId = chooseNextToReveal(this.revealPlayerId); + } + } + + private async transitNextStage(data: any, client: AuthenticatedSocket): Promise { this.currentStage = this.currentStage + 1; // deactivate stages @@ -320,13 +416,20 @@ export class Instance { } // create activity log - // await this.activityLogsService.createActivityLog({ - // userId: data.userId, - // lobbyId: client.data.lobby.id, - // action: constants.nextStageStarted, - // payload: data, - // }); + await this.lobby.activityLogsService.createActivityLog({ + userId: data.userId, + lobbyId: client.data.lobby.id, + action: constants.nextStageStarted, + payload: { currentStage: this.currentStage }, // only currentStage needed + }); + this.lobby.dispatchToLobby( + ServerEvents.GameMessage, + { + color: 'blue', + message: `Stage ${this.currentStage} is started!`, + }, + ); } /* applies changes on special card use */ diff --git a/apps/shelter-gateway/src/game/lobby/lobby.manager.ts b/apps/shelter-gateway/src/game/lobby/lobby.manager.ts index d177262..3ebf1df 100644 --- a/apps/shelter-gateway/src/game/lobby/lobby.manager.ts +++ b/apps/shelter-gateway/src/game/lobby/lobby.manager.ts @@ -7,14 +7,10 @@ import { ServerException } from '../server.exception'; import { SocketExceptions } from '../utils/SocketExceptions'; import { ServerEvents } from '../utils/ServerEvents'; import { ServerPayloads } from '../utils/ServerPayloads'; -import { DatabaseService } from '@app/common'; import { LobbiesService } from '../../lobbies/lobbies.service'; export class LobbyManager { - constructor( - private readonly databaseService: DatabaseService, - private readonly lobbiesService: LobbiesService, - ) {} + constructor(private readonly lobbiesService: LobbiesService) {} public server: Server; private readonly lobbies: Map = new Map< Lobby['id'], @@ -27,8 +23,17 @@ export class LobbyManager { client.data.lobby?.removeClient(client); } - public createLobby(maxClients: number): Lobby { - const lobby = new Lobby(this.server, maxClients); + public createLobby( + maxClients: number, + databaseService, + activityLogsService, + ): Lobby { + const lobby = new Lobby( + this.server, + maxClients, + databaseService, + activityLogsService, + ); this.lobbies.set(lobby.id, lobby); return lobby; } @@ -50,6 +55,14 @@ export class LobbyManager { ); } + // TODO: Game already started check here + // if (lobby.instance.hasStarted) { + // throw new ServerException( + // SocketExceptions.LobbyError, + // 'Game already started', + // ); + // } + playerData.socketId = client.id; lobby.addClient(client, playerData); diff --git a/apps/shelter-gateway/src/game/lobby/lobby.ts b/apps/shelter-gateway/src/game/lobby/lobby.ts index a0d9301..d727c68 100644 --- a/apps/shelter-gateway/src/game/lobby/lobby.ts +++ b/apps/shelter-gateway/src/game/lobby/lobby.ts @@ -4,6 +4,8 @@ import { Instance } from '../instance/instance'; import { ServerEvents } from '../utils/ServerEvents'; import { ServerPayloads } from '../utils/ServerPayloads'; import { generateSixSymbolHash } from 'helpers'; +import { DatabaseService } from '@app/common'; +import { ActivityLogsService } from '../../activityLogs/activity-logs.service'; export class Lobby { public readonly id: string = generateSixSymbolHash(); @@ -14,10 +16,15 @@ export class Lobby { >(); public readonly instance: Instance = new Instance(this); public isPrivate: boolean = true; + public timer: number = 0; + public readonly databaseService = this._databaseService; + public readonly activityLogsService = this._activityLogsService; constructor( private readonly server: Server, public maxClients: number, + private readonly _databaseService: DatabaseService, + private readonly _activityLogsService: ActivityLogsService, ) {} public addClient(client: AuthenticatedSocket, playerData: any = {}): void { @@ -50,8 +57,7 @@ export class Lobby { client.leave(this.id); client.data.lobby = null; - // If player leave then the game isn't worth to play anymore - this.instance.triggerFinish(); + // TODO: suspend the game // Alert the remaining player that client left lobby this.dispatchToLobby( @@ -68,7 +74,6 @@ export class Lobby { public dispatchLobbyState(): void { const payload: ServerPayloads[ServerEvents.LobbyState] = { lobbyId: this.id, - maxClients: this.maxClients, hasStarted: this.instance.hasStarted, hasFinished: this.instance.hasFinished, playersCount: this.clients.size, @@ -77,7 +82,9 @@ export class Lobby { characteristics: this.instance.characteristics, specialCards: this.instance.specialCards, conditions: this.instance.conditions, + maxClients: this.maxClients, isPrivate: this.isPrivate, + timer: this.timer, currentStage: this.instance.currentStage, stages: this.instance.stages, revealPlayerId: this.instance.revealPlayerId, diff --git a/apps/shelter-gateway/src/game/utils/ClientEvents.ts b/apps/shelter-gateway/src/game/utils/ClientEvents.ts index 83b94ef..e2c5acd 100644 --- a/apps/shelter-gateway/src/game/utils/ClientEvents.ts +++ b/apps/shelter-gateway/src/game/utils/ClientEvents.ts @@ -6,6 +6,7 @@ export enum ClientEvents { LobbyLeave = 'client.lobby.leave', GameStart = 'client.game.start', GameRevealChar = 'client.game.reveal_char', // characteristic, i.e: gender, health etc.. + GameEndTurn = 'client.game.end_turn', GameVoteKick = 'client.game.vote_kick', GameUseSpecialCard = 'client.game.use_special_card', } diff --git a/apps/shelter-gateway/src/game/utils/ServerPayloads.ts b/apps/shelter-gateway/src/game/utils/ServerPayloads.ts index 6cbb773..2391087 100644 --- a/apps/shelter-gateway/src/game/utils/ServerPayloads.ts +++ b/apps/shelter-gateway/src/game/utils/ServerPayloads.ts @@ -3,7 +3,6 @@ import { ServerEvents } from './ServerEvents'; export type ServerPayloads = { [ServerEvents.LobbyState]: { lobbyId: string; - maxClients: number; hasStarted: boolean; hasFinished: boolean; playersCount: number; @@ -12,7 +11,9 @@ export type ServerPayloads = { characteristics: any; specialCards: any; conditions: any; + maxClients: number; isPrivate: boolean; + timer: number; currentStage: number; stages: any[]; revealPlayerId: string; diff --git a/characteristics.json b/characteristics.json index 2a87345..dcc22e3 100644 --- a/characteristics.json +++ b/characteristics.json @@ -36,9 +36,10 @@ "Ржавий ніж в плечі", "сифіліс", "cпід", + "склероз", "тиф", "тремор", - "nуберкульоз", + "туберкульоз", "цинга" ], "hobby": [ @@ -56,6 +57,7 @@ "експериментувати з кулінарними рецептами", "збирати античні монети", "фотографувати", + "чорна магія", "розв'язувати складні головоломки", "вивчати астрономію", "вчитися новим мовам", @@ -81,6 +83,7 @@ "вермінофобія", "генофобія", "гімнофобія", + "Гінофобія: страх жінок", "кінофобія", "мегалофобія", "мусофобія", @@ -213,13 +216,14 @@ "ліцензований пілот легкомоторного літака", "володіє технікою швидкого читання", "експерт з історії мистецтва", - "є досвідом в автономних системах життєзабезпечення", + "розбирається в системах життєзабезпечення", "має досвід роботи в зоопарку", - "пройшов курси першої допомоги в екстремальних умовах", + "пройшов курси першої допомоги", "знавець рідкісних мов", "має досвід вирощування органічних продуктів", "майстер з виготовлення меблів вручну", - "колишній каскадер" + "колишній каскадер", + "хропить" ], "backpack": [ "аптечка", @@ -228,6 +232,7 @@ "блок пива", "блок туалетного паперу", "ліки від голови", + "генератор", "граната", "дитина", "дерев'яний кінь", @@ -235,6 +240,7 @@ "дефебрилятор", "книги Террі Пратчетта", "коргі", + "дитина", "набір теплого одягу", "набір для вишивання", "ніж", diff --git a/helpers.ts b/helpers.ts index f89108e..6149b4d 100644 --- a/helpers.ts +++ b/helpers.ts @@ -10,6 +10,14 @@ export const getRandomIndex = (maxIndex: number) => { return Math.floor(Math.random() * maxIndex); }; +export const getRandomIndexInRange = (minIndex: number, maxIndex: number) => { + return Math.floor(Math.random() * (maxIndex - minIndex) + minIndex); +}; + +export const isset = (val: any) => { + return val !== null && val !== undefined; +}; + export const generateFromCharacteristics = ( type: 'charList' | 'conditions' | 'specialCard', ): any => { @@ -23,8 +31,9 @@ export const generateFromCharacteristics = ( const _getRandomChar = (data: any, type: string) => { if (type === 'gender') { - const reandomIndex = Math.floor(Math.random() * 2); - return ['Чоловік', 'Жінка'][reandomIndex]; + const age = getRandomIndexInRange(18, 101); + const gender = ['Чоловік', 'Жінка'][getRandomIndex(2)]; + return `${gender}(Вік:${age})`; // example: Чоловік(Вік:101) } const charsByType = data[type]; const randomIndex = Math.floor(Math.random() * charsByType.length); @@ -74,6 +83,9 @@ export const generateFromCharacteristics = ( }, { type: 'health', + stage: ['тяжка форма', 'критична форма', 'легка форма', 'середняя форма'][ + getRandomIndex(4) + ], text: _getRandomChar(jsonData, 'health'), icon: 'healthIcon', isRevealed: false, diff --git a/libs/common/src/database/database.service.ts b/libs/common/src/database/database.service.ts index 893cac0..4274122 100644 --- a/libs/common/src/database/database.service.ts +++ b/libs/common/src/database/database.service.ts @@ -141,6 +141,10 @@ export class DatabaseService { data.settings.isPrivate !== undefined ? data.settings.isPrivate : lobby.settings.isPrivate, + timer: + data.settings.timer !== undefined + ? data.settings.timer + : lobby.settings.timer, }; // Update lobby settings with the merged object @@ -176,7 +180,7 @@ export class DatabaseService { return lobby; } - async getLobbyByKeyOrNull(key: string) { + async getLobbyByKeyOrNull(key: string): Promise { const lobby = await this.prisma.lobbies.findFirst({ where: { key: key }, });