1
0

Lots of fixes for volcano and other features

Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
James Ketrenos 2022-06-18 12:30:26 -07:00
parent c093d7141e
commit 99f0971873
15 changed files with 146 additions and 65 deletions

View File

@ -140,8 +140,10 @@ const Activities = () => {
placement = (state === 'initial-placement' || (turn && turn.active === 'road-building')), placement = (state === 'initial-placement' || (turn && turn.active === 'road-building')),
placeRoad = placement && turn && turn.actions && turn.actions.indexOf('place-road') !== -1, placeRoad = placement && turn && turn.actions && turn.actions.indexOf('place-road') !== -1,
mustStealResource = turn && turn.actions && turn.actions.indexOf('steal-resource') !== -1, mustStealResource = turn && turn.actions && turn.actions.indexOf('steal-resource') !== -1,
rollForVolcano = (isTurn && state === 'volcano'), rollForVolcano = (isTurn && state === 'volcano' && turn && !turn.select),
rollForOrder = (state === 'game-order'); rollForOrder = (state === 'game-order'),
selectResources = turn && turn.actions && turn.actions.indexOf('select-resources') !== -1;
console.log(`activities - `, state, turn, activities); console.log(`activities - `, state, turn, activities);
let discarders = [], mustDiscard = false; let discarders = [], mustDiscard = false;
for (let key in players) { for (let key in players) {
@ -161,6 +163,16 @@ console.log(`activities - `, state, turn, activities);
}); });
let who; let who;
if (turn && turn.select) {
const selecting = [];
for (let key in turn.select) {
selecting.push({
color: key,
name: color === key ? 'You' : players[key].name});
}
who = selecting.map((player, index) =>
<><PlayerColor color={player.color} />{ player.name }{ index !== selecting.length - 1 ? ', ' : '' }</>);
} else {
if (isTurn) { if (isTurn) {
who = 'You'; who = 'You';
} else { } else {
@ -170,6 +182,7 @@ console.log(`activities - `, state, turn, activities);
who = <><PlayerColor color={turn.color}/> {turn.name}</> who = <><PlayerColor color={turn.color}/> {turn.name}</>
} }
} }
}
return ( return (
<div className="Activities"> <div className="Activities">
@ -190,10 +203,14 @@ console.log(`activities - `, state, turn, activities);
<div className="Requirement">{who} must roll for game order.</div> <div className="Requirement">{who} must roll for game order.</div>
} }
{rollForVolcano && { rollForVolcano &&
<div className="Requirement">{who} must roll for Volcano devastation!</div> <div className="Requirement">{who} must roll for Volcano devastation!</div>
} }
{ selectResources &&
<div className="Requirement">{who} must select resources!</div>
}
{ normalPlay && mustDiscard && <> { discarders } </> } { normalPlay && mustDiscard && <> { discarders } </> }
{ !isTurn && normalPlay && turn && { !isTurn && normalPlay && turn &&

View File

@ -68,10 +68,6 @@ body {
background-color: #00000060; background-color: #00000060;
} }
.Table .PlayersStatus {
z-index: 5000;
}
.Table .Game { .Table .Game {
position: relative; position: relative;
display: flex; display: flex;

View File

@ -31,6 +31,17 @@ import { Dice } from "./Dice.js";
import history from "./history.js"; import history from "./history.js";
import "./App.css"; import "./App.css";
import equal from "fast-deep-equal"; import equal from "fast-deep-equal";
/*
const Pip = () => {
<div className="Pip"
style={{
backgroundImage: `url(${assetsPath}/gfx/pip-numbers.png)`,
backgroundPositionX: `${100. * (pip.order % 6) / 5.}%`,
backgroundPositionY: `${100 * Math.floor(pip.order / 6) / 5.}%`
}}
><div className="Pip-Shape" /></div>
}
*/
const Table = () => { const Table = () => {
const params = useParams(); const params = useParams();

View File

@ -1,5 +1,4 @@
import React, { useEffect, useState, useContext, useRef, useMemo, import React, { useEffect, useState, useContext, useRef, useMemo } from "react";
useCallback } from "react";
import equal from "fast-deep-equal"; import equal from "fast-deep-equal";
import { assetsPath } from "./Common.js"; import { assetsPath } from "./Common.js";
import "./Board.css"; import "./Board.css";
@ -510,20 +509,23 @@ const Board = () => {
const tile = Object.assign({}, const tile = Object.assign({},
tiles[order], tiles[order],
{ index: index++, left: x, top: y}); { index: index++, left: x, top: y});
if ('volcano' in rules const volcanoActive = 'volcano' in rules
&& rules[`volcano`].enabled && rules[`volcano`].enabled;
&& tile.type === 'desert') {
tile.type = 'volcano';
tile.card = 0;
}
if ('tiles-start-facing-down' in rules if ('tiles-start-facing-down' in rules
&& rules[`tiles-start-facing-down`].enabled && rules[`tiles-start-facing-down`].enabled
&& state !== 'normal' && state !== 'normal'
&& state !== 'volcano' && state !== 'volcano'
&& state !== 'winner') { && state !== 'winner'
&& (volcanoActive && tile.type !== 'desert')) {
tile.type = 'jungle'; tile.type = 'jungle';
tile.card = 0; tile.card = 0;
} }
if (volcanoActive
&& tile.type === 'desert') {
tile.type = 'volcano';
tile.card = 0;
}
let div = <Tile let div = <Tile
key={`tile-${order}`} key={`tile-${order}`}
tile={tile} tile={tile}

View File

@ -22,7 +22,6 @@
.ChatList .System { .ChatList .System {
background-color: #f0f0f0; background-color: #f0f0f0;
transform: scale(0.8);
border-radius: 0.25em; border-radius: 0.25em;
} }
@ -31,6 +30,15 @@
flex-shrink: 0; flex-shrink: 0;
} }
.ChatList .System.MuiListItem-gutters {
margin-bottom: 0.25rem;
max-width: calc(100% - 0.5rem);
}
.ChatList .System .MuiTypography-body1 {
font-size: 0.6rem;
}
.ChatList .MuiListItem-gutters { .ChatList .MuiListItem-gutters {
padding: 2px 0 2px 0; padding: 2px 0 2px 0;
} }

View File

@ -151,7 +151,7 @@ const Chat = () => {
if (resource) { if (resource) {
const count = resource[3] ? parseInt(resource[3]) : 1; const count = resource[3] ? parseInt(resource[3]) : 1;
message = <><Resource label={true} count={count} message = <><Resource label={true} count={count}
type={resource[4]}/>{resource[5]}{message}</>; type={resource[4]} disabled/>{resource[5]}{message}</>;
start = resource[1]; start = resource[1];
} else { } else {
message = <>{start}{message}</>; message = <>{start}{message}</>;
@ -169,7 +169,7 @@ const Chat = () => {
<PlayerColor color={item.color}/> <PlayerColor color={item.color}/>
} }
<ListItemText primary={message} <ListItemText primary={message}
secondary={item.color && <Moment fromNow date={item.date > Date.now() ? secondary={item.color && <Moment fromNow trim date={item.date > Date.now() ?
Date.now() : item.date} interval={1000}/>} /> Date.now() : item.date} interval={1000}/>} />
</ListItem> </ListItem>
); );
@ -188,10 +188,11 @@ const Chat = () => {
<TextField className="ChatInput" <TextField className="ChatInput"
disabled={!name} disabled={!name}
onKeyPress={chatKeyPress} onKeyPress={chatKeyPress}
label={startTime !== 0 && <Moment tz={"Etc/GMT"} label={startTime !== 0 && <>Game duration: <Moment tz={"Etc/GMT"}
format="h:mm:ss" format="h:mm:ss"
trim
durationFromNow interval={1000} durationFromNow interval={1000}
date={startTime}/>} date={startTime}/></>}
variant="outlined"/> variant="outlined"/>
</Paper> </Paper>
); );

View File

@ -80,8 +80,6 @@ const ChooseCard = () => {
} }
const selectCard = useCallback((event) => { const selectCard = useCallback((event) => {
event.target.classList.toggle('Selected');
const selected = document.querySelectorAll('.ChooseCard .Selected'); const selected = document.querySelectorAll('.ChooseCard .Selected');
if (selected.length > count) { if (selected.length > count) {
for (let i = 0; i < selected.length; i++) { for (let i = 0; i < selected.length; i++) {

View File

@ -1,14 +1,4 @@
const getPlayerName = (sessions, color) => {
for (let i = 0; i < sessions.length; i++) {
const session = sessions[i];
if (session.color === color) {
return session.name;
}
}
return null;
}
function debounce(fn, ms) { function debounce(fn, ms) {
let timer; let timer;
return _ => { return _ => {
@ -25,4 +15,4 @@ const base = process.env.PUBLIC_URL;
const assetsPath = `${base}/assets`; const assetsPath = `${base}/assets`;
const gamesPath = `${base}`; const gamesPath = `${base}`;
export { base, debounce, assetsPath, gamesPath, getPlayerName }; export { base, debounce, assetsPath, gamesPath };

View File

@ -11,6 +11,20 @@
flex-shrink: 1; flex-shrink: 1;
} }
.Hand .CardsSelected {
display: flex;
position: absolute;
bottom: 1rem;
left: 2rem;
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
background-color: white;
border: 2px solid black;
z-index: 500; /* above cards */
pointer-events: none;
opacity: 0.75;
}
.Hand .CardGroup { .Hand .CardGroup {
display: flex; display: flex;
min-height: calc(7.2em + 0.5em); min-height: calc(7.2em + 0.5em);

View File

@ -27,6 +27,7 @@ const Hand = ({buildActive, setBuildActive, setCardActive}) => {
const [development, setDevelopment] = useState([]); const [development, setDevelopment] = useState([]);
const [mostPorts, setMostPorts] = useState(undefined); const [mostPorts, setMostPorts] = useState(undefined);
const [mostDeveloped, setMostDeveloped] = useState(undefined); const [mostDeveloped, setMostDeveloped] = useState(undefined);
const [selected, setSelected] = useState(0);
const fields = useMemo(() => [ const fields = useMemo(() => [
'private', 'turn', 'color', 'longestRoad', 'largestArmy', 'mostPorts', 'mostDeveloped' 'private', 'turn', 'color', 'longestRoad', 'largestArmy', 'mostPorts', 'mostDeveloped'
@ -116,17 +117,34 @@ const Hand = ({buildActive, setBuildActive, setCardActive}) => {
setDevelopment(development); setDevelopment(development);
}, [priv, setDevelopment, setCardActive]); }, [priv, setDevelopment, setCardActive]);
useEffect(() => {
const count = document.querySelectorAll('.Hand .CardGroup .Selected');
if (count.length !== selected) {
setSelected(count.length);
}
}, [setSelected, selected]);
if (!priv) { if (!priv) {
return <></>; return <></>;
} }
const cardSelected = (event) => {
const count = document.querySelectorAll('.Hand .CardGroup .Selected');
setSelected(count.length);
}
return <div className="Hand"> return <div className="Hand">
{<div className="CardsSelected"
style={selected === 0 ? { display: 'none' } : {}}>
{selected} cards selected
</div>}
<div className="CardGroup"> <div className="CardGroup">
<Resource type="wood" count={priv.wood}/> <Resource type="wood" count={priv.wood} onClick={cardSelected}/>
<Resource type="wheat" count={priv.wheat}/> <Resource type="wheat" count={priv.wheat} onClick={cardSelected} />
<Resource type="stone" count={priv.stone}/> <Resource type="stone" count={priv.stone} onClick={cardSelected} />
<Resource type="brick" count={priv.brick}/> <Resource type="brick" count={priv.brick} onClick={cardSelected} />
<Resource type="sheep" count={priv.sheep}/> <Resource type="sheep" count={priv.sheep} onClick={cardSelected} />
</div> </div>
<div className="CardGroup"> <div className="CardGroup">
{ development } { development }

View File

@ -19,7 +19,7 @@
align-items: flex-start; align-items: flex-start;
pointer-events: all; pointer-events: all;
right: auto; right: auto;
bottom: 7rem; /* 1rem over top of Resource cards in hand */ bottom: 8rem; /* 1rem over top of Resource cards in hand */
} }
.PlayersStatus .Player:not(:last-child) { .PlayersStatus .Player:not(:last-child) {

View File

@ -2,19 +2,22 @@ import React from "react";
import "./Resource.css"; import "./Resource.css";
import { assetsPath } from './Common.js'; import { assetsPath } from './Common.js';
const Resource = ({ type, select, disabled, available, count, label, onClick }) => { const Resource = ({ type, disabled, available, count, label, onClick }) => {
const array = new Array(Number(count ? count : 0)); const array = new Array(Number(count ? count : 0));
const click = select ? select : (event) => { const click = (event) => {
if (!disabled) { if (!disabled) {
event.target.classList.toggle('Selected'); event.target.classList.toggle('Selected');
} }
if (onClick) {
onClick(event);
}
}; };
if (label) { if (label) {
return <div className={`Resource ${count === 0 ? 'None' : ''}`} return <div className={`Resource ${count === 0 ? 'None' : ''}`}
disabled={disabled} disabled={disabled}
data-type={type} data-type={type}
onClick={onClick ? onClick : click} onClick={click}
style={{backgroundImage:`url(${assetsPath}/gfx/card-${type}.png)`}}> style={{backgroundImage:`url(${assetsPath}/gfx/card-${type}.png)`}}>
{ available !== undefined && <div className="Left">{available}</div> } { available !== undefined && <div className="Left">{available}</div> }
<div className="Right">{count}</div> <div className="Right">{count}</div>
@ -29,7 +32,7 @@ const Resource = ({ type, select, disabled, available, count, label, onClick })
<div className="Resource" <div className="Resource"
data-type={type} data-type={type}
disabled={disabled} disabled={disabled}
onClick={onClick ? onClick : click} onClick={click}
style={{backgroundImage:`url(${assetsPath}/gfx/card-${type}.png)`}}> style={{backgroundImage:`url(${assetsPath}/gfx/card-${type}.png)`}}>
</div> </div>
)) } )) }

View File

@ -104,12 +104,14 @@ const Trade = () => {
onClick={() => transfer(resource, 'get')} onClick={() => transfer(resource, 'get')}
label={true} label={true}
type={resource} type={resource}
disabled
count={gets[resource]}/> count={gets[resource]}/>
<div className="Direction">{ gets[resource] === gives[resource] ? '' : (gets[resource] > gives[resource] ? <ArrowDownwardIcon/> : <ArrowUpwardIcon/>)}</div> <div className="Direction">{ gets[resource] === gives[resource] ? '' : (gets[resource] > gives[resource] ? <ArrowDownwardIcon/> : <ArrowUpwardIcon/>)}</div>
<Resource <Resource
onClick={() => transfer(resource, 'give')} onClick={() => transfer(resource, 'give')}
label={true} label={true}
type={resource} type={resource}
disabled
available={priv ? priv[resource] - gives[resource] : undefined} available={priv ? priv[resource] - gives[resource] : undefined}
count={gives[resource]}/> count={gives[resource]}/>
</div>; </div>;

View File

@ -105,7 +105,7 @@ const Winner = ({ winnerDismissed, setWinnerDismissed }) => {
} }
const count = stats.robber.stole[type]; const count = stats.robber.stole[type];
robber = <>{robber} robber = <>{robber}
<Resource label={true} type={type} count={count} /> <Resource label={true} type={type} count={count} disabled/>
</>; </>;
} }
robber = <div> robber = <div>
@ -137,7 +137,7 @@ const Winner = ({ winnerDismissed, setWinnerDismissed }) => {
} }
const count = stats[player].stolen[type]; const count = stats[player].stolen[type];
stolen = <>{stolen} stolen = <>{stolen}
<Resource label={true} type={type} count={count}/> <Resource label={true} type={type} count={count} disabled/>
</>; </>;
} }
if (stolen) { if (stolen) {

View File

@ -391,7 +391,7 @@ const distributeResources = (game, roll) => {
} }
if (session) { if (session) {
addChatMessage(game, session, `${session.name} receives ${message.join(', ')}.`); addChatMessage(game, session, `${session.name} receives ${message.join(', ')} for pip ${roll}.`);
} }
} }
@ -476,7 +476,6 @@ const processRoll = (game, session, dice) => {
game.turn.select = {}; game.turn.select = {};
const volcano = layout.tiles.find((tile, index) => const volcano = layout.tiles.find((tile, index) =>
staticData.tiles[game.tileOrder[index]].type === 'desert'); staticData.tiles[game.tileOrder[index]].type === 'desert');
console.log({ mode: 'gold', volcano });
volcano.corners.forEach(index => { volcano.corners.forEach(index => {
const corner = game.placements.corners[index]; const corner = game.placements.corners[index];
if (corner.color) { if (corner.color) {
@ -488,9 +487,11 @@ const processRoll = (game, session, dice) => {
count += corner.type === 'settlement' ? 1 : 2; count += corner.type === 'settlement' ? 1 : 2;
} }
}); });
console.log(`Volcano! - `, {
mode: 'gold',
selected: game.turn.select
});
if (count) { if (count) {
game.turn.actions = [ 'select-resources' ];
/* To gain volcano resources, you need at least 3 settlements, /* To gain volcano resources, you need at least 3 settlements,
* so Robin Hood Robber does not apply */ * so Robin Hood Robber does not apply */
if (volcano === layout.tiles[game.robber]) { if (volcano === layout.tiles[game.robber]) {
@ -499,6 +500,7 @@ const processRoll = (game, session, dice) => {
delete game.turn.select; delete game.turn.select;
} else { } else {
addChatMessage(game, null, `House rule 'Volcanoes have minerals' activated. Players must select which resources to receive from the Volcano!`); addChatMessage(game, null, `House rule 'Volcanoes have minerals' activated. Players must select which resources to receive from the Volcano!`);
game.turn.actions = ['select-resources'];
game.turn.active = 'volcano'; game.turn.active = 'volcano';
} }
} else { } else {
@ -521,7 +523,8 @@ const processRoll = (game, session, dice) => {
turn: game.turn, turn: game.turn,
players: getFilteredPlayers(game), players: getFilteredPlayers(game),
chat: game.chat, chat: game.chat,
dice: game.dice dice: game.dice,
state: game.state
}); });
return; return;
} }
@ -804,11 +807,10 @@ const adminActions = (game, action, value, query) => {
if (session.player.cities === 0) { if (session.player.cities === 0) {
return `Player ${game.turn.name} does not have any more cities to give.`; return `Player ${game.turn.name} does not have any more cities to give.`;
} }
corners = getValidCorners(game, session.color); corners = getValidCorners(game, session.color, 'settlement');
if (corners.length === 0) { if (corners.length === 0) {
return `There are no valid locations for ${game.turn.name} to place a settlement.`; return `There are no valid locations for ${game.turn.name} to place a settlement.`;
} }
corners = getValidCorners(game, session.color, 'settlement');
game.turn.free = true; game.turn.free = true;
setForCityPlacement(game, corners); setForCityPlacement(game, corners);
addChatMessage(game, null, `Admin gave a city to ${game.turn.name}. ` + addChatMessage(game, null, `Admin gave a city to ${game.turn.name}. ` +
@ -2766,7 +2768,7 @@ const placeRoad = (game, session, index) => {
}); });
message.push(`${receives[type]} ${type}`); message.push(`${receives[type]} ${type}`);
} }
addChatMessage(game, session, `${session.name} receives ${message.join(', ')}.`); addChatMessage(game, session, `${session.name} receives ${message.join(', ')} for initial settlement placement.`);
} }
} }
addChatMessage(game, null, `It is ${session.name}'s turn.`); addChatMessage(game, null, `It is ${session.name}'s turn.`);
@ -3005,6 +3007,7 @@ const selectResources = (game, session, cards) => {
count = 1; count = 1;
} }
if (game.state === 'volcano') { if (game.state === 'volcano') {
console.log({ cards, turn: game.turn });
if (!game.turn.select) { if (!game.turn.select) {
count = 0; count = 0;
} else if (session.color in game.turn.select) { } else if (session.color in game.turn.select) {
@ -3052,8 +3055,6 @@ const selectResources = (game, session, cards) => {
display.push(`${selected[card]} ${card}`); display.push(`${selected[card]} ${card}`);
} }
addActivity(game, session, `${session.name} has chosen ${display.join(', ')}!`);
switch (game.turn.active) { switch (game.turn.active) {
case 'monopoly': case 'monopoly':
const gave = [], type = cards[0]; const gave = [], type = cards[0];
@ -3084,10 +3085,11 @@ const selectResources = (game, session, cards) => {
} }
if (gave.length) { if (gave.length) {
addChatMessage(game, session, `${session.name} player Monopoly and selected ${display.join(', ')}. ` + addChatMessage(game, session, `${session.name} played Monopoly and selected ${display.join(', ')}. ` +
`Players ${gave.join(', ')}. In total, they received ${total} ${type}.`); `Players ${gave.join(', ')}. In total, they received ${total} ${type}.`);
} else { } else {
addActivity(game, session, 'No players had that resource. Wa-waaaa.');
addActivity(game, session, `${session.name} has chosen ${display.join(', ')}! Unfortunately, no players had that resource. Wa-waaaa.`);
} }
delete game.turn.active; delete game.turn.active;
game.turn.actions = []; game.turn.actions = [];
@ -4039,6 +4041,25 @@ router.ws("/ws/:id", async (ws, req) => {
case 'turn': case 'turn':
case 'turns': case 'turns':
case 'winner': case 'winner':
case 'placements':
case 'longestRoadLength':
case 'robber':
case 'robberName':
case 'pips':
case 'pipsOrder':
case 'borders':
case 'tileOrder':
case 'active':
case 'largestArmy':
case 'mostDeveloped':
case 'mostPorts':
case 'longestRoad':
case 'tiles':
case 'pipOrder':
case 'signature':
case 'borderOrder':
case 'dice':
case 'activities':
update[field] = game[field]; update[field] = game[field];
break; break;
case 'rules': case 'rules':