Add sheeps
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
parent
6a861e7841
commit
588777af90
BIN
client/public/assets/gfx/sheep.png
Executable file
BIN
client/public/assets/gfx/sheep.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
@ -28,10 +28,12 @@ import { Winner } from "./Winner.js";
|
||||
import { HouseRules } from "./HouseRules.js";
|
||||
import { Dice } from "./Dice.js";
|
||||
import { assetsPath } from "./Common.js";
|
||||
import { Sheep } from "./Sheep.js";
|
||||
|
||||
import history from "./history.js";
|
||||
import "./App.css";
|
||||
import equal from "fast-deep-equal";
|
||||
import { purple } from "@material-ui/core/colors";
|
||||
/*
|
||||
const Pip = () => {
|
||||
<div className="Pip"
|
||||
|
@ -50,10 +50,6 @@ const Bird = ({ origin, radius, speed, size, style }) => {
|
||||
setDirection(Math.floor(birdAngles * newAngle / 360.));
|
||||
}, [time, setCell, speed, rotation, setDirection]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
}, [angle]);
|
||||
|
||||
return <div className={`Bird`}
|
||||
style={{
|
||||
top: `${50 + 100 * radius * Math.sin(2 * Math.PI * (180 + angle) / 360.)}%`,
|
||||
|
@ -4,6 +4,7 @@ import { assetsPath } from "./Common.js";
|
||||
import "./Board.css";
|
||||
import { GlobalContext } from "./GlobalContext.js";
|
||||
import { Flock } from "./Bird.js";
|
||||
import { Herd } from "./Sheep.js";
|
||||
|
||||
const rows = [3, 4, 5, 4, 3, 2]; /* The final row of 2 is to place roads and corners */
|
||||
|
||||
@ -546,6 +547,17 @@ const Board = () => {
|
||||
}}/><Tile
|
||||
tile={tile}
|
||||
/></div>;
|
||||
} else if (tile.type === 'sheep') {
|
||||
div = <div key={`tile-${order}`}>
|
||||
<Herd count={Math.floor(1 + animationSeeds[index] * 4)}
|
||||
style={{
|
||||
top: `${tile.top - tileImageHeight * 0.5}px`,
|
||||
left: `${tile.left - tileImageWidth * 0.5}px`,
|
||||
width: `${tileImageWidth}px`,
|
||||
height: `${tileImageHeight}px`
|
||||
}} /><Tile
|
||||
tile={tile}
|
||||
/></div>;
|
||||
} else {
|
||||
div = <Tile
|
||||
key={`tile-${order}`}
|
||||
|
12
client/src/Sheep.css
Normal file
12
client/src/Sheep.css
Normal file
@ -0,0 +1,12 @@
|
||||
.Herd {
|
||||
z-index: 25; /* below pips */
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.Sheep {
|
||||
z-index: 50; /* Above Tile (5,10), Road (12), Corner (20) */
|
||||
position: absolute;
|
||||
background-size: 1200% auto;
|
||||
background-repeat: no-repeat;
|
||||
}
|
107
client/src/Sheep.js
Normal file
107
client/src/Sheep.js
Normal file
@ -0,0 +1,107 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { assetsPath } from "./Common.js";
|
||||
import "./Sheep.css";
|
||||
|
||||
const
|
||||
sheepSteps = 12;
|
||||
|
||||
const useAnimationFrame = callback => {
|
||||
// Use useRef for mutable variables that we want to persist
|
||||
// without triggering a re-render on their change
|
||||
const requestRef = React.useRef();
|
||||
|
||||
const animate = time => {
|
||||
callback(time)
|
||||
requestRef.current = requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
requestRef.current = requestAnimationFrame(animate);
|
||||
return () => cancelAnimationFrame(requestRef.current);
|
||||
}, []); // Make sure the effect runs only once
|
||||
}
|
||||
|
||||
const Sheep = ({ radius, speed, size, style }) => {
|
||||
const [time, setTime] = useState(0);
|
||||
const [direction, setDirection] = useState(Math.random() * 2 * Math.PI);
|
||||
const [y, setY] = useState((Math.random() - 0.5) * radius);
|
||||
const [frame, setFrame] = useState(0);
|
||||
const [x, setX] = useState((Math.random() - 0.5) * radius);
|
||||
|
||||
const previousTimeRef = React.useRef();
|
||||
|
||||
useAnimationFrame(time => {
|
||||
if (previousTimeRef.current !== undefined) {
|
||||
const deltaTime = time - previousTimeRef.current;
|
||||
previousTimeRef.current = time;
|
||||
setTime(deltaTime);
|
||||
} else {
|
||||
previousTimeRef.current = time;
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
let alpha = time / speed;
|
||||
const sheepSpeed = 0.05;
|
||||
if (alpha > 1.0) {
|
||||
alpha = 0.1;
|
||||
}
|
||||
let newX = x + sheepSpeed * Math.sin(direction) * alpha,
|
||||
newY = y + sheepSpeed * Math.cos(direction) * alpha;
|
||||
if (Math.sqrt((newX * newX) + (newY * newY)) > Math.sqrt(radius * radius)) {
|
||||
let newDirection = direction + Math.PI + 0.5 * (Math.random() - 0.5) * Math.PI;
|
||||
while (newDirection >= 2 * Math.PI) {
|
||||
newDirection -= 2 * Math.PI;
|
||||
}
|
||||
while (newDirection <= -2 * Math.PI) {
|
||||
newDirection += 2 * Math.PI;
|
||||
}
|
||||
setDirection(newDirection);
|
||||
newX += sheepSpeed * Math.sin(newDirection) * alpha;
|
||||
newY += sheepSpeed * Math.cos(newDirection) * alpha;
|
||||
}
|
||||
setX(newX);
|
||||
setY(newY);
|
||||
setFrame(frame + sheepSteps * alpha);
|
||||
}, [time, speed, setDirection]);
|
||||
|
||||
const cell = Math.floor(frame) % sheepSteps;
|
||||
|
||||
return <div className={`Sheep`}
|
||||
style={{
|
||||
top: `${Math.floor(50 + 50 * y)}%`,
|
||||
left: `${Math.floor(50 + 50 * x)}%`,
|
||||
width: `${size * 60}px`,
|
||||
height: `${size * 52}px`,
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundImage: `url(${assetsPath}/gfx/sheep.png)`,
|
||||
backgroundPositionX: `${100. * cell / (sheepSteps - 1)}%`,
|
||||
transformOrigin: `50% 50%`,
|
||||
transform: `translate(-50%, -50%) scale(${Math.sin(direction) > 0 ? +1 : -1}, 1)`,
|
||||
...style
|
||||
}}
|
||||
></div>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
const Herd = ({count, style}) => {
|
||||
const [sheep, setSheep] = useState([]);
|
||||
useEffect(() => {
|
||||
const tmp = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
const scalar = Math.random();
|
||||
tmp.push(<Sheep
|
||||
speed={1000. + 500 * scalar}
|
||||
size={0.25}
|
||||
radius={0.8}
|
||||
key={i}
|
||||
/>)
|
||||
}
|
||||
setSheep(tmp);
|
||||
}, [count, setSheep]);
|
||||
|
||||
return <div className="Herd" style={style}>{ sheep }</div>;
|
||||
};
|
||||
|
||||
export { Sheep, Herd };
|
BIN
original/sheep-alpha.xcf
Executable file
BIN
original/sheep-alpha.xcf
Executable file
Binary file not shown.
BIN
original/sheep.png
Executable file
BIN
original/sheep.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
303
server/ai/app.js
303
server/ai/app.js
@ -1,5 +1,6 @@
|
||||
const fetch = require('node-fetch');
|
||||
const WebSocket = require('ws');
|
||||
const fs = require('fs').promises;
|
||||
|
||||
const version = '0.0.1';
|
||||
|
||||
@ -18,7 +19,7 @@ For example:
|
||||
const server = process.argv[2];
|
||||
const gameId = process.argv[3];
|
||||
let session = undefined;
|
||||
const user = process.argv[4];
|
||||
const name = process.argv[4];
|
||||
|
||||
const game = {};
|
||||
|
||||
@ -30,19 +31,29 @@ const error = (e) => {
|
||||
|
||||
const connect = async () => {
|
||||
let loc = new URL(server), new_uri;
|
||||
const res = await fetch(`${server}/api/v1/games`, {
|
||||
method: 'GET',
|
||||
cache: 'no-cache',
|
||||
credentials: 'same-origin', /* include cookies */
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
let player;
|
||||
try {
|
||||
const data = JSON.parse(await fs.readFile(`${name}.json`, 'utf-8'));
|
||||
player = data.player;
|
||||
} catch (_) {
|
||||
const res = await fetch(`${server}/api/v1/games`, {
|
||||
method: 'GET',
|
||||
cache: 'no-cache',
|
||||
credentials: 'same-origin', /* include cookies */
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
if (!res) {
|
||||
throw new Error(`Unable to connect to ${server}`);
|
||||
}
|
||||
});
|
||||
if (!res) {
|
||||
throw new Error(`Unable to connect to ${server}`);
|
||||
}
|
||||
|
||||
const { player } = JSON.parse(await res.text());
|
||||
player = JSON.parse(await res.text()).player;
|
||||
await fs.writeFile(`${name}.json`, JSON.stringify({
|
||||
name,
|
||||
player
|
||||
}));
|
||||
}
|
||||
console.log(`Connecting to ${server} as ${player}`);
|
||||
|
||||
if (loc.protocol === "https:") {
|
||||
@ -68,52 +79,264 @@ const connect = async () => {
|
||||
resolve(ws);
|
||||
};
|
||||
|
||||
const connection = (ws) => {
|
||||
console.log("connection request cookie: ", ws.upgradeReq.headers.cookie);
|
||||
};
|
||||
|
||||
const close = (e) => {
|
||||
console.log(`ws - close`);
|
||||
};
|
||||
|
||||
ws.on('open', open);
|
||||
ws.on('connect', () => { connect(ws); });
|
||||
ws.on('headers', headers);
|
||||
ws.on('close', close);
|
||||
ws.on('error', error);
|
||||
ws.on('message', (data) => { message(ws, data) });
|
||||
ws.on('message', (data) => { message(ws, data); });
|
||||
});
|
||||
};
|
||||
|
||||
const createPlayer = (ws) => {
|
||||
const send = (data) => {
|
||||
ws.send(JSON.stringify(data));
|
||||
};
|
||||
|
||||
if (game.name === '') {
|
||||
send({ type: 'player-name', name });
|
||||
return;
|
||||
}
|
||||
|
||||
if (game.state !== 'lobby') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (game.unselected.indexOf(name) === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const slots = [];
|
||||
for (let color in game.players) {
|
||||
if (game.players[color].status === 'Not active') {
|
||||
slots.push(color);
|
||||
}
|
||||
}
|
||||
if (slots.length === 0) {
|
||||
return;
|
||||
}
|
||||
const index = Math.floor(Math.random() * slots.length);
|
||||
console.log(`Requesting to play as ${slots[index]}.`);
|
||||
game.unselected = game.unselected.filter(
|
||||
color => color === slots[index]);
|
||||
send({
|
||||
type: 'set',
|
||||
field: 'color',
|
||||
value: slots[index]
|
||||
});
|
||||
send({
|
||||
type: 'chat',
|
||||
message: `Woohoo! Robot AI ${version} is alive!`
|
||||
});
|
||||
};
|
||||
|
||||
const tryBuild = (ws) => {
|
||||
const send = (data) => {
|
||||
console.log(`ws - send`);
|
||||
ws.send(JSON.stringify(data));
|
||||
};
|
||||
let trying = false;
|
||||
if (game.private.wood
|
||||
&& game.private.brick
|
||||
&& game.private.sheep
|
||||
&& game.private.wheat) {
|
||||
send({
|
||||
type: 'buy-settlement'
|
||||
});
|
||||
trying = true;
|
||||
}
|
||||
|
||||
if (game.private.wood && game.private.brick) {
|
||||
send({
|
||||
type: 'buy-road'
|
||||
});
|
||||
trying = true;
|
||||
}
|
||||
|
||||
return trying;
|
||||
};
|
||||
|
||||
const message = (ws, data) => {
|
||||
const send = (data) => {
|
||||
console.log(`ws - send: ${data.type}`);
|
||||
ws.send(JSON.stringify(data));
|
||||
};
|
||||
|
||||
data = JSON.parse(data);
|
||||
switch (data.type) {
|
||||
case 'game-update':
|
||||
console.log(`ws - receive - `,
|
||||
Object.assign({}, data.update, {
|
||||
activities: 'filtered out',
|
||||
chat: 'filtered out'
|
||||
})
|
||||
);
|
||||
|
||||
Object.assign(game, data.update);
|
||||
if (game.name === '') {
|
||||
ws.send(JSON.stringify({ type: 'player-name', name: user }));
|
||||
}
|
||||
if (game.state === 'lobby' && game.unselected.indexOf(user) !== -1) {
|
||||
const slots = [];
|
||||
for (let color in game.players) {
|
||||
if (game.players[color].status === 'Not active') {
|
||||
slots.push(color);
|
||||
console.log(`state - ${game.state}`);
|
||||
|
||||
switch (game.state) {
|
||||
case undefined:
|
||||
case 'lobby':
|
||||
createPlayer(ws);
|
||||
break;
|
||||
|
||||
case 'game-order':
|
||||
if (!game.color) {
|
||||
console.log(`game-order - player not active`);
|
||||
return;
|
||||
}
|
||||
console.log(`game-order - `, {
|
||||
color: game.color,
|
||||
players: game.players
|
||||
});
|
||||
if (!game.players[game.color].orderRoll) {
|
||||
console.log(`Time to roll as ${game.color}`);
|
||||
send({ type: 'roll' });
|
||||
}
|
||||
break;
|
||||
|
||||
case 'initial-placement': {
|
||||
console.log({ color: game.color, state: game.state, turn: game.turn });
|
||||
if (game.turn.color !== game.color) {
|
||||
break;
|
||||
}
|
||||
|
||||
let index;
|
||||
const type = game.turn.actions[0];
|
||||
if (type === 'place-road') {
|
||||
console.log({ roads: game.turn.limits.roads });
|
||||
index = game.turn.limits.roads[Math.floor(
|
||||
Math.random() * game.turn.limits.roads.length)];
|
||||
} else if (type === 'place-settlement') {
|
||||
console.log({ corners: game.turn.limits.corners });
|
||||
index = game.turn.limits.corners[Math.floor(
|
||||
Math.random() * game.turn.limits.corners.length)];
|
||||
}
|
||||
console.log(`Selecting ${type} at ${index}`);
|
||||
send({
|
||||
type, index
|
||||
});
|
||||
} break;
|
||||
|
||||
case 'normal':
|
||||
if (game.turn.color !== game.color) {
|
||||
return;
|
||||
}
|
||||
if (game.turn.actions && game.turn.actions.indexOf('place-road') !== -1) {
|
||||
index = game.turn.limits.roads[Math.floor(
|
||||
Math.random() * game.turn.limits.roads.length)];
|
||||
send({
|
||||
type: 'place-road', index
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (game.turn.actions && game.turn.actions.indexOf('place-settlement') !== -1) {
|
||||
console.log({ corners: game.turn.limits.corners });
|
||||
index = game.turn.limits.corners[Math.floor(
|
||||
Math.random() * game.turn.limits.corners.length)];
|
||||
send({
|
||||
type: 'place-settlement', index
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!game.dice) {
|
||||
console.log(`Rolling...`);
|
||||
send({
|
||||
type: 'roll'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (game.private.mustDiscard) {
|
||||
let mustDiscard = game.private.mustDiscard;
|
||||
const cards = [],
|
||||
discards = {};
|
||||
const types = ['wheat', 'sheep', 'stone', 'brick', 'wood'];
|
||||
types.forEach(type => {
|
||||
for (let i = 0; i < game.private[type]; i++) {
|
||||
cards.push(type);
|
||||
}
|
||||
});
|
||||
while (mustDiscard--) {
|
||||
const type = cards[Math.floor(Math.random() * cards.length)];
|
||||
if (!(type in discards)) {
|
||||
discards[type] = 1;
|
||||
} else {
|
||||
discards[type]++;
|
||||
}
|
||||
}
|
||||
console.log(`discarding - `, discards);
|
||||
send({
|
||||
type: 'discard',
|
||||
discards
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (game.turn.actions
|
||||
&& game.turn.actions.indexOf('place-robber') !== -1) {
|
||||
console.log({ pips: game.turn.limits.pips });
|
||||
const index = game.turn.limits.pips[Math.floor(Math.random() * game.turn.limits.pips.length)];
|
||||
console.log(`placing robber - ${index}`)
|
||||
send({
|
||||
type: 'place-robber',
|
||||
index
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (game.turn.actions && game.turn.actions.indexOf('steal-resource') !== -1) {
|
||||
const { color } = game.turn.limits.players[Math.floor(Math.random() * game.turn.limits.players.length)];
|
||||
console.log(`stealing resouce from ${game.players[color].name}`);
|
||||
send({
|
||||
type: 'steal-resource',
|
||||
color
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (game.turn.robberInAction) {
|
||||
console.log({ turn: game.turn });
|
||||
} else {
|
||||
console.log({
|
||||
turn: game.turn,
|
||||
wheat: game.private.wheat,
|
||||
sheep: game.private.sheep,
|
||||
stone: game.private.stone,
|
||||
brick: game.private.brick,
|
||||
wood: game.private.wood,
|
||||
});
|
||||
if (!tryBuild(ws)) {
|
||||
send({
|
||||
type: 'pass'
|
||||
});
|
||||
}
|
||||
}
|
||||
if (slots.length !== 0) {
|
||||
const index = Math.floor(Math.random() * slots.length);
|
||||
console.log(`Requesting to play as ${slots[index]}.`);
|
||||
game.unselected = game.unselected.filter(
|
||||
color => color === slots[index]);
|
||||
ws.send(JSON.stringify({
|
||||
type: 'set',
|
||||
field: 'color',
|
||||
value: slots[index]
|
||||
}));
|
||||
ws.send(JSON.stringify({
|
||||
type: 'chat',
|
||||
message: `Woohoo! Robot AI ${version} is alive!`
|
||||
}));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.log({ state: game.state, turn: game.turn });
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ping':
|
||||
if (!game.state) {
|
||||
console.log(`ping received with no game. Sending update request`);
|
||||
ws.send(JSON.stringify({
|
||||
type: 'game-update'
|
||||
}));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -123,14 +346,10 @@ const message = (ws, data) => {
|
||||
}
|
||||
|
||||
const ai = async (ws) => {
|
||||
ws.send(JSON.stringify({
|
||||
type: 'game-update'
|
||||
}));
|
||||
}
|
||||
|
||||
connect().then((ws) => {
|
||||
ai(ws).then(() => {
|
||||
})
|
||||
ai(ws)
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
ws.close();
|
||||
|
@ -753,7 +753,7 @@ const canGiveBuilding = (game) => {
|
||||
}
|
||||
}
|
||||
|
||||
const adminActions = (game, action, value, query) => {
|
||||
const adminCommands = (game, action, value, query) => {
|
||||
let color, player, parts, session, corners, error;
|
||||
|
||||
switch (action) {
|
||||
@ -962,6 +962,15 @@ const adminActions = (game, action, value, query) => {
|
||||
return;
|
||||
}
|
||||
return `Unable to find active session for ${colorToWord(color)} (${value})`;
|
||||
case "state":
|
||||
if (game.state !== 'lobby') {
|
||||
return `Game already started.`;
|
||||
}
|
||||
if (game.active < 2) {
|
||||
return `Not enough players in game to start.`;
|
||||
}
|
||||
game.state = 'game-order';
|
||||
break;
|
||||
|
||||
default:
|
||||
return `Invalid admin action ${action}.`;
|
||||
@ -1864,7 +1873,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
if (req.headers['private-token'] !== req.app.get('admin')) {
|
||||
error = `Invalid admin credentials.`;
|
||||
} else {
|
||||
error = adminActions(game, action, value, req.query);
|
||||
error = adminCommands(game, action, value, req.query);
|
||||
}
|
||||
if (!error) {
|
||||
sendGameToPlayers(game);
|
||||
@ -2500,21 +2509,25 @@ const playCard = (game, session, card) => {
|
||||
const placeSettlement = (game, session, index) => {
|
||||
const player = session.player;
|
||||
index = parseInt(index);
|
||||
|
||||
if (game.state !== 'initial-placement' && game.state !== 'normal') {
|
||||
return `You cannot purchase a development card unless the game is active (${game.state}).`;
|
||||
return `You cannot place a settlement unless the game is active (${game.state}).`;
|
||||
}
|
||||
|
||||
if (session.color !== game.turn.color) {
|
||||
return `It is not your turn! It is ${game.turn.name}'s turn.`;
|
||||
}
|
||||
|
||||
/* index out of range... */
|
||||
if (game.placements.corners[index] === undefined) {
|
||||
return `You have requested to place a settlement illegally!`;
|
||||
}
|
||||
|
||||
/* If this is not a valid road in the turn limits, discard it */
|
||||
if (game.turn
|
||||
&& game.turn.limits
|
||||
&& game.turn.limits.corners
|
||||
&& game.turn.limits.corners.indexOf(index) === -1) {
|
||||
if (!game.turn
|
||||
|| !game.turn.limits
|
||||
|| !game.turn.limits.corners
|
||||
|| game.turn.limits.corners.indexOf(index) === -1) {
|
||||
return `You tried to cheat! You should not try to break the rules.`;
|
||||
}
|
||||
const corner = game.placements.corners[index];
|
||||
@ -2650,19 +2663,26 @@ const placeRoad = (game, session, index) => {
|
||||
const player = session.player;
|
||||
index = parseInt(index);
|
||||
if (game.state !== 'initial-placement' && game.state !== 'normal') {
|
||||
return `You cannot purchase a development card unless the game is active (${game.state}).`;
|
||||
return `You cannot purchase a place a road unless the game is active (${game.state}).`;
|
||||
}
|
||||
|
||||
if (session.color !== game.turn.color) {
|
||||
return `It is not your turn! It is ${game.turn.name}'s turn.`;
|
||||
}
|
||||
|
||||
/* Valid index location */
|
||||
if (game.placements.roads[index] === undefined) {
|
||||
return `You have requested to place a road illegally!`;
|
||||
}
|
||||
|
||||
/* If this is not a valid road in the turn limits, discard it */
|
||||
if (game.turn && game.turn.limits && game.turn.limits.roads && game.turn.limits.roads.indexOf(index) === -1) {
|
||||
if (!game.turn
|
||||
|| !game.turn.limits
|
||||
|| !game.turn.limits.roads
|
||||
|| game.turn.limits.roads.indexOf(index) === -1) {
|
||||
return `You tried to cheat! You should not try to break the rules.`;
|
||||
}
|
||||
|
||||
const road = game.placements.roads[index];
|
||||
if (road.color) {
|
||||
return `This location already has a road belonging to ${game.players[road.color].name}!`;
|
||||
@ -2930,6 +2950,9 @@ const discard = (game, session, discards) => {
|
||||
}
|
||||
sum += parseInt(discards[type]);
|
||||
}
|
||||
if (sum > player.mustDiscard) {
|
||||
return `You can not discard that many cards! You can only discard ${player.mustDiscard}.`;
|
||||
}
|
||||
/*
|
||||
if (sum !== player.mustDiscard) {
|
||||
return `You need to discard ${player.mustDiscard} cards.`;
|
||||
@ -2943,13 +2966,13 @@ const discard = (game, session, discards) => {
|
||||
player.resources -= count;
|
||||
}
|
||||
addChatMessage(game, null, `${session.name} discarded ${sum} resource cards.`);
|
||||
if (player.mustDiscard) {
|
||||
if (player.mustDiscard > 0) {
|
||||
addChatMessage(game, null, `${session.name} did not discard enough and must discard ${player.mustDiscard} more cards.`);
|
||||
}
|
||||
|
||||
let move = true;
|
||||
for (let color in game.players) {
|
||||
const discard = game.players[color].mustDiscard;
|
||||
const discard = game.players[color].mustDiscard > 0;
|
||||
if (discard) {
|
||||
move = false;
|
||||
}
|
||||
@ -3231,11 +3254,15 @@ const placeCity = (game, session, index) => {
|
||||
if (session.color !== game.turn.color) {
|
||||
return `It is not your turn! It is ${game.turn.name}'s turn.`;
|
||||
}
|
||||
/* Valid index check */
|
||||
if (game.placements.corners[index] === undefined) {
|
||||
return `You have requested to place a city illegally!`;
|
||||
}
|
||||
/* If this is not a placement the turn limits, discard it */
|
||||
if (game.turn && game.turn.limits && game.turn.limits.corners && game.turn.limits.corners.indexOf(index) === -1) {
|
||||
if (!game.turn
|
||||
|| !game.turn.limits
|
||||
|| !game.turn.limits.corners
|
||||
|| game.turn.limits.corners.indexOf(index) === -1) {
|
||||
return `You tried to cheat! You should not try to break the rules.`;
|
||||
}
|
||||
const corner = game.placements.corners[index];
|
||||
@ -4371,11 +4398,15 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
|
||||
resetDisconnectCheck(game, req);
|
||||
console.log(`${short}: Game ${id} - WebSocket connect from ${getName(session)}`);
|
||||
|
||||
if (session.keepAlive) {
|
||||
|
||||
/* Send initial ping to initiate communication with client */
|
||||
if (!session.keepAlive) {
|
||||
console.log(`${short}: Sending initial ping`);
|
||||
ping(session);
|
||||
} else {
|
||||
clearTimeout(session.keepAlive);
|
||||
session.keepAlive = setTimeout(() => { ping(session); }, 2500);
|
||||
}
|
||||
session.keepAlive = setTimeout(() => { ping(session); }, 2500);
|
||||
});
|
||||
|
||||
const debugChat = (game, preamble) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user