667 lines
22 KiB
TypeScript
667 lines
22 KiB
TypeScript
import { Player, ObfuscatedPlayer } from "./player";
|
|
import { CardStack } from "./card";
|
|
import { Card, ObfuscatedCardStack } from "./card";
|
|
import { Socket } from "socket.io";
|
|
import { io } from "../server";
|
|
|
|
type PlayerSocketSet = Set<string>;
|
|
|
|
type PlayerActionEventName = string;
|
|
type PlayerActionCallback = (playerSocketId: string, data: any) => void;
|
|
|
|
type ExpectedPlayerActions = Array<
|
|
[PlayerActionEventName, PlayerActionCallback]
|
|
>;
|
|
|
|
type PlayerAction<ActionDataType> = {
|
|
playerSocketId: string;
|
|
data: ActionDataType;
|
|
};
|
|
|
|
type CardPosition = number;
|
|
|
|
// obfuscated types are used to send only necessary data to the client
|
|
export type ObfuscatedGame = {
|
|
sessionId: string;
|
|
playerCount: number;
|
|
players: ObfuscatedPlayer[];
|
|
cardStack: ObfuscatedCardStack;
|
|
discardPile: Card[];
|
|
phase: string;
|
|
round: number;
|
|
};
|
|
|
|
export const allGames: Game[] = [];
|
|
|
|
const gamePhase = {
|
|
newRound: "new round",
|
|
revealTwoCards: "reveal two cards",
|
|
pickUpCard: "pick up card",
|
|
placeCard: "place card",
|
|
revealCard: "reveal card",
|
|
revealedLastCard: "revealed last card",
|
|
gameEnded: "game ended",
|
|
};
|
|
|
|
export class Game {
|
|
socket: Socket;
|
|
sessionId: string;
|
|
playerCount: number;
|
|
players: Player[];
|
|
cardStack: CardStack;
|
|
discardPile: Card[];
|
|
phase: string;
|
|
round: number;
|
|
|
|
constructor(socket: Socket, sessionId: string, playerIds: PlayerSocketSet) {
|
|
this.socket = socket;
|
|
this.sessionId = sessionId;
|
|
|
|
this.cardStack = new CardStack();
|
|
this.cardStack.shuffleCards();
|
|
|
|
this.playerCount = playerIds.size;
|
|
this.players = this.initializePlayers(playerIds, this.cardStack);
|
|
|
|
// get the first card from the cardStack and put it in the discard pile
|
|
this.discardPile = [this.cardStack.cards.pop()!];
|
|
this.phase = gamePhase.revealTwoCards;
|
|
this.round = 1;
|
|
}
|
|
|
|
initializePlayers(
|
|
playerIds: PlayerSocketSet,
|
|
cardStack: CardStack
|
|
): Player[] {
|
|
let players: Player[] = [];
|
|
|
|
let index = 0;
|
|
playerIds.forEach((socketId) => {
|
|
index++;
|
|
const playerCards = cardStack.cards.splice(0, 12);
|
|
const player = new Player(
|
|
index,
|
|
socketId,
|
|
`Player ${index}`,
|
|
playerCards
|
|
);
|
|
players.push(player);
|
|
});
|
|
|
|
this.cardStack = cardStack;
|
|
|
|
return players;
|
|
}
|
|
|
|
initializeNewRound(startOver: boolean = false) {
|
|
this.round = startOver ? 1 : this.round + 1;
|
|
this.cardStack = new CardStack();
|
|
this.cardStack.shuffleCards();
|
|
this.players.forEach((player) => {
|
|
player.cards = this.cardStack.cards.splice(0, 12);
|
|
player.knownCardPositions = new Array(12).fill(false);
|
|
player.playersTurn = true;
|
|
player.cardCache = null;
|
|
player.tookDispiledCard = false;
|
|
player.roundPoints = 0;
|
|
player.totalPoints = startOver ? 0 : player.totalPoints;
|
|
player.closedRound = false;
|
|
player.place = null;
|
|
});
|
|
this.discardPile = [this.cardStack.cards.pop()!];
|
|
this.phase = gamePhase.revealTwoCards;
|
|
}
|
|
|
|
async gameLoop() {
|
|
console.log("Game started!");
|
|
this.sendObfuscatedGameUpdate();
|
|
while (this.phase !== gamePhase.gameEnded) {
|
|
this.checkForFullRevealedCards();
|
|
switch (this.phase) {
|
|
case gamePhase.revealTwoCards:
|
|
console.log("\nGame phase: revealTwoCards");
|
|
await this.revealInitialCards();
|
|
break;
|
|
case gamePhase.pickUpCard:
|
|
console.log("\nGame phase: pickUpCard");
|
|
await this.pickUpCard();
|
|
break;
|
|
case gamePhase.placeCard:
|
|
console.log("\nGame phase: placeCard");
|
|
await this.placeCard();
|
|
break;
|
|
case gamePhase.revealCard:
|
|
console.log("\nGame phase: revealCard");
|
|
await this.revealCard();
|
|
break;
|
|
case gamePhase.revealedLastCard:
|
|
console.log("\nGame phase: revealedLastCard");
|
|
await this.revealedLastCard();
|
|
break;
|
|
case gamePhase.newRound:
|
|
console.log("\nGame phase: newRound");
|
|
this.checkIfPointLimitReached();
|
|
await this.nextRound();
|
|
break;
|
|
default:
|
|
console.log("\nGame Ended.");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Game Phases
|
|
|
|
async revealInitialCards() {
|
|
this.sendMessageToAllPlayers("Reveal two cards");
|
|
while (!this.allPlayersRevealedInitialCards()) {
|
|
const playersWithRevealedInitialCards =
|
|
this.getPlayersWithRevealedInitialCards();
|
|
const playersWithUnrevealedInitialCards = this.players.filter(
|
|
(player) => !playersWithRevealedInitialCards.includes(player)
|
|
);
|
|
const playersSocketIds = playersWithUnrevealedInitialCards.map(
|
|
(player) => player.socketId
|
|
);
|
|
await this.waitForPlayerActions<CardPosition>(
|
|
[["click-card", this.revealCardAction.bind(this)]],
|
|
playersSocketIds
|
|
);
|
|
}
|
|
this.setInitialPlayersTurn();
|
|
this.phase = gamePhase.pickUpCard;
|
|
this.sendObfuscatedGameUpdate();
|
|
}
|
|
|
|
async pickUpCard() {
|
|
const playerOnTurn = this.getPlayersTurn();
|
|
if (playerOnTurn.closedRound) {
|
|
this.phase = gamePhase.revealedLastCard;
|
|
this.sendObfuscatedGameUpdate();
|
|
return;
|
|
}
|
|
console.log(`Waiting for ${playerOnTurn.name} to pick up card`);
|
|
await this.waitForPlayerActions(
|
|
[
|
|
["draw-from-card-stack", this.drawCardAction.bind(this)],
|
|
["click-discard-pile", this.takeDiscardPileAction.bind(this)],
|
|
],
|
|
[playerOnTurn.socketId]
|
|
);
|
|
this.phase = gamePhase.placeCard;
|
|
this.sendObfuscatedGameUpdate();
|
|
}
|
|
|
|
async placeCard() {
|
|
const playerOnTurn = this.getPlayersTurn();
|
|
console.log(`Waiting for ${playerOnTurn.name} to place card`);
|
|
|
|
const expectedActions: ExpectedPlayerActions = [
|
|
["click-card", this.placeCardAction.bind(this)],
|
|
];
|
|
if (!playerOnTurn.tookDispiledCard) {
|
|
expectedActions.push([
|
|
"click-discard-pile",
|
|
this.discardCardToPileAction.bind(this),
|
|
]);
|
|
}
|
|
playerOnTurn.tookDispiledCard = false;
|
|
await this.waitForPlayerActions(expectedActions, [playerOnTurn.socketId]);
|
|
this.sendObfuscatedGameUpdate();
|
|
}
|
|
|
|
async revealCard() {
|
|
const playerOnTurn = this.getPlayersTurn();
|
|
console.log(`Waiting for ${playerOnTurn.name} to reveal a card`);
|
|
|
|
const numberOfRevealedCards = playerOnTurn.getRevealedCardCount();
|
|
// ensures that the player does not select an already revealed card
|
|
while (playerOnTurn.getRevealedCardCount() <= numberOfRevealedCards) {
|
|
await this.waitForPlayerActions(
|
|
[["click-card", this.revealCardAction.bind(this)]],
|
|
[playerOnTurn.socketId]
|
|
);
|
|
}
|
|
this.nextPlayersTurn();
|
|
this.phase = gamePhase.pickUpCard;
|
|
this.sendObfuscatedGameUpdate();
|
|
}
|
|
|
|
async revealedLastCard() {
|
|
this.revealAllCards();
|
|
this.evaluateAndSavePoints();
|
|
this.phase = gamePhase.newRound;
|
|
this.sendObfuscatedGameUpdate();
|
|
console.log("Waiting for next round");
|
|
}
|
|
|
|
async nextRound() {
|
|
const playerSocketIds = this.players.map((player) => player.socketId);
|
|
await this.waitForPlayerActions(
|
|
[["next-round", this.nextRoundAction.bind(this)]],
|
|
playerSocketIds
|
|
);
|
|
}
|
|
|
|
// Player Action Callbacks
|
|
|
|
revealCardAction(playerSocketId: string, cardPosition: number) {
|
|
const player = this.getPlayerBySocketId(playerSocketId);
|
|
const revealedCard = player.cards[cardPosition];
|
|
console.log(`Revealed card ${revealedCard} at position ${cardPosition}`);
|
|
const playerIndex = this.players.indexOf(player!);
|
|
this.players[playerIndex].knownCardPositions[cardPosition] = true;
|
|
this.sendObfuscatedGameUpdate();
|
|
}
|
|
|
|
drawCardAction(playerSocketId: string, data: any) {
|
|
const player = this.getPlayerBySocketId(playerSocketId);
|
|
console.log(`Player ${player.name} drawed a card.`);
|
|
const drawnCard = this.cardStack.cards.pop()!;
|
|
player.cardCache = drawnCard;
|
|
this.sendObfuscatedGameUpdate();
|
|
}
|
|
|
|
takeDiscardPileAction(playerSocketId: string, data: any) {
|
|
const player = this.getPlayerBySocketId(playerSocketId);
|
|
console.log(`Player ${player.name} took the card from discard pile.`);
|
|
const discardPileCard = this.discardPile.pop()!;
|
|
player.cardCache = discardPileCard;
|
|
player.tookDispiledCard = true;
|
|
this.sendObfuscatedGameUpdate();
|
|
}
|
|
|
|
discardCardToPileAction(playerSocketId: string, data: any) {
|
|
const player = this.getPlayerBySocketId(playerSocketId);
|
|
console.log(`Player ${player.name} discarded a card to the pile.`);
|
|
const discardedCard = player.cardCache!;
|
|
this.discardPile.push(discardedCard);
|
|
player.cardCache = null;
|
|
this.phase = gamePhase.revealCard;
|
|
this.sendObfuscatedGameUpdate();
|
|
}
|
|
|
|
placeCardAction(playerSocketId: string, cardPosition: number) {
|
|
const player = this.getPlayerBySocketId(playerSocketId);
|
|
console.log(`Player ${player.name} placed a card.`);
|
|
const placedCard = player.cardCache!;
|
|
player.cardCache = null;
|
|
const replacedCard = player.cards[cardPosition];
|
|
this.discardPile.push(replacedCard);
|
|
player.cards[cardPosition] = placedCard;
|
|
player.knownCardPositions[cardPosition] = true;
|
|
this.nextPlayersTurn();
|
|
this.phase = gamePhase.pickUpCard;
|
|
this.sendObfuscatedGameUpdate();
|
|
}
|
|
|
|
nextRoundAction(playerSocketId: string, data: any) {
|
|
this.initializeNewRound();
|
|
this.sendObfuscatedGameUpdate();
|
|
}
|
|
|
|
/**
|
|
* This function waits for a player to perform one of the expected actions.
|
|
* When a player performs one of the expected actions, the corresponding callback is called and further processes the player data.
|
|
* All event listeners are removed after every player defined in expectedFrom performed the expected action.
|
|
* The function also returns a promise that resolves with the data sent by the player.
|
|
* @param expectedActions
|
|
* @param expectedFrom
|
|
* @returns playerSocketId and data sent by the player
|
|
*/
|
|
waitForPlayerActions<ActionDataType>(
|
|
expectedActions: ExpectedPlayerActions,
|
|
expectedFrom: Player["socketId"][]
|
|
): Promise<PlayerAction<ActionDataType>> {
|
|
const eventListeners = new Map<string, (...args: any[]) => void>();
|
|
|
|
const addPlayerActionListeners = (
|
|
resolve: (
|
|
value:
|
|
| PlayerAction<ActionDataType>
|
|
| PromiseLike<PlayerAction<ActionDataType>>
|
|
) => void
|
|
) => {
|
|
expectedFrom.forEach((playerSocketId) => {
|
|
const playerSocket = io.sockets.sockets.get(playerSocketId);
|
|
if (playerSocket) {
|
|
expectedActions.forEach((expectedAction) => {
|
|
const [actionName, processAction] = expectedAction;
|
|
const eventListener = (data: ActionDataType, ackFunction: any) => {
|
|
console.log(`Received ${actionName} from ${playerSocketId}`);
|
|
processAction(playerSocketId, data);
|
|
// remove current and event listeners of alternative expected actions
|
|
removePlayerActionListeners();
|
|
const playerResponse = { playerSocketId, data };
|
|
// ackFunction("success");
|
|
resolve(playerResponse);
|
|
};
|
|
playerSocket.on(actionName, eventListener);
|
|
eventListeners.set(
|
|
`${playerSocketId}-${actionName}`,
|
|
eventListener
|
|
);
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
const removePlayerActionListeners = () => {
|
|
expectedFrom.forEach((playerSocketId) => {
|
|
const playerSocket = io.sockets.sockets.get(playerSocketId);
|
|
if (playerSocket) {
|
|
expectedActions.forEach((expectedAction) => {
|
|
const [actionName] = expectedAction;
|
|
|
|
const eventListener = eventListeners.get(
|
|
`${playerSocketId}-${actionName}`
|
|
);
|
|
if (eventListener) {
|
|
playerSocket.off(actionName, eventListener);
|
|
eventListeners.delete(`${playerSocketId}-${actionName}`);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
return new Promise<PlayerAction<ActionDataType>>((resolve) => {
|
|
addPlayerActionListeners(resolve);
|
|
});
|
|
}
|
|
|
|
sendObfuscatedGameUpdate() {
|
|
// console.trace("sendObfuscatedGameUpdate");
|
|
this.updatePlayerRoundPoints();
|
|
const obfuscatedGame: ObfuscatedGame = {
|
|
sessionId: this.sessionId,
|
|
playerCount: this.playerCount,
|
|
phase: this.phase,
|
|
round: this.round,
|
|
discardPile: this.discardPile,
|
|
players: this.players.map(({ cards, ...player }) => {
|
|
return {
|
|
...player,
|
|
cards: cards.map((card: Card, index: number) => {
|
|
// unknown cards are obfuscated to X
|
|
return {
|
|
id: player.knownCardPositions[index] ? card.id : 0,
|
|
value: player.knownCardPositions[index] ? card.value : "X",
|
|
name: player.knownCardPositions[index]
|
|
? card.name
|
|
: "Facedown Card",
|
|
color: player.knownCardPositions[index] ? card.color : "black",
|
|
matchColorToCardValue: card.matchColorToCardValue,
|
|
};
|
|
}),
|
|
};
|
|
}),
|
|
cardStack: {
|
|
cards: this.cardStack.cards.map((card: Card) => {
|
|
// player may not see the value of the facedown cards in the cardStack
|
|
return {
|
|
id: 0,
|
|
value: "X",
|
|
name: "Facedown Card",
|
|
color: "black",
|
|
};
|
|
}),
|
|
},
|
|
};
|
|
console.log("Sending game update");
|
|
io.to(this.sessionId).emit("game-update", obfuscatedGame);
|
|
}
|
|
|
|
sendNullGameUpdate() {
|
|
io.to(this.sessionId).emit("game-update", null);
|
|
}
|
|
|
|
updatePlayerRoundPoints() {
|
|
this.players.forEach((player) => {
|
|
const revealedCardValuesSum = player.getRevealedCardsValueSum();
|
|
const threeOfAKinds = player.getThreeOfAKinds();
|
|
const threeOfAKindPoints = threeOfAKinds.reduce(
|
|
(points, threeOfAKind) => points + threeOfAKind.value * 3,
|
|
0
|
|
);
|
|
|
|
player.roundPoints = revealedCardValuesSum - threeOfAKindPoints;
|
|
});
|
|
}
|
|
|
|
getPlayersWithLowestPoints(): Player[] {
|
|
this.updatePlayerRoundPoints();
|
|
const lowestScore = Math.min(
|
|
...this.players.map((player) => player.roundPoints)
|
|
);
|
|
const playersWithLowestPoints = this.players.filter(
|
|
(player) => player.roundPoints === lowestScore
|
|
);
|
|
return playersWithLowestPoints;
|
|
}
|
|
|
|
evaluateAndSavePoints() {
|
|
const playersWithLowestPoints = this.getPlayersWithLowestPoints();
|
|
const playerClosedRound = this.getPlayerThatClosedRound();
|
|
let playerClosedRoundLostMessage = "";
|
|
if (
|
|
playersWithLowestPoints.includes(playerClosedRound) &&
|
|
playersWithLowestPoints.length === 1
|
|
) {
|
|
this.sendMessageToAllPlayers(`${playerClosedRound.name} won the round!`);
|
|
this.players.forEach((player) => {
|
|
player.totalPoints += player.roundPoints;
|
|
});
|
|
return;
|
|
} else if (playersWithLowestPoints.length === 1) {
|
|
playerClosedRoundLostMessage = playerClosedRoundLostMessage.concat(
|
|
`${playersWithLowestPoints[0].name} won the round!`
|
|
);
|
|
} else if (playersWithLowestPoints.length > 1) {
|
|
playerClosedRoundLostMessage = playerClosedRoundLostMessage.concat(
|
|
`\n ${playersWithLowestPoints
|
|
.map((player) => player.name)
|
|
.join(", ")} scored equally the lowest points!`
|
|
);
|
|
}
|
|
this.players.forEach((player) => {
|
|
if (player.closedRound) player.totalPoints += player.roundPoints * 2;
|
|
else player.totalPoints += player.roundPoints;
|
|
});
|
|
playerClosedRoundLostMessage = playerClosedRoundLostMessage.concat(
|
|
`\n ${playerClosedRound.name} points are doubled!`
|
|
);
|
|
this.sendMessageToAllPlayers(playerClosedRoundLostMessage);
|
|
}
|
|
|
|
checkForFullRevealedCards() {
|
|
const alreadyClosedPlayers = this.players.filter(
|
|
(player) => player.closedRound
|
|
);
|
|
if (alreadyClosedPlayers.length > 0) return; // TODO: check if this is correct with more than 2 players
|
|
|
|
const playerWithAllCardsRevealed = this.players.find((player) =>
|
|
player.knownCardPositions.every(
|
|
(knownCardPosition) => knownCardPosition === true
|
|
)
|
|
);
|
|
if (playerWithAllCardsRevealed) {
|
|
playerWithAllCardsRevealed.closedRound = true;
|
|
}
|
|
}
|
|
|
|
checkIfPointLimitReached() {
|
|
const highestPoints = Math.max(
|
|
...this.players.map((player) => player.totalPoints)
|
|
);
|
|
|
|
const lowestPoints = Math.min(
|
|
...this.players.map((player) => player.totalPoints)
|
|
);
|
|
const playersWithHighestPoints = this.players.filter(
|
|
(player) => player.totalPoints === highestPoints
|
|
);
|
|
|
|
if (highestPoints >= 100) {
|
|
if (playersWithHighestPoints.length === 1) {
|
|
const playerWithHighestPoints = playersWithHighestPoints[0];
|
|
this.sendMessageToAllPlayers(
|
|
`${playerWithHighestPoints.name} lost with ${playerWithHighestPoints.totalPoints}!`
|
|
);
|
|
} else {
|
|
const playerNames = playersWithHighestPoints
|
|
.map((player) => player.name)
|
|
.join(", ");
|
|
this.sendMessageToAllPlayers(
|
|
`Multiple players: ${playerNames} lost with ${highestPoints} points!`
|
|
);
|
|
}
|
|
const playersWithLowestPoints = this.players.filter(
|
|
(player) => player.totalPoints === lowestPoints
|
|
);
|
|
playersWithLowestPoints.forEach((player) => (player.place = 1));
|
|
|
|
this.phase = gamePhase.gameEnded;
|
|
this.sendObfuscatedGameUpdate();
|
|
allGames.splice(allGames.indexOf(this), 1);
|
|
}
|
|
}
|
|
|
|
checkForPlayerLeave() {
|
|
const playersInSession = io.sockets.adapter.rooms.get(this.sessionId);
|
|
if (playersInSession?.size ?? 0 < this.playerCount) {
|
|
const playerThatLeftSession = this.players.filter(
|
|
(player) => !playersInSession?.has(player.socketId)
|
|
);
|
|
console.log("players that left session", playerThatLeftSession);
|
|
if (playerThatLeftSession.length > 0) {
|
|
this.sendMessageToAllPlayers(
|
|
`${playerThatLeftSession.map(
|
|
(player) => player.name + " "
|
|
)} left the session!`
|
|
);
|
|
this.phase = gamePhase.gameEnded;
|
|
this.sendNullGameUpdate();
|
|
io.to(this.sessionId).emit(
|
|
"clients-in-session",
|
|
playersInSession?.size ?? 0
|
|
);
|
|
allGames.splice(allGames.indexOf(this), 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
revealAllCards() {
|
|
this.players.forEach((player) => {
|
|
player.knownCardPositions = player.knownCardPositions.map(
|
|
(knownCardPosition) => true
|
|
);
|
|
});
|
|
}
|
|
|
|
// Helpers
|
|
|
|
getPlayersWithRevealedInitialCards(): Player[] {
|
|
return this.players.filter((player) => {
|
|
return player.hasInitialCardsRevealed();
|
|
});
|
|
}
|
|
|
|
allPlayersRevealedInitialCards() {
|
|
const playersWithRevealedInitialCards =
|
|
this.getPlayersWithRevealedInitialCards();
|
|
return playersWithRevealedInitialCards.length === this.playerCount;
|
|
}
|
|
|
|
setInitialPlayersTurn() {
|
|
const playersWithHighestRevealedCardsValueSum = this.players.reduce(
|
|
(playersWithHighestRevealedCardsValueSum, player) => {
|
|
if (
|
|
player.getRevealedCardsValueSum() ===
|
|
playersWithHighestRevealedCardsValueSum[0].getRevealedCardsValueSum()
|
|
) {
|
|
if (
|
|
player.getHighestRevealedCardValue() >
|
|
playersWithHighestRevealedCardsValueSum[0].getHighestRevealedCardValue()
|
|
) {
|
|
playersWithHighestRevealedCardsValueSum = [player];
|
|
} else if (
|
|
player.getHighestRevealedCardValue() ===
|
|
playersWithHighestRevealedCardsValueSum[0].getHighestRevealedCardValue()
|
|
) {
|
|
playersWithHighestRevealedCardsValueSum.push(player);
|
|
}
|
|
} else if (
|
|
player.getRevealedCardsValueSum() >
|
|
playersWithHighestRevealedCardsValueSum[0].getRevealedCardsValueSum()
|
|
) {
|
|
playersWithHighestRevealedCardsValueSum = [player];
|
|
}
|
|
return playersWithHighestRevealedCardsValueSum;
|
|
},
|
|
[this.players[0]]
|
|
);
|
|
|
|
const playerWithHighestRevealedCardsValueSum =
|
|
playersWithHighestRevealedCardsValueSum[
|
|
Math.floor(
|
|
Math.random() * playersWithHighestRevealedCardsValueSum.length
|
|
)
|
|
];
|
|
|
|
this.players.forEach((player) => {
|
|
if (player === playerWithHighestRevealedCardsValueSum) {
|
|
player.playersTurn = true;
|
|
} else {
|
|
player.playersTurn = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
nextPlayersTurn() {
|
|
const playerOnTurn = this.getPlayersTurn();
|
|
const playersTurnIndex = this.players.indexOf(playerOnTurn);
|
|
const nextPlayersTurnIndex = (playersTurnIndex + 1) % this.playerCount;
|
|
this.players[playersTurnIndex].playersTurn = false;
|
|
this.players[nextPlayersTurnIndex].playersTurn = true;
|
|
}
|
|
|
|
getPlayersTurn(): Player {
|
|
const playerOnTurn = this.players.find(
|
|
(player) => player.playersTurn === true
|
|
);
|
|
if (playerOnTurn) return playerOnTurn;
|
|
else throw new Error("No player on turn found!");
|
|
}
|
|
|
|
getPlayerThatClosedRound(): Player {
|
|
const playerThatClosedRound = this.players.find(
|
|
(player) => player.closedRound === true
|
|
);
|
|
if (playerThatClosedRound) return playerThatClosedRound;
|
|
else throw new Error("No player that closed the round found!");
|
|
}
|
|
|
|
getPlayerBySocketId(playerSocketId: string): Player {
|
|
const player = this.players.find(
|
|
(player) => player.socketId === playerSocketId
|
|
);
|
|
if (player) return player;
|
|
else throw new Error(`No player with socketId ${playerSocketId} found!`);
|
|
}
|
|
|
|
listPlayerSocketListeners() {
|
|
console.log("Player socket listeners:");
|
|
io.sockets.sockets.forEach((socket) => {
|
|
console.log(socket.id);
|
|
socket.eventNames().forEach((eventName) => {
|
|
console.log(`${eventName.toString()}`);
|
|
});
|
|
});
|
|
}
|
|
|
|
sendMessageToAllPlayers(message: string) {
|
|
io.to(this.sessionId).emit("message", message);
|
|
console.log(`Sent Message (Session): ${message}`);
|
|
}
|
|
}
|