Continue refactoring
This commit is contained in:
parent
b9d7523800
commit
4d061a8054
@ -43,7 +43,7 @@ const empty: Resources = {
|
||||
};
|
||||
|
||||
const Trade: React.FC = () => {
|
||||
const { ws, sendJsonMessage } = useContext(GlobalContext);
|
||||
const { sendJsonMessage, lastJsonMessage } = useContext(GlobalContext);
|
||||
const [gives, setGives] = useState<Resources>(Object.assign({}, empty));
|
||||
const [gets, setGets] = useState<Resources>(Object.assign({}, empty));
|
||||
const [turn, setTurn] = useState<any>(undefined);
|
||||
@ -53,8 +53,11 @@ const Trade: React.FC = () => {
|
||||
|
||||
const fields = useMemo(() => ["turn", "players", "private", "color"], []);
|
||||
|
||||
const onWsMessage = (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data);
|
||||
useEffect(() => {
|
||||
if (!lastJsonMessage) {
|
||||
return;
|
||||
}
|
||||
const data = lastJsonMessage;
|
||||
switch (data.type) {
|
||||
case "game-update":
|
||||
console.log(`trade - game-update: `, data.update);
|
||||
@ -74,21 +77,8 @@ const Trade: React.FC = () => {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const refWsMessage = useRef(onWsMessage);
|
||||
useEffect(() => {
|
||||
refWsMessage.current = onWsMessage;
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!ws) {
|
||||
return;
|
||||
}
|
||||
const cbMessage = (e: MessageEvent) => refWsMessage.current(e);
|
||||
ws.addEventListener("message", cbMessage);
|
||||
return () => {
|
||||
ws.removeEventListener("message", cbMessage);
|
||||
};
|
||||
}, [ws, refWsMessage]);
|
||||
}, [lastJsonMessage, turn, players, priv, color]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!sendJsonMessage) {
|
||||
return;
|
||||
@ -98,6 +88,7 @@ const Trade: React.FC = () => {
|
||||
fields,
|
||||
});
|
||||
}, [sendJsonMessage, fields]);
|
||||
|
||||
const transfer = useCallback(
|
||||
(type: string, direction: string) => {
|
||||
if (direction === "give") {
|
||||
|
@ -7,7 +7,6 @@ import basePath from "../basepath";
|
||||
import {
|
||||
MAX_SETTLEMENTS,
|
||||
MAX_CITIES,
|
||||
MAX_ROADS,
|
||||
types,
|
||||
debug,
|
||||
all,
|
||||
@ -18,6 +17,7 @@ import {
|
||||
|
||||
import { getValidRoads, getValidCorners, isRuleEnabled } from "../util/validLocations";
|
||||
import { Player, Game, Session, CornerPlacement, RoadPlacement } 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
|
||||
|
||||
@ -607,31 +607,7 @@ const processRoll = (game: Game, session: Session, dice: number[]): any => {
|
||||
});
|
||||
};
|
||||
|
||||
const newPlayer = (color: string) => {
|
||||
return {
|
||||
roads: MAX_ROADS,
|
||||
cities: MAX_CITIES,
|
||||
settlements: MAX_SETTLEMENTS,
|
||||
points: 0,
|
||||
status: "Not active",
|
||||
lastActive: 0,
|
||||
resources: 0,
|
||||
order: 0,
|
||||
stone: 0,
|
||||
wheat: 0,
|
||||
sheep: 0,
|
||||
wood: 0,
|
||||
brick: 0,
|
||||
army: 0,
|
||||
development: [],
|
||||
color: color,
|
||||
name: "",
|
||||
totalTime: 0,
|
||||
turnStart: 0,
|
||||
ports: 0,
|
||||
developmentCards: 0,
|
||||
};
|
||||
};
|
||||
// newPlayer is provided by ./games/playerFactory
|
||||
|
||||
const getSession = (game: Game, id: string) => {
|
||||
if (!game.sessions) {
|
||||
@ -993,6 +969,10 @@ const adminCommands = (game: any, action: string, value: string, query: any): an
|
||||
case "pass":
|
||||
let name = game.turn.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,
|
||||
color: next.color,
|
||||
@ -1193,10 +1173,11 @@ const colorToWord = (color: string): string => {
|
||||
}
|
||||
};
|
||||
|
||||
const getActiveCount = (game: any): number => {
|
||||
const getActiveCount = (game: Game): number => {
|
||||
let active = 0;
|
||||
for (let color in game.players) {
|
||||
if (!game.players[color].name) {
|
||||
const p = game.players[color];
|
||||
if (!p || !p.name) {
|
||||
continue;
|
||||
}
|
||||
active++;
|
||||
@ -1204,7 +1185,7 @@ const getActiveCount = (game: any): number => {
|
||||
return active;
|
||||
};
|
||||
|
||||
const setPlayerColor = (game: any, session: any, color: string): string | undefined => {
|
||||
const setPlayerColor = (game: Game, session: Session, color: string): string | undefined => {
|
||||
/* Selecting the same color is a NO-OP */
|
||||
if (session.color === color) {
|
||||
return;
|
||||
@ -1225,8 +1206,14 @@ const setPlayerColor = (game: any, session: any, color: string): string | undefi
|
||||
}
|
||||
|
||||
/* Verify selection is not already taken */
|
||||
if (color && game.players[color].status !== "Not active") {
|
||||
return `${game.players[color].name} already has ${colorToWord(color)}`;
|
||||
if (color) {
|
||||
const candidate = game.players[color];
|
||||
if (!candidate) {
|
||||
return `An invalid player selection was attempted.`;
|
||||
}
|
||||
if (candidate.status !== "Not active") {
|
||||
return `${candidate.name} already has ${colorToWord(color)}`;
|
||||
}
|
||||
}
|
||||
|
||||
let active = getActiveCount(game);
|
||||
@ -1234,14 +1221,17 @@ const setPlayerColor = (game: any, session: any, color: string): string | undefi
|
||||
if (session.player) {
|
||||
/* Deselect currently active player for this session */
|
||||
clearPlayer(session.player);
|
||||
session.player = undefined;
|
||||
// remove the player association
|
||||
delete (session as any).player;
|
||||
const old_color = session.color;
|
||||
session.color = "";
|
||||
active--;
|
||||
|
||||
/* If the player is not selecting a color, then return */
|
||||
if (!color) {
|
||||
addChatMessage(game, null, `${session.name} is no longer ${colorToWord(old_color)}.`);
|
||||
const msg = String(session.name || "") + " is no longer " + String(colorToWord(String(old_color)));
|
||||
addChatMessage(game, null, msg);
|
||||
if (!game.unselected) game.unselected = [] as any[];
|
||||
game.unselected.push(session);
|
||||
game.active = active;
|
||||
if (active === 1) {
|
||||
@ -1267,11 +1257,14 @@ const setPlayerColor = (game: any, session: any, color: string): string | undefi
|
||||
active++;
|
||||
session.color = color;
|
||||
session.live = true;
|
||||
session.player = game.players[color];
|
||||
session.player.name = session.name;
|
||||
session.player.status = `Active`;
|
||||
session.player.lastActive = Date.now();
|
||||
session.player.live = true;
|
||||
const picked = game.players[color];
|
||||
if (picked) {
|
||||
(session as any).player = picked;
|
||||
picked.name = session.name;
|
||||
picked.status = `Active`;
|
||||
picked.lastActive = Date.now();
|
||||
picked.live = true;
|
||||
}
|
||||
addChatMessage(game, session, `${session.name} has chosen to play as ${colorToWord(color)}.`);
|
||||
|
||||
const update: any = {
|
||||
@ -1282,15 +1275,19 @@ const setPlayerColor = (game: any, session: any, color: string): string | undefi
|
||||
/* Rebuild the unselected list */
|
||||
const unselected = [];
|
||||
for (let id in game.sessions) {
|
||||
if (!game.sessions[id].color && game.sessions[id].name) {
|
||||
unselected.push(game.sessions[id]);
|
||||
const s = game.sessions[id];
|
||||
if (!s) continue;
|
||||
if (!s.color && s.name) {
|
||||
unselected.push(s);
|
||||
}
|
||||
}
|
||||
if (!game.unselected) game.unselected = [] as any[];
|
||||
if (unselected.length !== game.unselected.length) {
|
||||
game.unselected = unselected;
|
||||
update.unselected = getFilteredUnselected(game);
|
||||
}
|
||||
|
||||
if (!game.active) game.active = 0;
|
||||
if (game.active !== active) {
|
||||
if (game.active < 2 && active >= 2) {
|
||||
addChatMessage(game, null, `There are now enough players to start the game.`);
|
||||
@ -1794,8 +1791,6 @@ const offerToString = (offer: any): string => {
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
router.put("/:id/:action/:value?", async (req, res) => {
|
||||
const { action, id } = req.params,
|
||||
value = req.params.value ? req.params.value : "";
|
||||
@ -2140,13 +2135,18 @@ const pass = (game: any, session: any): string | undefined => {
|
||||
}
|
||||
|
||||
const next = getNextPlayerSession(game, session.name);
|
||||
if (!next) {
|
||||
return `Unable to find the next player to pass to.`;
|
||||
}
|
||||
session.player.totalTime += Date.now() - session.player.turnStart;
|
||||
session.player.turnNotice = "";
|
||||
game.turn = {
|
||||
name: next.name,
|
||||
color: next.color,
|
||||
};
|
||||
next.player.turnStart = Date.now();
|
||||
if (next.player) {
|
||||
(next.player as any)["turnStart"] = Date.now();
|
||||
}
|
||||
startTurnTimer(game, next);
|
||||
game.turns++;
|
||||
addActivity(game, session, `${name} passed their turn.`);
|
||||
@ -2528,8 +2528,9 @@ const playCard = (game: any, session: any, card: any): string | undefined => {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const placeSettlement = (game: any, session: any, index: any): string | undefined => {
|
||||
const player = session.player;
|
||||
const placeSettlement = (game: Game, session: Session, index: number | string): string | undefined => {
|
||||
if (!session.player) return `You are not playing a player.`;
|
||||
const player: any = session.player;
|
||||
if (typeof index === "string") index = parseInt(index);
|
||||
|
||||
if (game.state !== "initial-placement" && game.state !== "normal") {
|
||||
@ -2560,26 +2561,26 @@ const placeSettlement = (game: any, session: any, index: any): string | undefine
|
||||
|
||||
if (game.state === "normal") {
|
||||
if (!game.turn.free) {
|
||||
if (player.brick < 1 || player.wood < 1 || player.wheat < 1 || player.sheep < 1) {
|
||||
if ((player.brick || 0) < 1 || (player.wood || 0) < 1 || (player.wheat || 0) < 1 || (player.sheep || 0) < 1) {
|
||||
return `You have insufficient resources to build a settlement.`;
|
||||
}
|
||||
}
|
||||
|
||||
if (player.settlements < 1) {
|
||||
if ((player.settlements || 0) < 1) {
|
||||
return `You have already built all of your settlements.`;
|
||||
}
|
||||
|
||||
player.settlements--;
|
||||
player.settlements = (player.settlements || 0) - 1;
|
||||
|
||||
if (!game.turn.free) {
|
||||
addChatMessage(game, session, `${session.name} spent 1 brick, 1 wood, 1 sheep, 1 wheat to purchase a settlement.`);
|
||||
player.brick--;
|
||||
player.wood--;
|
||||
player.wheat--;
|
||||
player.sheep--;
|
||||
player.brick = (player.brick || 0) - 1;
|
||||
player.wood = (player.wood || 0) - 1;
|
||||
player.wheat = (player.wheat || 0) - 1;
|
||||
player.sheep = (player.sheep || 0) - 1;
|
||||
player.resources = 0;
|
||||
["wheat", "brick", "sheep", "stone", "wood"].forEach((resource) => {
|
||||
player.resources += player[resource];
|
||||
player.resources += player[resource] || 0;
|
||||
});
|
||||
}
|
||||
delete game.turn.free;
|
||||
@ -2648,7 +2649,7 @@ const placeSettlement = (game: any, session: any, index: any): string | undefine
|
||||
player.ports++;
|
||||
});
|
||||
}
|
||||
player.settlements--;
|
||||
player.settlements = (player.settlements || 0) - 1;
|
||||
if (bankType) {
|
||||
addActivity(
|
||||
game,
|
||||
@ -5140,6 +5141,17 @@ const createGame = async (id: any) => {
|
||||
},
|
||||
sessions: {},
|
||||
unselected: [],
|
||||
placements: {
|
||||
corners: [],
|
||||
roads: [],
|
||||
},
|
||||
turn: {
|
||||
name: "",
|
||||
color: "",
|
||||
actions: [],
|
||||
limits: {},
|
||||
roll: 0,
|
||||
},
|
||||
rules: {
|
||||
"victory-points": {
|
||||
points: 10,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { Game, Session, Player } from "./types";
|
||||
import { newPlayer } from "./playerFactory";
|
||||
|
||||
export const addActivity = (game: Game, session: Session | null, message: string): void => {
|
||||
let date = Date.now();
|
||||
@ -138,32 +139,8 @@ export const clearPlayer = (player: Player) => {
|
||||
delete (player as any)[key];
|
||||
}
|
||||
|
||||
// Inline minimal newPlayer factory to avoid circular import at runtime
|
||||
const base = {
|
||||
roads: 15,
|
||||
cities: 4,
|
||||
settlements: 5,
|
||||
points: 0,
|
||||
status: "Not active",
|
||||
lastActive: 0,
|
||||
resources: 0,
|
||||
order: 0,
|
||||
stone: 0,
|
||||
wheat: 0,
|
||||
sheep: 0,
|
||||
wood: 0,
|
||||
brick: 0,
|
||||
army: 0,
|
||||
development: [],
|
||||
color: color,
|
||||
name: "",
|
||||
totalTime: 0,
|
||||
turnStart: 0,
|
||||
ports: 0,
|
||||
developmentCards: 0,
|
||||
} as Player;
|
||||
|
||||
Object.assign(player, base);
|
||||
// Use shared factory to ensure a single source of defaults
|
||||
Object.assign(player, newPlayer(color || ""));
|
||||
};
|
||||
|
||||
export const canGiveBuilding = (game: Game): string | undefined => {
|
||||
|
30
server/routes/games/playerFactory.ts
Normal file
30
server/routes/games/playerFactory.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { MAX_ROADS, MAX_CITIES, MAX_SETTLEMENTS } from "./constants";
|
||||
import type { Player } from "./types";
|
||||
|
||||
export const newPlayer = (color: string): Player => {
|
||||
return {
|
||||
roads: MAX_ROADS,
|
||||
cities: MAX_CITIES,
|
||||
settlements: MAX_SETTLEMENTS,
|
||||
points: 0,
|
||||
status: "Not active",
|
||||
lastActive: 0,
|
||||
resources: 0,
|
||||
order: 0,
|
||||
stone: 0,
|
||||
wheat: 0,
|
||||
sheep: 0,
|
||||
wood: 0,
|
||||
brick: 0,
|
||||
army: 0,
|
||||
development: [],
|
||||
color: color,
|
||||
name: "",
|
||||
totalTime: 0,
|
||||
turnStart: 0,
|
||||
ports: 0,
|
||||
developmentCards: 0,
|
||||
} as Player;
|
||||
};
|
||||
|
||||
export default newPlayer;
|
Loading…
x
Reference in New Issue
Block a user