Trading appears to work
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
parent
3cc166932a
commit
3823b3d50b
@ -106,6 +106,10 @@ body {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.Table .Trade {
|
||||
z-index: 25000;
|
||||
}
|
||||
|
||||
.Table .Dialogs {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
|
@ -24,6 +24,7 @@ import { PlayersStatus } from "./PlayersStatus.js";
|
||||
import { ViewCard } from "./ViewCard.js";
|
||||
import { ChooseCard } from "./ChooseCard.js";
|
||||
import { Hand } from "./Hand.js";
|
||||
import { Trade } from "./Trade.js";
|
||||
|
||||
import history from "./history.js";
|
||||
import "./App.css";
|
||||
@ -290,6 +291,7 @@ const Table = () => {
|
||||
{ /* <PingPong/> */ }
|
||||
<div className="Table">
|
||||
<Activities/>
|
||||
<Trade/>
|
||||
<div className="Game">
|
||||
<div className="Dialogs">
|
||||
{ error && <div className="ErrorDialog">
|
||||
@ -303,7 +305,7 @@ const Table = () => {
|
||||
</div> }
|
||||
{ state === 'normal' && <SelectPlayer/> }
|
||||
{ color && state === 'game-order' && <GameOrder/> }
|
||||
{ /* state === 'normal' && <Trade/> */ }
|
||||
|
||||
{ /* state === 'winner' && <Winner color={winner}/> */ }
|
||||
<ViewCard {...{cardActive, setCardActive }}/>
|
||||
<ChooseCard/>
|
||||
|
@ -5,7 +5,7 @@
|
||||
top: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
margin: 0.25em;
|
||||
}
|
||||
|
||||
.Trade > * {
|
||||
@ -15,7 +15,6 @@
|
||||
display: inline-flex;
|
||||
padding: 0.5em;
|
||||
flex-direction: column;
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
.Trade .Title {
|
||||
|
@ -1,13 +1,18 @@
|
||||
import React, { useState, useCallback, useEffect } from "react";
|
||||
import "./Trade.css";
|
||||
import { getPlayerName } from './Common.js';
|
||||
import PlayerColor from './PlayerColor.js';
|
||||
import React, { useState, useCallback, useEffect, useContext, useMemo,
|
||||
useRef } from "react";
|
||||
import equal from "fast-deep-equal";
|
||||
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Resource from './Resource.js';
|
||||
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'
|
||||
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'
|
||||
|
||||
import {Resource} from './Resource.js';
|
||||
import {PlayerColor} from './PlayerColor.js';
|
||||
import { GlobalContext } from "./GlobalContext.js";
|
||||
|
||||
import "./Trade.css";
|
||||
|
||||
const empty = {
|
||||
wheat: 0,
|
||||
brick: 0,
|
||||
@ -16,22 +21,63 @@ const empty = {
|
||||
sheep: 0
|
||||
};
|
||||
|
||||
/*
|
||||
&& turn && turn.actions
|
||||
&& turn.actions.indexOf('trade') !== -1 &&
|
||||
*/
|
||||
const Trade = () => {
|
||||
const { ws } = useContext(GlobalContext);
|
||||
const [gives, setGives] = useState(Object.assign({}, empty));
|
||||
const [gets, setGets] = useState(Object.assign({}, empty));
|
||||
const player = (table.game && table.game.player) ? table.game.player : undefined;
|
||||
const [turn, setTurn] = useState(undefined);
|
||||
const [priv, setPriv] = useState(undefined);
|
||||
const [players, setPlayers] = useState(undefined);
|
||||
const [color, setColor] = useState(undefined);
|
||||
|
||||
const fields = useMemo(() => [
|
||||
'turn', 'players', 'private', 'color'
|
||||
], []);
|
||||
|
||||
const onWsMessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
switch (data.type) {
|
||||
case 'game-update':
|
||||
console.log(`trade - game-update: `, data.update);
|
||||
if ('turn' in data.update && !equal(turn, data.update.turn)) {
|
||||
setTurn(data.update.turn);
|
||||
}
|
||||
if ('players' in data.update && !equal(players, data.update.players)) {
|
||||
setPlayers(data.update.players);
|
||||
}
|
||||
if ('private' in data.update && !equal(priv, data.update.private)) {
|
||||
setPriv(data.update.private);
|
||||
}
|
||||
if ('color' in data.update && color !== data.update.color) {
|
||||
setColor(data.update.color);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const refWsMessage = useRef(onWsMessage);
|
||||
useEffect(() => { refWsMessage.current = onWsMessage; });
|
||||
useEffect(() => {
|
||||
if (!ws) { return; }
|
||||
const cbMessage = e => refWsMessage.current(e);
|
||||
ws.addEventListener('message', cbMessage);
|
||||
return () => { ws.removeEventListener('message', cbMessage); }
|
||||
}, [ws, refWsMessage]);
|
||||
useEffect(() => {
|
||||
if (!ws) { return; }
|
||||
ws.send(JSON.stringify({
|
||||
type: 'get',
|
||||
fields
|
||||
}));
|
||||
}, [ws, fields]);
|
||||
const transfer = useCallback((type, direction) => {
|
||||
if (direction === 'give') { /* give clicked */
|
||||
if (gets[type]) {
|
||||
gets[type]--;
|
||||
gives[type] = 0;
|
||||
} else {
|
||||
if (gives[type] < player[type]) {
|
||||
if (gives[type] < priv[type]) {
|
||||
gives[type]++;
|
||||
}
|
||||
gets[type] = 0;
|
||||
@ -50,7 +96,7 @@ const Trade = () => {
|
||||
|
||||
setGets({...gets});
|
||||
setGives({...gives});
|
||||
}, [setGets, setGives, gets, gives, player]);
|
||||
}, [setGets, setGives, gets, gives, priv]);
|
||||
|
||||
const createTransfer = useCallback(resource => {
|
||||
return <div key={resource} className="Transfer">
|
||||
@ -64,23 +110,31 @@ const Trade = () => {
|
||||
onClick={() => transfer(resource, 'give')}
|
||||
label={true}
|
||||
type={resource}
|
||||
available={player ? player[resource] - gives[resource] : undefined}
|
||||
available={priv ? priv[resource] - gives[resource] : undefined}
|
||||
count={gives[resource]}/>
|
||||
</div>;
|
||||
}, [ gives, gets, transfer, player]);
|
||||
}, [ gives, gets, transfer, priv]);
|
||||
|
||||
const sendTrade = useCallback((action, offer) => {
|
||||
ws.send(JSON.stringify({
|
||||
type: 'trade',
|
||||
action,
|
||||
offer
|
||||
}));
|
||||
}, [ws]);
|
||||
|
||||
useEffect(() => {
|
||||
if (table.game && table.game.player && table.game.player.gives) {
|
||||
if (priv && priv.gives) {
|
||||
const _gives = {};
|
||||
table.game.player.gives.forEach(give => _gives[give.type] = give.count);
|
||||
priv.gives.forEach(give => _gives[give.type] = give.count);
|
||||
setGives(Object.assign({}, empty, _gives));
|
||||
}
|
||||
if (table.game && table.game.player && table.game.player.gets) {
|
||||
if (priv && priv.gets) {
|
||||
const _gets = {};
|
||||
table.game.player.gets.forEach(get => _gets[get.type] = get.count);
|
||||
priv.gets.forEach(get => _gets[get.type] = get.count);
|
||||
setGets(Object.assign({}, empty, _gets));
|
||||
}
|
||||
}, [ setGets, setGives, table.game ]);
|
||||
}, [ setGets, setGives, priv ]);
|
||||
|
||||
const agreeClicked = useCallback((offer) => {
|
||||
const trade = {
|
||||
@ -91,19 +145,19 @@ const Trade = () => {
|
||||
console.log(gives, gets);
|
||||
trade.gives.forEach(give => _gives[give.type] = give.count);
|
||||
trade.gets.forEach(get => _gets[get.type] = get.count);
|
||||
table.offerTrade(trade);
|
||||
sendTrade('offer', trade);
|
||||
console.log(_gives, _gets);
|
||||
setGives(Object.assign({}, empty, _gives));
|
||||
setGets(Object.assign({}, empty, _gets));
|
||||
}, [setGives, setGets, gives, gets, table]);
|
||||
}, [setGives, setGets, gives, gets, sendTrade]);
|
||||
|
||||
if (!table.game || !player || !table.game.player) {
|
||||
if (!priv || !turn || !turn.actions || turn.actions.indexOf('trade') === -1) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const transfers = [ 'brick', 'wood', 'wheat', 'sheep', 'stone' ].map(resource => { return createTransfer(resource); });
|
||||
|
||||
player.offerRejected = player.offerRejected ? player.offerRejected: {};
|
||||
priv.offerRejected = priv.offerRejected ? priv.offerRejected: {};
|
||||
|
||||
const canMeetOffer = (player, offer) => {
|
||||
if (offer.gets.length === 0 || offer.gives.length === 0) {
|
||||
@ -181,7 +235,7 @@ const Trade = () => {
|
||||
return valid;
|
||||
};
|
||||
|
||||
const isTurn = (table.game.turn && table.game.turn.color === table.game.color) ? true : false;
|
||||
const isTurn = (turn && turn.color === color) ? true : false;
|
||||
|
||||
const offerClicked = (event) => {
|
||||
const trade = {
|
||||
@ -198,38 +252,38 @@ const Trade = () => {
|
||||
trade.gets.push({type: key, count: gets[key]});
|
||||
}
|
||||
}
|
||||
table.offerTrade(trade);
|
||||
sendTrade('offer', trade);
|
||||
}
|
||||
|
||||
const cancelOffer = (offer) => {
|
||||
table.cancelTrade(offer);
|
||||
sendTrade('cancel', offer);
|
||||
}
|
||||
|
||||
const acceptClicked = (offer) => {
|
||||
if (offer.name === 'The bank') {
|
||||
table.acceptTrade(Object.assign({}, { name: offer.name, gives: trade.gets, gets: trade.gives }));
|
||||
sendTrade('accept', Object.assign({}, { name: offer.name, gives: trade.gets, gets: trade.gives }));
|
||||
} else if (offer.self) {
|
||||
table.acceptTrade(offer);
|
||||
sendTrade('accept', offer);
|
||||
} else {
|
||||
table.acceptTrade(Object.assign({}, offer, { gives: offer.gets, gets: offer.gives }));
|
||||
sendTrade('accept', Object.assign({}, offer, { gives: offer.gets, gets: offer.gives }));
|
||||
}
|
||||
};
|
||||
|
||||
const cancelClicked = (event) => {
|
||||
table.cancelTrading();
|
||||
sendTrade('cancel');
|
||||
}
|
||||
|
||||
/* Player has rejected the active player's bid or active player rejected
|
||||
* the other player's bid */
|
||||
const rejectClicked = (trade) => {
|
||||
table.rejectTrade(trade);
|
||||
sendTrade('reject', trade);
|
||||
}
|
||||
|
||||
/* Create list of players with active trades */
|
||||
let players = [];
|
||||
for (let color in table.game.players) {
|
||||
const item = table.game.players[color],
|
||||
name = getPlayerName(table.game.sessions, color);
|
||||
/* Create list of active trades */
|
||||
const activeTrades = [];
|
||||
for (let color in players) {
|
||||
const item = players[color],
|
||||
name = item.name;
|
||||
item.offerRejected = item.offerRejected ? item.offerRejected : {};
|
||||
if (item.status !== 'Active') {
|
||||
continue;
|
||||
@ -237,15 +291,15 @@ const Trade = () => {
|
||||
/* Only list players with an offer, unless it is the active player (see
|
||||
* that you haven't submitted an offer) or the current turn player,
|
||||
* or the player explicitly rejected the player's offer */
|
||||
if (table.game.turn.name !== name && table.game.name !== name
|
||||
&& !(color in player.offerRejected)
|
||||
if (turn.name !== name && priv.name !== name
|
||||
&& !(color in priv.offerRejected)
|
||||
&& (!item.gets || item.gets.length === 0 || !item.gives || item.gives.length === 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const tmp = {
|
||||
negotiator: table.game.turn.name === name,
|
||||
self: table.game.name === name,
|
||||
negotiator: turn.name === name,
|
||||
self: priv.name === name,
|
||||
name: name,
|
||||
color: color,
|
||||
valid: false,
|
||||
@ -256,10 +310,10 @@ const Trade = () => {
|
||||
|
||||
tmp.canSubmit = (tmp.gets.length && tmp.gives.length);
|
||||
|
||||
players.push(tmp);
|
||||
activeTrades.push(tmp);
|
||||
}
|
||||
|
||||
players.sort((A, B) => {
|
||||
activeTrades.sort((A, B) => {
|
||||
if (A.negotiator) {
|
||||
return -1;
|
||||
}
|
||||
@ -291,14 +345,14 @@ const Trade = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const isOfferSubmitted = isCompatibleOffer(table.game.player, trade),
|
||||
isNegiatorSubmitted = table.game.turn && table.game.turn.offer && isCompatibleOffer(table.game.player, table.game.turn.offer),
|
||||
const isOfferSubmitted = isCompatibleOffer(priv, trade),
|
||||
isNegiatorSubmitted = turn && turn.offer && isCompatibleOffer(priv, turn.offer),
|
||||
isOfferValid = trade.gives.length && trade.gets.length ? true : false;
|
||||
|
||||
if (isTurn && table.game.player && table.game.player.banks) {
|
||||
table.game.player.banks.forEach(bank => {
|
||||
if (isTurn && priv && priv.banks) {
|
||||
priv.banks.forEach(bank => {
|
||||
const count = (bank === 'bank') ? 3 : 2;
|
||||
players.push({
|
||||
activeTrades.push({
|
||||
name: `The bank`,
|
||||
color: undefined,
|
||||
gives: [ { count: 1, type: '*' } ],
|
||||
@ -308,7 +362,7 @@ const Trade = () => {
|
||||
});
|
||||
});
|
||||
|
||||
players.push({
|
||||
activeTrades.push({
|
||||
name: `The bank`,
|
||||
color: undefined,
|
||||
gives: [ { count: 1, type: '*' } ],
|
||||
@ -319,21 +373,21 @@ const Trade = () => {
|
||||
}
|
||||
|
||||
if (isTurn) {
|
||||
players.forEach(offer => offer.valid = !(table.game.turn.color in offer.offerRejected) && canMeetOffer(player, offer));
|
||||
activeTrades.forEach(offer => offer.valid = !(turn.color in offer.offerRejected) && canMeetOffer(priv, offer));
|
||||
} else {
|
||||
const found = players.find(item => item.name === table.game.turn.name);
|
||||
const found = activeTrades.find(item => item.name === turn.name);
|
||||
if (found) {
|
||||
found.valid = !(table.game.color in found.offerRejected) && canMeetOffer(player, found);
|
||||
found.valid = !(color in found.offerRejected) && canMeetOffer(priv, found);
|
||||
}
|
||||
}
|
||||
|
||||
players = players.map((item, index) => {
|
||||
const youRejectedOffer = table.game.color in item.offerRejected;
|
||||
const tradeElements = activeTrades.map((item, index) => {
|
||||
const youRejectedOffer = color in item.offerRejected;
|
||||
let youWereRejected;
|
||||
if (isTurn) {
|
||||
youWereRejected = item.color && item.color in player.offerRejected;
|
||||
youWereRejected = item.color && item.color in priv.offerRejected;
|
||||
} else {
|
||||
youWereRejected = Object.getOwnPropertyNames(player.offerRejected).length !== 0;
|
||||
youWereRejected = Object.getOwnPropertyNames(priv.offerRejected).length !== 0;
|
||||
}
|
||||
|
||||
const isNewOffer = item.self && !isOfferSubmitted;
|
||||
@ -345,8 +399,8 @@ const Trade = () => {
|
||||
isSameOffer = isCompatibleOffer(trade,
|
||||
{ gets: item.gives, gives: item.gets });
|
||||
} else {
|
||||
isSameOffer = table.game.turn.offer &&
|
||||
isCompatibleOffer(player, table.game.turn.offer);
|
||||
isSameOffer = turn.offer &&
|
||||
isCompatibleOffer(priv, turn.offer);
|
||||
}
|
||||
|
||||
let source;
|
||||
@ -384,7 +438,7 @@ const Trade = () => {
|
||||
</span> }
|
||||
|
||||
{ youWereRejected && !isNewOffer && <span>
|
||||
{ table.game.turn.name } rejected your offer.
|
||||
{ turn.name } rejected your offer.
|
||||
</span> }
|
||||
|
||||
{ !youWereRejected && _gets === 'nothing' && _gives === 'nothing' && <span>
|
||||
@ -392,7 +446,7 @@ const Trade = () => {
|
||||
</span>}
|
||||
|
||||
{ !isTurn && isSameOffer && isOfferValid && _gets !== 'nothing' && _gives !== 'nothing' && <span style={{fontWeight: 'bold'}}>
|
||||
Your submitted offer agrees with {table.game.turn.name}'s terms.
|
||||
Your submitted offer agrees with {turn.name}'s terms.
|
||||
</span> }
|
||||
</> }
|
||||
|
||||
@ -430,12 +484,12 @@ const Trade = () => {
|
||||
onClick={() => acceptClicked(item)}>accept</Button>
|
||||
}
|
||||
|
||||
{ !isTurn && item.color === table.game.turn.color &&
|
||||
{ !isTurn && item.color === turn.color &&
|
||||
<Button disabled={!item.valid || isNegiatorSubmitted}
|
||||
onClick={() => agreeClicked(item)}>agree</Button>
|
||||
}
|
||||
|
||||
{ item.name !== 'The bank' && !item.self && (isTurn || item.name === table.game.turn.name) &&
|
||||
{ item.name !== 'The bank' && !item.self && (isTurn || item.name === turn.name) &&
|
||||
<Button disabled={!item.gets.length ||
|
||||
!item.gives.length || youRejectedOffer }
|
||||
onClick={() => rejectClicked(item)}>reject</Button>
|
||||
@ -457,16 +511,16 @@ const Trade = () => {
|
||||
<div className="Trade">
|
||||
<Paper>
|
||||
<div className="Title">
|
||||
Trading negotiations {isTurn ? '' : `with ${table.game.turn.name}`}
|
||||
Trading negotiations {isTurn ? '' : `with ${turn.name}`}
|
||||
</div>
|
||||
<div className="PlayerList">
|
||||
{ players }
|
||||
{ tradeElements }
|
||||
</div>
|
||||
{ player.resources === 0 && <b>You have no resources to participate in this trade.</b> }
|
||||
{ priv.resources === 0 && <b>You have no resources to participate in this trade.</b> }
|
||||
<Button disabled={isOfferSubmitted || !isOfferValid}
|
||||
onClick={offerClicked}>Offer</Button>
|
||||
|
||||
{ player.resources !== 0 &&
|
||||
{ priv.resources !== 0 &&
|
||||
<div className="Transfers">
|
||||
<div className="GiveGet"><div>Get</div><div>Give</div><div>Have</div></div>
|
||||
{ transfers }
|
||||
@ -478,4 +532,4 @@ const Trade = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Trade;
|
||||
export { Trade };
|
||||
|
@ -1657,7 +1657,8 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
});
|
||||
|
||||
const trade = (game, session, action, offer) => {
|
||||
const name = session.name;
|
||||
const name = session.name, player = session.player;
|
||||
let warning;
|
||||
|
||||
if (game.state !== "normal") {
|
||||
return `Game not in correct state to begin trading.`;
|
||||
@ -1693,9 +1694,9 @@ const trade = (game, session, action, offer) => {
|
||||
|
||||
/* Any player can make an offer */
|
||||
if (action === 'offer') {
|
||||
error = checkPlayerOffer(game, session.player, offer);
|
||||
if (error) {
|
||||
return error;
|
||||
warning = checkPlayerOffer(game, session.player, offer);
|
||||
if (warning) {
|
||||
return warning;
|
||||
}
|
||||
|
||||
if (isSameOffer(session.player, offer)) {
|
||||
@ -1752,14 +1753,13 @@ const trade = (game, session, action, offer) => {
|
||||
return `Only the active player can accept an offer.`;
|
||||
}
|
||||
|
||||
const offer = req.body;
|
||||
let target;
|
||||
|
||||
console.log({ offer, description: offerToString(offer) });
|
||||
|
||||
error = checkPlayerOffer(game, session.player, offer);
|
||||
if (error) {
|
||||
return error;
|
||||
warning = checkPlayerOffer(game, session.player, offer);
|
||||
if (warning) {
|
||||
return warning;
|
||||
}
|
||||
|
||||
/* Verify that the offer sent by the active player matches what
|
||||
@ -1773,9 +1773,9 @@ const trade = (game, session, action, offer) => {
|
||||
return `Unfortunately, trades were re-negotiated in transit and the deal is invalid!`;
|
||||
}
|
||||
|
||||
error = checkPlayerOffer(game, target, { gives: offer.gets, gets: offer.gives });
|
||||
if (error) {
|
||||
return error;
|
||||
warning = checkPlayerOffer(game, target, { gives: offer.gets, gets: offer.gives });
|
||||
if (warning) {
|
||||
return warning;
|
||||
}
|
||||
|
||||
if (!isSameOffer(target, { gives: offer.gets, gets: offer.gives })) {
|
||||
@ -3494,7 +3494,7 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
}
|
||||
break;
|
||||
case 'trade':
|
||||
console.log(`${short}: <- trade:${getName(session)}`);
|
||||
console.log(`${short}: <- trade:${getName(session)} - ${data.action} - `, data.offer);
|
||||
warning = trade(game, session, data.action, data.offer);
|
||||
if (warning) {
|
||||
sendWarning(session, warning);
|
||||
|
Loading…
x
Reference in New Issue
Block a user