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

View File

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

View File

@ -31,6 +31,17 @@ import { Dice } from "./Dice.js";
import history from "./history.js";
import "./App.css";
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 params = useParams();

View File

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

View File

@ -22,7 +22,6 @@
.ChatList .System {
background-color: #f0f0f0;
transform: scale(0.8);
border-radius: 0.25em;
}
@ -31,6 +30,15 @@
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 {
padding: 2px 0 2px 0;
}

View File

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

View File

@ -80,8 +80,6 @@ const ChooseCard = () => {
}
const selectCard = useCallback((event) => {
event.target.classList.toggle('Selected');
const selected = document.querySelectorAll('.ChooseCard .Selected');
if (selected.length > count) {
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) {
let timer;
return _ => {
@ -25,4 +15,4 @@ const base = process.env.PUBLIC_URL;
const assetsPath = `${base}/assets`;
const gamesPath = `${base}`;
export { base, debounce, assetsPath, gamesPath, getPlayerName };
export { base, debounce, assetsPath, gamesPath };

View File

@ -11,6 +11,20 @@
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 {
display: flex;
min-height: calc(7.2em + 0.5em);

View File

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

View File

@ -19,7 +19,7 @@
align-items: flex-start;
pointer-events: all;
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) {

View File

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

View File

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

View File

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

View File

@ -391,7 +391,7 @@ const distributeResources = (game, roll) => {
}
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 = {};
const volcano = layout.tiles.find((tile, index) =>
staticData.tiles[game.tileOrder[index]].type === 'desert');
console.log({ mode: 'gold', volcano });
volcano.corners.forEach(index => {
const corner = game.placements.corners[index];
if (corner.color) {
@ -488,9 +487,11 @@ const processRoll = (game, session, dice) => {
count += corner.type === 'settlement' ? 1 : 2;
}
});
console.log(`Volcano! - `, {
mode: 'gold',
selected: game.turn.select
});
if (count) {
game.turn.actions = [ 'select-resources' ];
/* To gain volcano resources, you need at least 3 settlements,
* so Robin Hood Robber does not apply */
if (volcano === layout.tiles[game.robber]) {
@ -499,6 +500,7 @@ const processRoll = (game, session, dice) => {
delete game.turn.select;
} else {
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';
}
} else {
@ -521,7 +523,8 @@ const processRoll = (game, session, dice) => {
turn: game.turn,
players: getFilteredPlayers(game),
chat: game.chat,
dice: game.dice
dice: game.dice,
state: game.state
});
return;
}
@ -804,11 +807,10 @@ const adminActions = (game, action, value, query) => {
if (session.player.cities === 0) {
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) {
return `There are no valid locations for ${game.turn.name} to place a settlement.`;
}
corners = getValidCorners(game, session.color, 'settlement');
game.turn.free = true;
setForCityPlacement(game, corners);
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}`);
}
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.`);
@ -3005,6 +3007,7 @@ const selectResources = (game, session, cards) => {
count = 1;
}
if (game.state === 'volcano') {
console.log({ cards, turn: game.turn });
if (!game.turn.select) {
count = 0;
} else if (session.color in game.turn.select) {
@ -3052,8 +3055,6 @@ const selectResources = (game, session, cards) => {
display.push(`${selected[card]} ${card}`);
}
addActivity(game, session, `${session.name} has chosen ${display.join(', ')}!`);
switch (game.turn.active) {
case 'monopoly':
const gave = [], type = cards[0];
@ -3084,10 +3085,11 @@ const selectResources = (game, session, cards) => {
}
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}.`);
} 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;
game.turn.actions = [];
@ -4039,6 +4041,25 @@ router.ws("/ws/:id", async (ws, req) => {
case 'turn':
case 'turns':
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];
break;
case 'rules':