Testing claude
This commit is contained in:
parent
5312b0dc7f
commit
720c0aa143
@ -16,7 +16,7 @@ import {
|
||||
} from "./games/constants";
|
||||
|
||||
import { getValidRoads, getValidCorners, isRuleEnabled } from "../util/validLocations";
|
||||
import { Player, Game, Session, CornerPlacement, RoadPlacement, Offer } from "./games/types";
|
||||
import { Player, Game, Session, CornerPlacement, RoadPlacement, Offer, Turn } from "./games/types";
|
||||
import { newPlayer } from "./games/playerFactory";
|
||||
import { normalizeIncoming, shuffleArray } from "./games/utils";
|
||||
// import type { GameState } from './games/state'; // unused import removed during typing pass
|
||||
@ -49,7 +49,7 @@ initGameDB()
|
||||
|
||||
// shuffleArray imported from './games/utils.ts'
|
||||
|
||||
const games: Record<string, any> = {};
|
||||
const games: Record<string, Game> = {};
|
||||
const audio: Record<string, any> = {};
|
||||
|
||||
const processTies = (players: Player[]): boolean => {
|
||||
@ -57,12 +57,12 @@ const processTies = (players: Player[]): boolean => {
|
||||
* order, and their current roll. If a resulting
|
||||
* roll array has more than one element, then there
|
||||
* is a tie that must be resolved */
|
||||
let slots: any[] = [];
|
||||
let slots: Player[][] = [];
|
||||
players.forEach((player: Player) => {
|
||||
if (!slots[player.order]) {
|
||||
slots[player.order] = [];
|
||||
}
|
||||
slots[player.order].push(player);
|
||||
slots[player.order]!.push(player);
|
||||
});
|
||||
|
||||
let ties = false,
|
||||
@ -385,7 +385,7 @@ const distributeResources = (game: Game, roll: number): void => {
|
||||
}
|
||||
};
|
||||
|
||||
const pickRobber = (game: any): void => {
|
||||
const pickRobber = (game: Game): void => {
|
||||
const selection = Math.floor(Math.random() * 3);
|
||||
switch (selection) {
|
||||
case 0:
|
||||
@ -756,8 +756,13 @@ const loadGame = async (id: string) => {
|
||||
return game;
|
||||
};
|
||||
|
||||
const adminCommands = (game: any, action: string, value: string, query: any): any => {
|
||||
let color: string | undefined, parts: RegExpMatchArray | null, session: any, corners: any, corner: any, error: any;
|
||||
const adminCommands = (game: Game, action: string, value: string, query: any): any => {
|
||||
let color: string | undefined,
|
||||
parts: RegExpMatchArray | null,
|
||||
session: Session | any,
|
||||
corners: any,
|
||||
corner: any,
|
||||
error: any;
|
||||
void color;
|
||||
|
||||
switch (action) {
|
||||
@ -809,9 +814,11 @@ const adminCommands = (game: any, action: string, value: string, query: any): an
|
||||
const type = parts[1],
|
||||
card = parts[3] || 1;
|
||||
|
||||
for (let id in game.sessions) {
|
||||
if (game.sessions[id].name === game.turn.name) {
|
||||
session = game.sessions[id];
|
||||
if (game.sessions) {
|
||||
for (let id in game.sessions) {
|
||||
if (game.sessions[id] && game.sessions[id].name === game.turn.name) {
|
||||
session = game.sessions[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -902,8 +909,10 @@ const adminCommands = (game: any, action: string, value: string, query: any): an
|
||||
}
|
||||
|
||||
let tmp = game.developmentCards.splice(index, 1)[0];
|
||||
tmp.turn = game.turns ? game.turns - 1 : 0;
|
||||
session.player.development.push(tmp);
|
||||
if (tmp) {
|
||||
(tmp as any)["turn"] = game.turns ? game.turns - 1 : 0;
|
||||
session.player.development.push(tmp);
|
||||
}
|
||||
addChatMessage(game, null, `Admin gave a ${card}-${type} to ${game.turn.name}.`);
|
||||
break;
|
||||
|
||||
@ -972,16 +981,16 @@ const adminCommands = (game: any, action: string, value: string, query: any): an
|
||||
|
||||
case "pass":
|
||||
let name = game.turn.name;
|
||||
const next = getNextPlayerSession(game, name);
|
||||
const next = getNextPlayerSession(game, name || "");
|
||||
if (!next) {
|
||||
addChatMessage(game, null, `Admin attempted to skip turn but no next player was found.`);
|
||||
break;
|
||||
}
|
||||
game.turn = {
|
||||
name: next.player,
|
||||
name: next.name,
|
||||
color: next.color,
|
||||
};
|
||||
game.turns++;
|
||||
} as unknown as Turn;
|
||||
game.turns = (game.turns || 0) + 1;
|
||||
startTurnTimer(game, next);
|
||||
addChatMessage(game, null, `The admin skipped ${name}'s turn.`);
|
||||
addChatMessage(game, null, `It is ${next.name}'s turn.`);
|
||||
@ -1055,7 +1064,7 @@ const adminCommands = (game: any, action: string, value: string, query: any): an
|
||||
}
|
||||
};
|
||||
|
||||
const setPlayerName = (game: any, session: any, name: string): string | undefined => {
|
||||
const setPlayerName = (game: Game, session: Session, name: string): string | undefined => {
|
||||
if (session.name === name) {
|
||||
return; /* no-op */
|
||||
}
|
||||
@ -1118,7 +1127,7 @@ const setPlayerName = (game: any, session: any, name: string): string | undefine
|
||||
session.name = name;
|
||||
session.live = true;
|
||||
if (session.player) {
|
||||
session.color = session.player.color;
|
||||
session.color = session.player.color || "";
|
||||
session.player.name = session.name;
|
||||
session.player.status = `Active`;
|
||||
session.player.lastActive = Date.now();
|
||||
@ -1394,7 +1403,7 @@ const processRoad = (game: Game, color: string, roadIndex: number, placedRoad: R
|
||||
return roadLength;
|
||||
};
|
||||
|
||||
const buildRoadGraph = (game: Game, color: string, roadIndex: number, placedRoad: RoadPlacement, set: any) => {
|
||||
const buildRoadGraph = (game: Game, color: string, roadIndex: number, placedRoad: RoadPlacement, set: number[]) => {
|
||||
/* If this road isn't assigned to the walking color, skip it */
|
||||
if (placedRoad.color !== color) {
|
||||
return;
|
||||
@ -1407,7 +1416,7 @@ const buildRoadGraph = (game: Game, color: string, roadIndex: number, placedRoad
|
||||
placedRoad.walking = true;
|
||||
set.push(roadIndex);
|
||||
/* Calculate the longest road branching from both corners */
|
||||
layout.roads?.[roadIndex]?.corners.forEach((cornerIndex: any) => {
|
||||
layout.roads?.[roadIndex]?.corners.forEach((cornerIndex: number) => {
|
||||
const placedCorner = game.placements?.corners?.[cornerIndex];
|
||||
if (!placedCorner) return;
|
||||
buildCornerGraph(game, color, cornerIndex, placedCorner, set);
|
||||
@ -1454,11 +1463,11 @@ const calculateRoadLengths = (game: Game, session: Session): void => {
|
||||
* needed to catch loops where starting from an outside end
|
||||
* point may result in not counting the length of the loop
|
||||
*/
|
||||
let graphs: any[] = [];
|
||||
let graphs: { color: string; set: number[]; longestRoad?: number; longestStartSegment?: number }[] = [];
|
||||
layout.roads.forEach((_: any, roadIndex: number) => {
|
||||
const placedRoad = game.placements?.roads?.[roadIndex];
|
||||
if (placedRoad && placedRoad.color && typeof placedRoad.color === "string") {
|
||||
let set: any[] = [];
|
||||
let set: number[] = [];
|
||||
buildRoadGraph(game, placedRoad.color, roadIndex, placedRoad, set);
|
||||
if (set.length) {
|
||||
graphs.push({ color: placedRoad.color, set });
|
||||
@ -1744,26 +1753,26 @@ const canMeetOffer = (player: Player, offer: Offer): boolean => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const gameSignature = (game: any): string => {
|
||||
const gameSignature = (game: Game): string => {
|
||||
if (!game) {
|
||||
return "";
|
||||
}
|
||||
const salt = 251;
|
||||
const signature =
|
||||
game.borderOrder.map((border: any) => `00${(Number(border) ^ salt).toString(16)}`.slice(-2)).join("") +
|
||||
(game.borderOrder || []).map((border: any) => `00${(Number(border) ^ salt).toString(16)}`.slice(-2)).join("") +
|
||||
"-" +
|
||||
game.pipOrder
|
||||
(game.pipOrder || [])
|
||||
.map((pip: any, index: number) => `00${(Number(pip) ^ salt ^ (salt * index)).toString(16)}`.slice(-2))
|
||||
.join("") +
|
||||
"-" +
|
||||
game.tileOrder
|
||||
(game.tileOrder || [])
|
||||
.map((tile: any, index: number) => `00${(Number(tile) ^ salt ^ (salt * index)).toString(16)}`.slice(-2))
|
||||
.join("");
|
||||
|
||||
return signature;
|
||||
};
|
||||
|
||||
const setGameFromSignature = (game: any, border: string, pip: string, tile: string): boolean => {
|
||||
const setGameFromSignature = (game: Game, border: string, pip: string, tile: string): boolean => {
|
||||
const salt = 251;
|
||||
const borders = [],
|
||||
pips = [],
|
||||
@ -2074,19 +2083,19 @@ const trade = (game: Game, session: Session, action: string, offer?: Offer): str
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const clearTimeNotice = (game: any, session: any): string | undefined => {
|
||||
if (!session.player.turnNotice) {
|
||||
const clearTimeNotice = (game: Game, session: Session): string | undefined => {
|
||||
if (!session.player || !session.player.turnNotice) {
|
||||
/* benign state; don't alert the user */
|
||||
//return `You have not been idle.`;
|
||||
}
|
||||
session.player.turnNotice = "";
|
||||
if (session.player) session.player.turnNotice = "";
|
||||
sendUpdateToPlayer(game, session, {
|
||||
private: session.player,
|
||||
});
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const startTurnTimer = (game: any, session: any) => {
|
||||
const startTurnTimer = (game: Game, session: Session) => {
|
||||
const timeout = 90;
|
||||
if (!session.ws) {
|
||||
console.log(`${session.id}: Aborting turn timer as ${session.name} is disconnected.`);
|
||||
@ -2102,7 +2111,9 @@ const startTurnTimer = (game: any, session: any) => {
|
||||
}
|
||||
game.turnTimer = setTimeout(() => {
|
||||
console.log(`${session.id}: Turn timer expired for ${session.name}`);
|
||||
session.player.turnNotice = "It is still your turn.";
|
||||
if (session.player) {
|
||||
session.player.turnNotice = "It is still your turn.";
|
||||
}
|
||||
sendUpdateToPlayer(game, session, {
|
||||
private: session.player,
|
||||
});
|
||||
@ -2110,14 +2121,18 @@ const startTurnTimer = (game: any, session: any) => {
|
||||
}, timeout * 1000);
|
||||
};
|
||||
|
||||
const resetTurnTimer = (game: any, session: any): void => {
|
||||
const resetTurnTimer = (game: Game, session: Session): void => {
|
||||
startTurnTimer(game, session);
|
||||
};
|
||||
|
||||
const stopTurnTimer = (game: any): void => {
|
||||
const stopTurnTimer = (game: Game): void => {
|
||||
if (game.turnTimer) {
|
||||
console.log(`${info}: Stopping turn timer.`);
|
||||
clearTimeout(game.turnTimer);
|
||||
try {
|
||||
clearTimeout(game.turnTimer);
|
||||
} catch (e) {
|
||||
/* ignore if not a real timeout */
|
||||
}
|
||||
game.turnTimer = 0;
|
||||
}
|
||||
return undefined;
|
||||
@ -2172,7 +2187,7 @@ const pass = (game: any, session: any): string | undefined => {
|
||||
color: next.color,
|
||||
};
|
||||
if (next.player) {
|
||||
(next.player as any)["turnStart"] = Date.now();
|
||||
next.player.turnStart = Date.now();
|
||||
}
|
||||
startTurnTimer(game, next);
|
||||
game.turns++;
|
||||
@ -2196,11 +2211,9 @@ const pass = (game: any, session: any): string | undefined => {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const placeRobber = (game: any, session: any, robber: any): string | undefined => {
|
||||
const placeRobber = (game: Game, session: Session, robber: number | string): string | undefined => {
|
||||
const name = session.name;
|
||||
if (typeof robber === "string") {
|
||||
robber = parseInt(robber);
|
||||
}
|
||||
let robberIdx = typeof robber === "string" ? parseInt(robber) : robber;
|
||||
|
||||
if (game.state !== "normal" && game.turn.roll !== 7) {
|
||||
return `You cannot place robber unless 7 was rolled!`;
|
||||
@ -2209,26 +2222,26 @@ const placeRobber = (game: any, session: any, robber: any): string | undefined =
|
||||
return `You cannot place the robber when it isn't your turn.`;
|
||||
}
|
||||
|
||||
for (let color in game.players) {
|
||||
if (game.players[color].status === "Not active") {
|
||||
continue;
|
||||
}
|
||||
if (game.players[color].mustDiscard > 0) {
|
||||
for (const color in game.players) {
|
||||
const p = game.players[color];
|
||||
if (!p) continue;
|
||||
if (p.status === "Not active") continue;
|
||||
if ((p.mustDiscard || 0) > 0) {
|
||||
return `You cannot place the robber until everyone has discarded!`;
|
||||
}
|
||||
}
|
||||
|
||||
if (game.robber === robber) {
|
||||
if (game.robber === robberIdx) {
|
||||
return `You must move the robber to a new location!`;
|
||||
}
|
||||
game.robber = robber;
|
||||
game.turn.placedRobber = true;
|
||||
game.robber = robberIdx as number;
|
||||
(game.turn as any).placedRobber = true;
|
||||
|
||||
pickRobber(game);
|
||||
addActivity(game, null, `${game.robberName} Robber Robinson entered the scene as the nefarious robber!`);
|
||||
|
||||
let targets: Array<{ color: string; name: string }> = [];
|
||||
layout.tiles?.[robber]?.corners?.forEach((cornerIndex: number) => {
|
||||
const targets: Array<{ color: string; name: string }> = [];
|
||||
layout.tiles?.[robberIdx as number]?.corners?.forEach((cornerIndex: number) => {
|
||||
const active = game.placements?.corners?.[cornerIndex];
|
||||
if (
|
||||
active &&
|
||||
@ -2244,7 +2257,8 @@ const placeRobber = (game: any, session: any, robber: any): string | undefined =
|
||||
});
|
||||
|
||||
if (targets.length) {
|
||||
(game.turn.actions = ["steal-resource"]), (game.turn.limits = { players: targets });
|
||||
game.turn.actions = ["steal-resource"];
|
||||
game.turn.limits = { players: targets } as any;
|
||||
} else {
|
||||
game.turn.actions = [];
|
||||
game.turn.robberInAction = false;
|
||||
@ -2272,28 +2286,24 @@ const placeRobber = (game: any, session: any, robber: any): string | undefined =
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const stealResource = (game: any, session: any, color: any): string | undefined => {
|
||||
if (game.turn.actions.indexOf("steal-resource") === -1) {
|
||||
const stealResource = (game: Game, session: Session, color: string): string | undefined => {
|
||||
if (!game.turn.actions || game.turn.actions.indexOf("steal-resource") === -1) {
|
||||
return `You can only steal a resource when it is valid to do so!`;
|
||||
}
|
||||
if (game.turn.limits.players.findIndex((item: any) => item.color === color) === -1) {
|
||||
const playersLimit = (game.turn.limits as any)?.players || [];
|
||||
if (playersLimit.findIndex((item: any) => item.color === color) === -1) {
|
||||
return `You can only steal a resource from a player on this terrain!`;
|
||||
}
|
||||
let victim: any | undefined;
|
||||
for (let key in game.sessions) {
|
||||
if (game.sessions[key].color === color) {
|
||||
victim = game.sessions[key];
|
||||
break;
|
||||
}
|
||||
|
||||
const victimSession = sessionFromColor(game, color);
|
||||
if (!victimSession || !victimSession.player) {
|
||||
return `You sent a weird color for the target to steal from.`;
|
||||
}
|
||||
if (!victim || !victim.player) {
|
||||
return `You sent a wierd color for the target to steal from.`;
|
||||
}
|
||||
const victimPlayer: Record<string, any> = victim.player;
|
||||
const sessionPlayer: Record<string, any> = session.player;
|
||||
const victimPlayer = victimSession.player as Player;
|
||||
const sessionPlayer = session.player as Player;
|
||||
const cards: string[] = [];
|
||||
["wheat", "brick", "sheep", "stone", "wood"].forEach((field: string) => {
|
||||
for (let i = 0; i < (victimPlayer[field] || 0); i++) {
|
||||
for (let i = 0; i < ((victimPlayer as any)[field] || 0); i++) {
|
||||
cards.push(field);
|
||||
}
|
||||
});
|
||||
@ -2301,30 +2311,29 @@ const stealResource = (game: any, session: any, color: any): string | undefined
|
||||
debugChat(game, "Before steal");
|
||||
|
||||
if (cards.length === 0) {
|
||||
addChatMessage(game, session, `${victim.name} ` + `did not have any cards for ${session.name} to steal.`);
|
||||
addChatMessage(game, session, `${victimSession.name} did not have any cards for ${session.name} to steal.`);
|
||||
game.turn.actions = [];
|
||||
game.turn.limits = {};
|
||||
game.turn.limits = {} as any;
|
||||
} else {
|
||||
let index = Math.floor(Math.random() * cards.length),
|
||||
type = cards[index];
|
||||
const idx = Math.floor(Math.random() * cards.length);
|
||||
const type = cards[idx];
|
||||
if (!type) {
|
||||
// Defensive: no card type found
|
||||
game.turn.actions = [];
|
||||
game.turn.limits = {};
|
||||
return;
|
||||
game.turn.limits = {} as any;
|
||||
return undefined;
|
||||
}
|
||||
const t = String(type);
|
||||
victimPlayer[t] = (victimPlayer[t] || 0) - 1;
|
||||
victimPlayer["resources"] = (victimPlayer["resources"] || 0) - 1;
|
||||
sessionPlayer[t] = (sessionPlayer[t] || 0) + 1;
|
||||
sessionPlayer["resources"] = (sessionPlayer["resources"] || 0) + 1;
|
||||
(victimPlayer as any)[t] = ((victimPlayer as any)[t] || 0) - 1;
|
||||
(victimPlayer as any)["resources"] = ((victimPlayer as any)["resources"] || 0) - 1;
|
||||
(sessionPlayer as any)[t] = ((sessionPlayer as any)[t] || 0) + 1;
|
||||
(sessionPlayer as any)["resources"] = ((sessionPlayer as any)["resources"] || 0) + 1;
|
||||
game.turn.actions = [];
|
||||
game.turn.limits = {};
|
||||
trackTheft(game, victim.color || "", session.color, type, 1);
|
||||
game.turn.limits = {} as any;
|
||||
trackTheft(game, (victimSession as any).color || "", session.color, type, 1);
|
||||
|
||||
addChatMessage(game, session, `${session.name} randomly stole 1 ${type} from ` + `${victim.name}.`);
|
||||
sendUpdateToPlayer(game, victim, {
|
||||
private: victim.player,
|
||||
addChatMessage(game, session, `${session.name} randomly stole 1 ${type} from ${victimSession.name}.`);
|
||||
sendUpdateToPlayer(game, victimSession, {
|
||||
private: victimSession.player,
|
||||
});
|
||||
}
|
||||
debugChat(game, "After steal");
|
||||
@ -2343,8 +2352,8 @@ const stealResource = (game: any, session: any, color: any): string | undefined
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const buyDevelopment = (game: any, session: any): string | undefined => {
|
||||
const player = session.player;
|
||||
const buyDevelopment = (game: Game, session: Session): string | undefined => {
|
||||
const player = session.player as Player;
|
||||
|
||||
if (game.state !== "normal") {
|
||||
return `You cannot purchase a development card unless the game is active (${game.state}).`;
|
||||
@ -2362,42 +2371,46 @@ const buyDevelopment = (game: any, session: any): string | undefined => {
|
||||
return `Robber is in action. You can not purchase until all Robber tasks are resolved.`;
|
||||
}
|
||||
|
||||
if (game.developmentCards.length < 1) {
|
||||
if (!game.developmentCards || game.developmentCards.length < 1) {
|
||||
return `There are no more development cards!`;
|
||||
}
|
||||
|
||||
if (player.stone < 1 || player.wheat < 1 || player.sheep < 1) {
|
||||
if ((player.stone || 0) < 1 || (player.wheat || 0) < 1 || (player.sheep || 0) < 1) {
|
||||
return `You have insufficient resources to purchase a development card.`;
|
||||
}
|
||||
|
||||
if (game.turn.developmentPurchased) {
|
||||
if ((game.turn as any).developmentPurchased) {
|
||||
return `You have already purchased a development card this turn.`;
|
||||
}
|
||||
|
||||
debugChat(game, "Before development purchase");
|
||||
addActivity(game, session, `${session.name} purchased a development card.`);
|
||||
addChatMessage(game, session, `${session.name} spent 1 stone, 1 wheat, 1 sheep to purchase a development card.`);
|
||||
player.stone--;
|
||||
player.wheat--;
|
||||
player.sheep--;
|
||||
player.stone = (player.stone || 0) - 1;
|
||||
player.wheat = (player.wheat || 0) - 1;
|
||||
player.sheep = (player.sheep || 0) - 1;
|
||||
player.resources = 0;
|
||||
player.developmentCards++;
|
||||
player.developmentCards = (player.developmentCards || 0) + 1;
|
||||
["wheat", "brick", "sheep", "stone", "wood"].forEach((resource) => {
|
||||
player.resources += player[resource];
|
||||
player.resources = (player.resources || 0) + ((player as any)[resource] || 0);
|
||||
});
|
||||
debugChat(game, "After development purchase");
|
||||
const card = game.developmentCards.pop();
|
||||
card.turn = game.turns ? game.turns - 1 : 0;
|
||||
player.development.push(card);
|
||||
const card = (game.developmentCards || []).pop();
|
||||
if (card) {
|
||||
(card as any).turn = game.turns ? game.turns - 1 : 0;
|
||||
if (!player.development) player.development = [] as any;
|
||||
(player.development as any).push(card as any);
|
||||
}
|
||||
|
||||
if (isRuleEnabled(game, "most-developed")) {
|
||||
if (
|
||||
player.development.length >= 5 &&
|
||||
(!game.mostDeveloped || player.developmentCards > game.players[game.mostDeveloped].developmentCards)
|
||||
(player.development?.length || 0) >= 5 &&
|
||||
(!(game as any)["mostDeveloped"] ||
|
||||
(player.developmentCards || 0) > (game.players[(game as any)["mostDeveloped"] as string]?.developmentCards || 0))
|
||||
) {
|
||||
if (game.mostDeveloped !== session.color) {
|
||||
game.mostDeveloped = session.color;
|
||||
game.mostPortCount = player.developmentCards;
|
||||
if ((game as any)["mostDeveloped"] !== session.color) {
|
||||
(game as any)["mostDeveloped"] = session.color;
|
||||
(game as any)["mostPortCount"] = player.developmentCards;
|
||||
addChatMessage(
|
||||
game,
|
||||
session,
|
||||
@ -2414,15 +2427,15 @@ const buyDevelopment = (game: any, session: any): string | undefined => {
|
||||
sendUpdateToPlayers(game, {
|
||||
chat: game.chat,
|
||||
activities: game.activities,
|
||||
mostDeveloped: game.mostDeveloped,
|
||||
mostDeveloped: (game as any)["mostDeveloped"],
|
||||
players: getFilteredPlayers(game),
|
||||
});
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const playCard = (game: any, session: any, card: any): string | undefined => {
|
||||
const name = session.name,
|
||||
player = session.player;
|
||||
const playCard = (game: Game, session: Session, card: any): string | undefined => {
|
||||
const name = session.name;
|
||||
const player = session.player as Player;
|
||||
|
||||
if (game.state !== "normal") {
|
||||
return `You cannot purchase a development card unless the game is active (${game.state}).`;
|
||||
@ -2438,19 +2451,21 @@ const playCard = (game: any, session: any, card: any): string | undefined => {
|
||||
return `Robber is in action. You can not play a card until all Robber tasks are resolved.`;
|
||||
}
|
||||
|
||||
card = player.development.find((item: any) => item.type == card.type && item.card == card.card && !item.card.played);
|
||||
card = (player.development || []).find(
|
||||
(item: any) => item.type == card.type && item.card == card.card && !(item.card as any).played
|
||||
);
|
||||
if (!card) {
|
||||
return `The card you want to play was not found in your hand!`;
|
||||
}
|
||||
|
||||
if (player.playedCard === game.turns && card.type !== "vp") {
|
||||
if ((player as any)["playedCard"] === game.turns && card.type !== "vp") {
|
||||
return `You can only play one development card per turn!`;
|
||||
}
|
||||
|
||||
/* Check if this is a victory point */
|
||||
if (card.type === "vp") {
|
||||
let points = player.points;
|
||||
player.development.forEach((item: any) => {
|
||||
let points = player.points || 0;
|
||||
(player.development || []).forEach((item: any) => {
|
||||
if (item.type === "vp") {
|
||||
points++;
|
||||
}
|
||||
@ -2464,13 +2479,13 @@ const playCard = (game: any, session: any, card: any): string | undefined => {
|
||||
if (card.type === "progress") {
|
||||
switch (card.card) {
|
||||
case "road-1":
|
||||
case "road-2":
|
||||
const allowed = Math.min(player.roads, 2);
|
||||
case "road-2": {
|
||||
const allowed = Math.min(player.roads || 0, 2);
|
||||
if (!allowed) {
|
||||
addChatMessage(game, session, `${session.name} played a Road Building card, but has no roads to build.`);
|
||||
break;
|
||||
}
|
||||
let roads = getValidRoads(game, session.color);
|
||||
const roads = getValidRoads(game, session.color as string);
|
||||
if (roads.length === 0) {
|
||||
addChatMessage(
|
||||
game,
|
||||
@ -2479,9 +2494,9 @@ const playCard = (game: any, session: any, card: any): string | undefined => {
|
||||
);
|
||||
break;
|
||||
}
|
||||
game.turn.active = "road-building";
|
||||
game.turn.free = true;
|
||||
game.turn.freeRoads = allowed;
|
||||
game.turn.active = "road-building" as any;
|
||||
(game.turn as any).free = true;
|
||||
(game.turn as any).freeRoads = allowed;
|
||||
addChatMessage(
|
||||
game,
|
||||
session,
|
||||
@ -2489,9 +2504,10 @@ const playCard = (game: any, session: any, card: any): string | undefined => {
|
||||
);
|
||||
setForRoadPlacement(game, roads);
|
||||
break;
|
||||
}
|
||||
case "monopoly":
|
||||
game.turn.actions = ["select-resources"];
|
||||
game.turn.active = "monopoly";
|
||||
game.turn.active = "monopoly" as any;
|
||||
addActivity(
|
||||
game,
|
||||
session,
|
||||
@ -2500,7 +2516,7 @@ const playCard = (game: any, session: any, card: any): string | undefined => {
|
||||
break;
|
||||
case "year-of-plenty":
|
||||
game.turn.actions = ["select-resources"];
|
||||
game.turn.active = "year-of-plenty";
|
||||
game.turn.active = "year-of-plenty" as any;
|
||||
addActivity(game, session, `${session.name} played the Year of Plenty card.`);
|
||||
break;
|
||||
default:
|
||||
@ -2508,23 +2524,27 @@ const playCard = (game: any, session: any, card: any): string | undefined => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
card.played = true;
|
||||
player.playedCard = game.turns;
|
||||
(card as any).played = true;
|
||||
(player as any)["playedCard"] = game.turns;
|
||||
|
||||
if (card.type === "army") {
|
||||
player.army++;
|
||||
(player as any)["army"] = ((player as any)["army"] || 0) + 1;
|
||||
addChatMessage(game, session, `${session.name} played a Knight and must move the robber!`);
|
||||
|
||||
if (player.army > 2 && (!game.largestArmy || game.players[game.largestArmy].army < player.army)) {
|
||||
if (game.largestArmy !== session.color) {
|
||||
game.largestArmy = session.color;
|
||||
game.largestArmySize = player.army;
|
||||
addChatMessage(game, session, `${session.name} now has the largest army (${player.army})!`);
|
||||
if (
|
||||
(player as any)["army"] > 2 &&
|
||||
(!(game as any)["largestArmy"] ||
|
||||
((game.players as any)[(game as any)["largestArmy"]]?.army || 0) < (player as any)["army"])
|
||||
) {
|
||||
if ((game as any)["largestArmy"] !== session.color) {
|
||||
(game as any)["largestArmy"] = session.color;
|
||||
(game as any)["largestArmySize"] = (player as any)["army"];
|
||||
addChatMessage(game, session, `${session.name} now has the largest army (${(player as any)["army"]})!`);
|
||||
}
|
||||
}
|
||||
|
||||
game.turn.robberInAction = true;
|
||||
delete game.turn.placedRobber;
|
||||
delete (game.turn as any).placedRobber;
|
||||
addChatMessage(
|
||||
game,
|
||||
null,
|
||||
@ -2532,12 +2552,10 @@ const playCard = (game: any, session: any, card: any): string | undefined => {
|
||||
`but a new robber has returned and ${session.name} must now place them.`
|
||||
);
|
||||
game.turn.actions = ["place-robber", "playing-knight"];
|
||||
game.turn.limits = { pips: [] };
|
||||
game.turn.limits = { pips: [] } as any;
|
||||
for (let i = 0; i < 19; i++) {
|
||||
if (i === game.robber) {
|
||||
continue;
|
||||
}
|
||||
game.turn.limits.pips.push(i);
|
||||
if (i === game.robber) continue;
|
||||
(game.turn.limits as any).pips.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2547,8 +2565,8 @@ const playCard = (game: any, session: any, card: any): string | undefined => {
|
||||
sendUpdateToPlayers(game, {
|
||||
chat: game.chat,
|
||||
activities: game.activities,
|
||||
largestArmy: game.largestArmy,
|
||||
largestArmySize: game.largestArmySize,
|
||||
largestArmy: (game as any)["largestArmy"],
|
||||
largestArmySize: (game as any)["largestArmySize"],
|
||||
turn: game.turn,
|
||||
players: getFilteredPlayers(game),
|
||||
});
|
||||
|
@ -27,6 +27,9 @@ export interface Player {
|
||||
status?: string;
|
||||
developmentCards?: number;
|
||||
development?: DevelopmentCard[];
|
||||
turnNotice?: string;
|
||||
turnStart?: number;
|
||||
totalTime?: number;
|
||||
[key: string]: any; // allow incremental fields until fully typed
|
||||
}
|
||||
|
||||
@ -82,6 +85,7 @@ export interface Session {
|
||||
live?: boolean;
|
||||
lastActive?: number;
|
||||
keepAlive?: any;
|
||||
connected?: boolean;
|
||||
_initialSnapshotSent?: boolean;
|
||||
_getBatch?: { fields: Set<string>; timer?: any };
|
||||
_pendingMessage?: any;
|
||||
@ -107,6 +111,8 @@ export interface Game {
|
||||
players: Record<string, Player>;
|
||||
sessions: Record<string, Session>;
|
||||
unselected?: any[];
|
||||
turnTimer?: any;
|
||||
debug?: boolean;
|
||||
active?: number;
|
||||
rules?: any;
|
||||
step?: number;
|
||||
|
Loading…
x
Reference in New Issue
Block a user