Continuing...
This commit is contained in:
parent
6b4e5d1e58
commit
b9d7523800
@ -5,7 +5,6 @@ import "./PlayerList.css";
|
|||||||
import { MediaControl, MediaAgent, Peer } from "./MediaControl";
|
import { MediaControl, MediaAgent, Peer } from "./MediaControl";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import { GlobalContext } from "./GlobalContext";
|
import { GlobalContext } from "./GlobalContext";
|
||||||
import useWebSocket from "react-use-websocket";
|
|
||||||
|
|
||||||
type Player = {
|
type Player = {
|
||||||
name: string;
|
name: string;
|
||||||
@ -22,8 +21,8 @@ type Player = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const PlayerList: React.FC = () => {
|
const PlayerList: React.FC = () => {
|
||||||
const { session, socketUrl } = useContext(GlobalContext);
|
const { session, socketUrl, lastJsonMessage, sendJsonMessage } = useContext(GlobalContext);
|
||||||
const [Players, setPlayers] = useState<Player[] | null>(null);
|
const [players, setPlayers] = useState<Player[] | null>(null);
|
||||||
const [peers, setPeers] = useState<Record<string, Peer>>({});
|
const [peers, setPeers] = useState<Record<string, Peer>>({});
|
||||||
|
|
||||||
const sortPlayers = useCallback(
|
const sortPlayers = useCallback(
|
||||||
@ -55,82 +54,76 @@ const PlayerList: React.FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Use the WebSocket hook for room events with automatic reconnection
|
// Use the WebSocket hook for room events with automatic reconnection
|
||||||
const { sendJsonMessage } = useWebSocket(socketUrl, {
|
useEffect(() => {
|
||||||
share: true,
|
if (!lastJsonMessage) {
|
||||||
shouldReconnect: (closeEvent) => true, // Auto-reconnect on connection loss
|
return;
|
||||||
reconnectInterval: 5000,
|
}
|
||||||
onMessage: (event: MessageEvent) => {
|
const data: any = lastJsonMessage;
|
||||||
if (!session) {
|
switch (data.type) {
|
||||||
return;
|
case "players": {
|
||||||
}
|
type RoomStateData = {
|
||||||
const message = JSON.parse(event.data);
|
participants: Player[];
|
||||||
const data: any = message.data;
|
};
|
||||||
switch (message.type) {
|
const room_state = data as RoomStateData;
|
||||||
case "room_state": {
|
console.log(`Players - room_state`, room_state.participants);
|
||||||
type RoomStateData = {
|
room_state.participants.forEach((player) => {
|
||||||
participants: Player[];
|
player.local = player.session_id === session.id;
|
||||||
};
|
});
|
||||||
const room_state = data as RoomStateData;
|
room_state.participants.sort(sortPlayers);
|
||||||
console.log(`Players - room_state`, room_state.participants);
|
setPlayers(room_state.participants);
|
||||||
room_state.participants.forEach((Player) => {
|
// Initialize peers with remote mute/video state
|
||||||
Player.local = Player.session_id === session.id;
|
setPeers((prevPeers) => {
|
||||||
});
|
const updated: Record<string, Peer> = { ...prevPeers };
|
||||||
room_state.participants.sort(sortPlayers);
|
room_state.participants.forEach((player) => {
|
||||||
setPlayers(room_state.participants);
|
// Only update remote peers, never overwrite local peer object
|
||||||
// Initialize peers with remote mute/video state
|
if (!player.local && updated[player.session_id]) {
|
||||||
setPeers((prevPeers) => {
|
updated[player.session_id] = {
|
||||||
const updated: Record<string, Peer> = { ...prevPeers };
|
...updated[player.session_id],
|
||||||
room_state.participants.forEach((Player) => {
|
muted: player.muted ?? false,
|
||||||
// Only update remote peers, never overwrite local peer object
|
video_on: player.video_on ?? true,
|
||||||
if (!Player.local && updated[Player.session_id]) {
|
|
||||||
updated[Player.session_id] = {
|
|
||||||
...updated[Player.session_id],
|
|
||||||
muted: Player.muted ?? false,
|
|
||||||
video_on: Player.video_on ?? true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return updated;
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "update_name": {
|
|
||||||
// Update local session name immediately
|
|
||||||
if (data && typeof data.name === "string") {
|
|
||||||
session.name = data.name;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "peer_state_update": {
|
|
||||||
// Update peer state in peers, but do not override local mute
|
|
||||||
setPeers((prevPeers) => {
|
|
||||||
const updated = { ...prevPeers };
|
|
||||||
const peerId = data.peer_id;
|
|
||||||
if (peerId && updated[peerId]) {
|
|
||||||
updated[peerId] = {
|
|
||||||
...updated[peerId],
|
|
||||||
muted: data.muted,
|
|
||||||
video_on: data.video_on,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return updated;
|
|
||||||
});
|
});
|
||||||
break;
|
return updated;
|
||||||
}
|
});
|
||||||
default:
|
break;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
},
|
case "update_name": {
|
||||||
});
|
// Update local session name immediately
|
||||||
|
if (data && typeof data.name === "string") {
|
||||||
|
session.name = data.name;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "peer_state_update": {
|
||||||
|
// Update peer state in peers, but do not override local mute
|
||||||
|
setPeers((prevPeers) => {
|
||||||
|
const updated = { ...prevPeers };
|
||||||
|
const peerId = data.peer_id;
|
||||||
|
if (peerId && updated[peerId]) {
|
||||||
|
updated[peerId] = {
|
||||||
|
...updated[peerId],
|
||||||
|
muted: data.muted,
|
||||||
|
video_on: data.video_on,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return updated;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, [lastJsonMessage, session, sortPlayers, setPeers, setPlayers]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Players !== null) {
|
if (players !== null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sendJsonMessage({
|
sendJsonMessage({
|
||||||
type: "list_Players",
|
type: "list_Players",
|
||||||
});
|
});
|
||||||
}, [Players, sendJsonMessage]);
|
}, [players, sendJsonMessage]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ position: "relative", width: "100%" }}>
|
<Box sx={{ position: "relative", width: "100%" }}>
|
||||||
@ -144,17 +137,17 @@ const PlayerList: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<MediaAgent {...{ session, socketUrl, peers, setPeers }} />
|
<MediaAgent {...{ session, socketUrl, peers, setPeers }} />
|
||||||
<List className="PlayerSelector">
|
<List className="PlayerSelector">
|
||||||
{Players?.map((Player) => (
|
{players?.map((player) => (
|
||||||
<Box
|
<Box
|
||||||
key={Player.session_id}
|
key={player.session_id}
|
||||||
sx={{ display: "flex", flexDirection: "column", alignItems: "center" }}
|
sx={{ display: "flex", flexDirection: "column", alignItems: "center" }}
|
||||||
className={`PlayerEntry ${Player.local ? "PlayerSelf" : ""}`}
|
className={`PlayerEntry ${player.local ? "PlayerSelf" : ""}`}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Box style={{ display: "flex-wrap", alignItems: "center", justifyContent: "space-between" }}>
|
<Box style={{ display: "flex-wrap", alignItems: "center", justifyContent: "space-between" }}>
|
||||||
<Box style={{ display: "flex-wrap", alignItems: "center" }}>
|
<Box style={{ display: "flex-wrap", alignItems: "center" }}>
|
||||||
<div className="Name">{Player.name ? Player.name : Player.session_id}</div>
|
<div className="Name">{player.name ? player.name : player.session_id}</div>
|
||||||
{Player.protected && (
|
{player.protected && (
|
||||||
<div
|
<div
|
||||||
style={{ marginLeft: 8, fontSize: "0.8em", color: "#a00" }}
|
style={{ marginLeft: 8, fontSize: "0.8em", color: "#a00" }}
|
||||||
title="This name is protected with a password"
|
title="This name is protected with a password"
|
||||||
@ -162,26 +155,26 @@ const PlayerList: React.FC = () => {
|
|||||||
🔒
|
🔒
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{Player.bot_instance_id && (
|
{player.bot_instance_id && (
|
||||||
<div style={{ marginLeft: 8, fontSize: "0.8em", color: "#00a" }} title="This is a bot">
|
<div style={{ marginLeft: 8, fontSize: "0.8em", color: "#00a" }} title="This is a bot">
|
||||||
🤖
|
🤖
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{Player.name && !Player.live && <div className="NoNetwork"></div>}
|
{player.name && !player.live && <div className="NoNetwork"></div>}
|
||||||
</Box>
|
</Box>
|
||||||
{Player.name && Player.live && peers[Player.session_id] && (Player.local || Player.has_media !== false) ? (
|
{player.name && player.live && peers[player.session_id] && (player.local || player.has_media !== false) ? (
|
||||||
<MediaControl
|
<MediaControl
|
||||||
className="Medium"
|
className="Medium"
|
||||||
key={Player.session_id}
|
key={player.session_id}
|
||||||
peer={peers[Player.session_id]}
|
peer={peers[player.session_id]}
|
||||||
isSelf={Player.local}
|
isSelf={player.local}
|
||||||
sendJsonMessage={Player.local ? sendJsonMessage : undefined}
|
sendJsonMessage={player.local ? sendJsonMessage : undefined}
|
||||||
remoteAudioMuted={peers[Player.session_id].muted}
|
remoteAudioMuted={peers[player.session_id].muted}
|
||||||
remoteVideoOff={peers[Player.session_id].video_on === false}
|
remoteVideoOff={peers[player.session_id].video_on === false}
|
||||||
/>
|
/>
|
||||||
) : Player.name && Player.live && Player.has_media === false ? (
|
) : player.name && player.live && player.has_media === false ? (
|
||||||
<div
|
<div
|
||||||
className="Video fade-in"
|
className="Video fade-in"
|
||||||
style={{
|
style={{
|
||||||
|
@ -26,7 +26,16 @@ const router = express.Router();
|
|||||||
// normalizeIncoming imported from './games/utils'
|
// normalizeIncoming imported from './games/utils'
|
||||||
|
|
||||||
import { initGameDB } from "./games/store";
|
import { initGameDB } from "./games/store";
|
||||||
import { addActivity, addChatMessage, getNextPlayerSession } from "./games/helpers";
|
import {
|
||||||
|
addActivity,
|
||||||
|
addChatMessage,
|
||||||
|
getNextPlayerSession,
|
||||||
|
clearPlayer,
|
||||||
|
canGiveBuilding,
|
||||||
|
setForRoadPlacement,
|
||||||
|
setForCityPlacement,
|
||||||
|
setForSettlementPlacement,
|
||||||
|
} from "./games/helpers";
|
||||||
import type { GameDB } from "./games/store";
|
import type { GameDB } from "./games/store";
|
||||||
|
|
||||||
let gameDB: GameDB | undefined;
|
let gameDB: GameDB | undefined;
|
||||||
@ -767,24 +776,6 @@ const loadGame = async (id: string) => {
|
|||||||
return game;
|
return game;
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearPlayer = (player: any) => {
|
|
||||||
const color = player.color;
|
|
||||||
for (let key in player) {
|
|
||||||
delete player[key];
|
|
||||||
}
|
|
||||||
Object.assign(player, newPlayer(color));
|
|
||||||
};
|
|
||||||
|
|
||||||
const canGiveBuilding = (game: any): string | undefined => {
|
|
||||||
if (!game.turn.roll) {
|
|
||||||
return `Admin cannot give a building until the dice have been rolled.`;
|
|
||||||
}
|
|
||||||
if (game.turn.actions && game.turn.actions.length !== 0) {
|
|
||||||
return `Admin cannot give a building while other actions in play: ${game.turn.actions.join(", ")}.`;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
const adminCommands = (game: any, action: string, value: string, query: any): any => {
|
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;
|
let color: string | undefined, parts: RegExpMatchArray | null, session: any, corners: any, corner: any, error: any;
|
||||||
void color;
|
void color;
|
||||||
@ -1318,7 +1309,6 @@ const setPlayerColor = (game: any, session: any, color: string): string | undefi
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const processCorner = (game: Game, color: string, cornerIndex: number, placedCorner: CornerPlacement): number => {
|
const processCorner = (game: Game, color: string, cornerIndex: number, placedCorner: CornerPlacement): number => {
|
||||||
/* If this corner is allocated and isn't assigned to the walking color, skip it */
|
/* If this corner is allocated and isn't assigned to the walking color, skip it */
|
||||||
if (placedCorner.color && placedCorner.color !== color) {
|
if (placedCorner.color && placedCorner.color !== color) {
|
||||||
@ -1804,21 +1794,7 @@ const offerToString = (offer: any): string => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setForRoadPlacement = (game: Game, limits: any): void => {
|
|
||||||
game.turn.actions = ["place-road"];
|
|
||||||
game.turn.limits = { roads: limits };
|
|
||||||
};
|
|
||||||
|
|
||||||
const setForCityPlacement = (game: Game, limits: any): void => {
|
|
||||||
game.turn.actions = ["place-city"];
|
|
||||||
game.turn.limits = { corners: limits };
|
|
||||||
};
|
|
||||||
|
|
||||||
const setForSettlementPlacement = (game: Game, limits: number[] | undefined, _extra?: any): void => {
|
|
||||||
// limits: array of valid corner indices
|
|
||||||
game.turn.actions = ["place-settlement"];
|
|
||||||
game.turn.limits = { corners: limits };
|
|
||||||
};
|
|
||||||
|
|
||||||
router.put("/:id/:action/:value?", async (req, res) => {
|
router.put("/:id/:action/:value?", async (req, res) => {
|
||||||
const { action, id } = req.params,
|
const { action, id } = req.params,
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
export const addActivity = (game: any, session: any, message: string): void => {
|
import type { Game, Session, Player } from "./types";
|
||||||
|
|
||||||
|
export const addActivity = (game: Game, session: Session | null, message: string): void => {
|
||||||
let date = Date.now();
|
let date = Date.now();
|
||||||
|
if (!game.activities) game.activities = [] as any[];
|
||||||
if (game.activities.length && game.activities[game.activities.length - 1].date === date) {
|
if (game.activities.length && game.activities[game.activities.length - 1].date === date) {
|
||||||
date++;
|
date++;
|
||||||
}
|
}
|
||||||
@ -9,9 +12,10 @@ export const addActivity = (game: any, session: any, message: string): void => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addChatMessage = (game: any, session: any, message: string, isNormalChat?: boolean) => {
|
export const addChatMessage = (game: Game, session: Session | null, message: string, isNormalChat?: boolean) => {
|
||||||
let now = Date.now();
|
let now = Date.now();
|
||||||
let lastTime = 0;
|
let lastTime = 0;
|
||||||
|
if (!game.chat) game.chat = [] as any[];
|
||||||
if (game.chat.length) {
|
if (game.chat.length) {
|
||||||
lastTime = game.chat[game.chat.length - 1].date;
|
lastTime = game.chat[game.chat.length - 1].date;
|
||||||
}
|
}
|
||||||
@ -38,71 +42,151 @@ export const addChatMessage = (game: any, session: any, message: string, isNorma
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getColorFromName = (game: any, name: string): string => {
|
export const getColorFromName = (game: Game, name: string): string => {
|
||||||
for (let id in game.sessions) {
|
for (let id in game.sessions) {
|
||||||
if (game.sessions[id].name === name) {
|
const s = game.sessions[id];
|
||||||
return game.sessions[id].color;
|
if (s && s.name === name) {
|
||||||
|
return s.color || "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLastPlayerName = (game: any): string => {
|
export const getLastPlayerName = (game: Game): string => {
|
||||||
let index = game.playerOrder.length - 1;
|
const index = (game.playerOrder || []).length - 1;
|
||||||
|
const color = (game.playerOrder || [])[index];
|
||||||
|
if (!color) return "";
|
||||||
for (let id in game.sessions) {
|
for (let id in game.sessions) {
|
||||||
if (game.sessions[id].color === game.playerOrder[index]) {
|
const s = game.sessions[id];
|
||||||
return game.sessions[id].name;
|
if (s && s.color === color) {
|
||||||
|
return s.name || "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getFirstPlayerName = (game: any): string => {
|
export const getFirstPlayerName = (game: Game): string => {
|
||||||
let index = 0;
|
const color = (game.playerOrder || [])[0];
|
||||||
|
if (!color) return "";
|
||||||
for (let id in game.sessions) {
|
for (let id in game.sessions) {
|
||||||
if (game.sessions[id].color === game.playerOrder[index]) {
|
const s = game.sessions[id];
|
||||||
return game.sessions[id].name;
|
if (s && s.color === color) {
|
||||||
|
return s.name || "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getNextPlayerSession = (game: any, name: string): any => {
|
export const getNextPlayerSession = (game: Game, name: string): Session | undefined => {
|
||||||
let color;
|
let color: string | undefined;
|
||||||
for (let id in game.sessions) {
|
for (let id in game.sessions) {
|
||||||
if (game.sessions[id].name === name) {
|
const s = game.sessions[id];
|
||||||
color = game.sessions[id].color;
|
if (s && s.name === name) {
|
||||||
|
color = s.color;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!color) return undefined;
|
||||||
|
|
||||||
let index = game.playerOrder.indexOf(color);
|
const order = game.playerOrder || [];
|
||||||
index = (index + 1) % game.playerOrder.length;
|
let index = order.indexOf(color);
|
||||||
color = game.playerOrder[index];
|
if (index === -1) return undefined;
|
||||||
|
index = (index + 1) % order.length;
|
||||||
|
const nextColor = order[index];
|
||||||
for (let id in game.sessions) {
|
for (let id in game.sessions) {
|
||||||
if (game.sessions[id].color === color) {
|
const s = game.sessions[id];
|
||||||
return game.sessions[id];
|
if (s && s.color === nextColor) {
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.error(`getNextPlayerSession -- no player found!`);
|
console.error(`getNextPlayerSession -- no player found!`);
|
||||||
console.log(game.players);
|
console.log(game.players);
|
||||||
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPrevPlayerSession = (game: any, name: string): any => {
|
export const getPrevPlayerSession = (game: Game, name: string): Session | undefined => {
|
||||||
let color;
|
let color: string | undefined;
|
||||||
for (let id in game.sessions) {
|
for (let id in game.sessions) {
|
||||||
if (game.sessions[id].name === name) {
|
const s = game.sessions[id];
|
||||||
color = game.sessions[id].color;
|
if (s && s.name === name) {
|
||||||
|
color = s.color;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let index = game.playerOrder.indexOf(color);
|
if (!color) return undefined;
|
||||||
index = (index - 1) % game.playerOrder.length;
|
const order = game.playerOrder || [];
|
||||||
|
let index = order.indexOf(color);
|
||||||
|
if (index === -1) return undefined;
|
||||||
|
index = (index - 1 + order.length) % order.length;
|
||||||
|
const prevColor = order[index];
|
||||||
for (let id in game.sessions) {
|
for (let id in game.sessions) {
|
||||||
if (game.sessions[id].color === game.playerOrder[index]) {
|
const s = game.sessions[id];
|
||||||
return game.sessions[id];
|
if (s && s.color === prevColor) {
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.error(`getNextPlayerSession -- no player found!`);
|
console.error(`getPrevPlayerSession -- no player found!`);
|
||||||
console.log(game.players);
|
console.log(game.players);
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clearPlayer = (player: Player) => {
|
||||||
|
const color = player.color;
|
||||||
|
for (let key in player) {
|
||||||
|
// delete all runtime fields
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const canGiveBuilding = (game: Game): string | undefined => {
|
||||||
|
if (!game.turn.roll) {
|
||||||
|
return `Admin cannot give a building until the dice have been rolled.`;
|
||||||
|
}
|
||||||
|
if (game.turn.actions && game.turn.actions.length !== 0) {
|
||||||
|
return `Admin cannot give a building while other actions in play: ${game.turn.actions.join(", ")}.`;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setForRoadPlacement = (game: Game, limits: any): void => {
|
||||||
|
game.turn.actions = ["place-road"];
|
||||||
|
game.turn.limits = { roads: limits };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setForCityPlacement = (game: Game, limits: any): void => {
|
||||||
|
game.turn.actions = ["place-city"];
|
||||||
|
game.turn.limits = { corners: limits };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setForSettlementPlacement = (game: Game, limits: number[] | undefined, _extra?: any): void => {
|
||||||
|
game.turn.actions = ["place-settlement"];
|
||||||
|
game.turn.limits = { corners: limits };
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user