diff --git a/client/src/Board.js b/client/src/Board.js
index ab4458a..8e72a00 100644
--- a/client/src/Board.js
+++ b/client/src/Board.js
@@ -25,6 +25,15 @@ const
borderImageWidth = (2 + 2/3) * tileImageWidth, /* 2.667 * .Tile.width */
borderImageHeight = borderImageWidth * 0.29; /* 0.29 * .Border.height */
+
+const showTooltip = () => {
+ document.querySelector('.Board .Tooltip').style.display = 'flex';
+};
+
+const clearTooltip = () => {
+ document.querySelector('.Board .Tooltip').style.display = 'none';
+};
+
const Board = () => {
const { ws } = useContext(GlobalContext);
const board = useRef();
@@ -237,6 +246,13 @@ const Board = () => {
const Corner = ({corner}) => {
return
{
+ if (e.shiftPressed) {
+ const tooltip = document.querySelector('.Board .Tooltip');
+ tooltip.innerHTML = `
${corner}
`;
+ showTooltip();
+ }
+ }}
onClick={(event) => { onCornerClicked(event, corner) }}
data-index={corner.index}
style={{
@@ -538,7 +554,7 @@ const Board = () => {
if (tile.type === 'wheat') {
div =
- {
});
};
- const showTooltip = () => {
- document.querySelector('.Board .Tooltip').style.display = 'flex';
- }
-
- const clearTooltip = () => {
- document.querySelector('.Board .Tooltip').style.display = 'none';
- }
-
const calculateBorderSlot = (side, e) => {
const borderBox = document.querySelector('.Borders').getBoundingClientRect();
let angle = (360 + Math.floor(90 + Math.atan2(e.pageY - borderBox.top, e.pageX - borderBox.left) * 180 / Math.PI)) % 360 - (side * 60);
diff --git a/server/ai/app.js b/server/ai/app.js
index ea911fc..3322536 100644
--- a/server/ai/app.js
+++ b/server/ai/app.js
@@ -5,6 +5,7 @@ const calculateLongestRoad = require('./longest-road.js');
const { getValidRoads, getValidCorners } = require('../util/validLocations.js');
const { layout, staticData } = require('../util/layout.js');
+const { turn } = require('core-js/core/array');
const version = '0.0.1';
@@ -461,6 +462,17 @@ const processWaitingFor = (waitingFor) => {
received = {};
}
+
+const selectResources = async (received) => {
+ if (!game.turn) {
+ return { turn: anyValue };
+ }
+
+ if (!game.turn.actions || game.turn.actions.indexOf('select-resources') === -1) {
+ return;
+ }
+}
+
const processDiscard = async (received) => {
if (!game.players) {
waitingFor = {
@@ -612,6 +624,29 @@ const processTrade = async (received) => {
};
}
+const processVolcano = async (received) => {
+ if (!game.turn || !game.private) {
+ return {
+ turn: anyValue,
+ private: anyValue
+ }
+ };
+
+ if (game.turn.actions
+ && game.turn.actions.indexOf('select-resources') !== -1) {
+ console.log(`${name} - TODO - select resources -`, game.turn.select);
+ return;
+ }
+
+ send({
+ type: 'roll'
+ });
+
+ return {
+ turn: anyValue
+ };
+};
+
const processNormal = async (received) => {
let waitingFor = undefined;
@@ -628,6 +663,11 @@ const processNormal = async (received) => {
return waitingFor;
}
+ waitingFor = await selectResources(received);
+ if (waitingFor) {
+ return waitingFor;
+ }
+
/* From here on it is only actions that occur on the player's turn */
if (!received.turn || received.turn.color !== game.color) {
console.log(`${name} - waiting for turn... ${game.players[game.turn.color].name} is active.`);
@@ -858,7 +898,14 @@ const message = async (data) => {
processWaitingFor(waitingFor);
}
return;
-
+
+ case 'volcano':
+ waitingFor = await processVolcano(received);
+ if (waitingFor) {
+ processWaitingFor(waitingFor);
+ }
+ return;
+
case 'normal':
waitingFor = await processNormal(received);
if (waitingFor) {
diff --git a/server/routes/games.js b/server/routes/games.js
index 61657d9..a638969 100755
--- a/server/routes/games.js
+++ b/server/routes/games.js
@@ -415,7 +415,7 @@ const processRoll = (game, session, dice) => {
}
if (isRuleEnabled(game, 'volcano')) {
- if (sum === game.rules['volcano'].number
+ if (sum === parseInt(game.rules['volcano'].number)
|| (synonym
&& (game.rules['volcano'].number === 2
|| game.rules['volcano'].number === 12))) {
@@ -709,6 +709,17 @@ const adminCommands = (game, action, value, query) => {
switch (action) {
case 'rules':
const rule = value.replace(/=.*$/, '');
+ if (rule === 'list') {
+ const rules = {};
+ for (let key in supportedRules) {
+ if (game.rules[key]) {
+ rules[key] = game.rules[key];
+ } else {
+ rules[key] = { enabled: false };
+ }
+ }
+ return JSON.stringify(rules, null, 2);
+ }
let values = value.replace(/^.*=/, '').split(',');
const rules = {};
rules[rule] = {};
@@ -2768,6 +2779,79 @@ const getVictoryPointRule = (game) => {
return game.rules['victory-points'].points;
}
+const supportedRules = {
+ 'victory-points': (game, session, rules) => {
+ if (!('points' in rules[rule])) {
+ return `No points specified for victory-points`;
+ }
+ if (!rules[rule].enabled) {
+ addChatMessage(game, null,
+ `${getName(session)} has disabled the Victory Point ` +
+ `house rule.`);
+ } else {
+ addChatMessage(game, null,
+ `${getName(session)} set the minimum Victory Points to ` +
+ `${rules[rule].points}`);
+ }
+ },
+ 'roll-double-roll-again': (game, session, rules) => {
+ addChatMessage(game, null,
+ `${getName(session)} has ${rules[rule].enabled ? 'en' : 'dis'}abled the Roll Double, Roll Again house rule.`);
+ },
+ 'volcano': (game, session, rules) => {
+ if (!rules[rule].enabled) {
+ addChatMessage(game, null,
+ `${getName(session)} has disabled the Volcano ` +
+ `house rule.`);
+ } else {
+ if (!(rule in game.rules) || !game.rules[rule].enabled) {
+ addChatMessage(game, null,
+ `${getName(session)} enabled the Volcano ` +
+ `house rule with roll set to ` +
+ `${rules[rule].number} and 'Volanoes have gold' mode ` +
+ `${rules[rule].gold ? 'en' : 'dis'}abled.`);
+ } else {
+ if (game.rules[rule].number !== rules[rule].number) {
+ addChatMessage(game, null,
+ `${getName(session)} set the Volcano roll to ` +
+ `${rules[rule].number}`);
+ }
+
+ if (game.rules[rule].gold !== rules[rule].gold) {
+ addChatMessage(game, null,
+ `${getName(session)} has ` +
+ `${rules[rule].gold ? 'en' : 'dis'}abled the ` +
+ `'Volcanoes have gold' mode.`);
+ }
+ }
+ }
+ },
+ 'twelve-and-two-are-synonyms': (game, session, rules) => {
+ addChatMessage(game, null,
+ `${getName(session)} has ${rules[rule].enabled ? 'en' : 'dis'}abled the Twelve and Two are Synonyms house rule.`);
+ game.rules[rule] = rules[rule];
+ },
+ 'most-developed': (game, session, rules) => {
+ addChatMessage(game, null,
+ `${getName(session)} has ${rules[rule].enabled ? 'en' : 'dis'}abled the Most Developed house rule.`);
+ },
+ 'port-of-call': (game, session, rules) => {
+ addChatMessage(game, null,
+ `${getName(session)} has ${rules[rule].enabled ? 'en' : 'dis'}abled the Another Round of Port house rule.`);
+ },
+ 'tiles-start-facing-down': (game, session, rules) => {
+ addChatMessage(game, null,
+ `${getName(session)} has ${rules[rule].enabled ? 'en' : 'dis'}abled the Tiles Start Facing Down house rule.`);
+ if (rules[rule].enabled) {
+ shuffle(game, session);
+ }
+ },
+ 'robin-hood-robber': (game, session, rules) => {
+ addChatMessage(game, null,
+ `${getName(session)} has ${rules[rule].enabled ? 'en' : 'dis'}abled the Robin Hood Robber house rule.`);
+ }
+};
+
const setRules = (game, session, rules) => {
if (game.state !== 'lobby') {
return `You can not modify House Rules once the game has started.`;
@@ -2778,85 +2862,13 @@ const setRules = (game, session, rules) => {
continue;
}
- switch (rule) {
- case 'victory-points':
- if (!('points' in rules[rule])) {
- return `No points specified for victory-points`;
- }
- if (!rules[rule].enabled) {
- addChatMessage(game, null,
- `${getName(session)} has disabled the Victory Point ` +
- `house rule.`);
- } else {
- addChatMessage(game, null,
- `${getName(session)} set the minimum Victory Points to ` +
- `${rules[rule].points}`);
+ if (rule in supportedRules) {
+ const warning = supportedRules[rule](game, session, rules);
+ if (warning) {
+ return warning;
}
game.rules[rule] = rules[rule];
- break;
- case 'roll-double-roll-again':
- addChatMessage(game, null,
- `${getName(session)} has ${rules[rule].enabled ? 'en' : 'dis'}abled the Roll Double, Roll Again house rule.`);
- game.rules[rule] = rules[rule];
- break;
- case 'volcano':
- if (!rules[rule].enabled) {
- addChatMessage(game, null,
- `${getName(session)} has disabled the Volcano ` +
- `house rule.`);
- } else {
- if (!(rule in game.rules) || !game.rules[rule].enabled) {
- addChatMessage(game, null,
- `${getName(session)} enabled the Volcano ` +
- `house rule with roll set to ` +
- `${rules[rule].number} and 'Volanoes have gold' mode ` +
- `${rules[rule].gold ? 'en' : 'dis'}abled.`);
- } else {
- if (game.rules[rule].number !== rules[rule].number) {
- addChatMessage(game, null,
- `${getName(session)} set the Volcano roll to ` +
- `${rules[rule].number}`);
- }
-
- if (game.rules[rule].gold !== rules[rule].gold) {
- addChatMessage(game, null,
- `${getName(session)} has ` +
- `${rules[rule].gold ? 'en' : 'dis'}abled the ` +
- `'Volcanoes have gold' mode.`);
- }
- }
- }
- game.rules[rule] = rules[rule];
- break;
- case 'twelve-and-two-are-synonyms':
- addChatMessage(game, null,
- `${getName(session)} has ${rules[rule].enabled ? 'en' : 'dis'}abled the Twelve and Two are Synonyms house rule.`);
- game.rules[rule] = rules[rule];
- break;
- case 'most-developed':
- addChatMessage(game, null,
- `${getName(session)} has ${rules[rule].enabled ? 'en' : 'dis'}abled the Most Developed house rule.`);
- game.rules[rule] = rules[rule];
- break;
- case 'port-of-call':
- addChatMessage(game, null,
- `${getName(session)} has ${rules[rule].enabled ? 'en' : 'dis'}abled the Another Round of Port house rule.`);
- game.rules[rule] = rules[rule];
- break;
- case 'tiles-start-facing-down':
- addChatMessage(game, null,
- `${getName(session)} has ${rules[rule].enabled ? 'en' : 'dis'}abled the Tiles Start Facing Down house rule.`);
- if (rules[rule].enabled) {
- shuffle(game, session);
- }
- game.rules[rule] = rules[rule];
- break;
- case 'robin-hood-robber':
- addChatMessage(game, null,
- `${getName(session)} has ${rules[rule].enabled ? 'en' : 'dis'}abled the Robin Hood Robber house rule.`);
- game.rules[rule] = rules[rule];
- break;
- default:
+ } else {
return `Rule ${rule} not recognized.`;
}
}
@@ -3762,6 +3774,14 @@ const calculatePoints = (game, update) => {
}
}
+const clearGame = (game, session) => {
+ resetGame(game);
+ addChatMessage(game, null,
+ `The game has been reset. You can play again with this board, or ` +
+ `click 'New board' to mix things up a bit.`);
+ sendGameToPlayers(game);
+};
+
const gotoLobby = (game, session) => {
if (!game.waiting) {
game.waiting = [];
@@ -4261,6 +4281,13 @@ router.ws("/ws/:id", async (ws, req) => {
sendWarning(session, warning);
}
break;
+ case 'clear-game':
+ console.log(`${short}: <- clear-game:${getName(session)}`);
+ warning = clearGame(game, session);
+ if (warning) {
+ sendWarning(session, warning);
+ }
+ break;
case 'goto-lobby':
console.log(`${short}: <- goto-lobby:${getName(session)}`);
warning = gotoLobby(game, session);
diff --git a/server/rules b/server/rules
index 98e1ab1..48ada23 100755
--- a/server/rules
+++ b/server/rules
@@ -12,9 +12,12 @@ shift
params="${*}"
params="${params// /,}"
+if [[ "${rule}" == "list" ]]; then
+ params=" "
+fi
if [[ "${id}" == "" ]] || [[ "${rule}" == "" ]] || [[ "${params}" == "" ]]; then
cat << EOF
-Usage: rules GAME-ID RULE KEY:VALUE [KEY:VALUE]
+Usage: rules GAME-ID [(list)|(RULE KEY:VALUE [KEY:VALUE])]
Examples: