1
0
James Ketrenos f2fabcfd20 Fix #87 -- reverse direction and also fix counter offer messages
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
2022-03-08 12:04:13 -08:00

477 lines
14 KiB
JavaScript

import React, { useState, useCallback, useEffect } from "react";
import "./Trade.css";
import { getPlayerName } from './Common.js';
import PlayerColor from './PlayerColor.js';
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'
const empty = {
wheat: 0,
brick: 0,
wood: 0,
stone: 0,
sheep: 0
};
const Trade = ({table}) => {
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 transfer = useCallback((type, direction) => {
if (direction === 'give') { /* give clicked */
if (gets[type]) {
gets[type]--;
gives[type] = 0;
} else {
if (gives[type] < player[type]) {
gives[type]++;
}
gets[type] = 0;
}
} else if (direction === 'get') { /* get clicked */
if (gives[type]) {
gives[type]--;
gets[type] = 0;
} else {
if (gets[type] < 15) {
gets[type]++;
}
gives[type] = 0;
}
}
setGets({...gets});
setGives({...gives});
}, [setGets, setGives, gets, gives, player]);
const createTransfer = useCallback(resource => {
return <div key={resource} className="Transfer">
<Resource
onClick={() => transfer(resource, 'get')}
label={true}
type={resource}
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}
available={player ? player[resource] - gives[resource] : undefined}
count={gives[resource]}/>
</div>;
}, [ gives, gets, transfer, player]);
useEffect(() => {
if (table.game && table.game.player && table.game.player.gives) {
const _gives = {};
table.game.player.gives.forEach(give => _gives[give.type] = give.count);
setGives(Object.assign({}, empty, _gives));
}
if (table.game && table.game.player && table.game.player.gets) {
const _gets = {};
table.game.player.gets.forEach(get => _gets[get.type] = get.count);
setGets(Object.assign({}, empty, _gets));
}
}, [ setGets, setGives, table.game ]);
const agreeClicked = useCallback((offer) => {
const trade = {
gives: offer.gets.slice(),
gets: offer.gives.slice()
};
let _gives = {}, _gets = {};
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);
console.log(_gives, _gets);
setGives(Object.assign({}, empty, _gives));
setGets(Object.assign({}, empty, _gets));
}, [setGives, setGets, gives, gets, table]);
if (!table.game || !player || !table.game.player) {
return <></>;
}
const transfers = [ 'brick', 'wood', 'wheat', 'sheep', 'stone' ].map(resource => { return createTransfer(resource); });
player.offerRejected = player.offerRejected ? player.offerRejected: {};
const canMeetOffer = (player, offer) => {
if (offer.gets.length === 0 || offer.gives.length === 0) {
return false;
}
for (let i = 0; i < offer.gets.length; i++) {
const get = offer.gets[i];
if (offer.name === 'The bank') {
const _gives = [], _gets = [];
for (let type in gives) {
if (gives[type] > 0) {
_gives.push({ type, count: gives[type] });
}
}
for (let type in gets) {
if (gets[type] > 0) {
_gets.push({ type, count: gets[type] });
}
}
if (_gives.length !== 1 || _gets.length !== 1) {
return false;
}
if (_gives[0].count < get.count) {
return false;
}
if (get.type !== 'bank') {
if (gives[get.type] < get.count) {
return false;
}
}
if (_gets[0].count !== 1) {
return false;
}
} else if (player[get.type] < get.count) {
console.log(`cannot meet count`);
return false;
}
}
return true;
};
const isCompatibleOffer = (player, offer) => {
let valid = player.gets &&
player.gives &&
offer.gets &&
offer.gives &&
player.gets.length === offer.gives.length &&
player.gives.length === offer.gets.length;
if (!valid) {
return false;
}
player.gets.forEach(get => {
if (!valid) {
return;
}
valid = offer.gives.find(item =>
(item.type === get.type || item.type === '*') &&
item.count === get.count) !== undefined;
});
if (valid) player.gives.forEach(give => {
if (!valid) {
return;
}
valid = offer.gets.find(item =>
(item.type === give.type || item.type === 'bank') &&
item.count === give.count) !== undefined;
});
return valid;
};
const isTurn = (table.game.turn && table.game.turn.color === table.game.color) ? true : false;
const offerClicked = (event) => {
const trade = {
gives: [],
gets: []
};
for (let key in gives) {
if (gives[key] !== 0) {
trade.gives.push({type: key, count: gives[key]});
}
}
for (let key in gets) {
if (gets[key] !== 0) {
trade.gets.push({type: key, count: gets[key]});
}
}
table.offerTrade(trade);
}
const cancelOffer = (offer) => {
table.cancelTrade(offer);
}
const acceptClicked = (offer) => {
if (offer.name === 'The bank') {
table.acceptTrade(Object.assign({}, { name: offer.name, gives: trade.gets, gets: trade.gives }));
} else if (offer.self) {
table.acceptTrade(offer);
} else {
table.acceptTrade(Object.assign({}, offer, { gives: offer.gets, gets: offer.gives }));
}
};
const cancelClicked = (event) => {
table.cancelTrading();
}
/* Player has rejected the active player's bid or active player rejected
* the other player's bid */
const rejectClicked = (trade) => {
table.rejectTrade(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);
item.offerRejected = item.offerRejected ? item.offerRejected : {};
if (item.status !== 'Active') {
continue;
}
/* 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)
&& (!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,
name: name,
color: color,
valid: false,
gets: item.gets ? item.gets : [],
gives: item.gives ? item.gives : [],
offerRejected: item.offerRejected,
};
tmp.canSubmit = (tmp.gets.length && tmp.gives.length);
players.push(tmp);
}
players.sort((A, B) => {
if (A.negotiator) {
return -1;
}
if (B.negotiator) {
return +1;
}
if (A.self) {
return -1;
}
if (B.self) {
return +1;
}
return A.name.localeCompare(B.name);
});
const trade = {gives: [], gets: []};
for (let type in gives) {
if (gives[type]) {
trade.gets.push({ type, count: gives[type]});
}
}
for (let type in gets) {
if (gets[type]) {
trade.gives.push({ type, count: gets[type]});
}
}
const isOfferSubmitted = isCompatibleOffer(table.game.player, trade),
isNegiatorSubmitted = table.game.turn && table.game.turn.offer && isCompatibleOffer(table.game.player, table.game.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 => {
const count = (bank === 'bank') ? 3 : 2;
players.push({
name: `The bank`,
color: undefined,
gives: [ { count: 1, type: '*' } ],
gets: [ { count: count, type: bank } ],
valid: false,
offerRejected: {}
});
});
players.push({
name: `The bank`,
color: undefined,
gives: [ { count: 1, type: '*' } ],
gets: [ { count: 4, type: 'bank' } ],
valid: false,
offerRejected: {}
});
}
if (isTurn) {
players.forEach(offer => offer.valid = !(table.game.turn.color in offer.offerRejected) && canMeetOffer(player, offer));
} else {
const found = players.find(item => item.name === table.game.turn.name);
if (found) {
found.valid = !(table.game.color in found.offerRejected) && canMeetOffer(player, found);
}
}
players = players.map((item, index) => {
const youRejectedOffer = table.game.color in item.offerRejected;
let youWereRejected;
if (isTurn) {
youWereRejected = item.color && item.color in player.offerRejected;
} else {
youWereRejected = Object.getOwnPropertyNames(player.offerRejected).length !== 0;
}
const isNewOffer = item.self && !isOfferSubmitted;
let isSameOffer;
const isBank = (item.name === 'The bank');
if (isTurn) {
isSameOffer = isCompatibleOffer(trade,
{ gets: item.gives, gives: item.gets });
} else {
isSameOffer = table.game.turn.offer &&
isCompatibleOffer(player, table.game.turn.offer);
}
let source;
if (item.self) {
/* Order direction is reversed for self */
source = {
name: item.name,
gets: trade.gives,
gives: trade.gets
};
} else {
source = item;
}
const _gets = source.gets.length ? source.gets.map((get, index) => {
if (get.type === 'bank') {
return <span key={`get-bank-${index}`}><b>{get.count}</b> of any resource </span>;
}
return <Resource key={`get-${get.type}-${index}`} disabled label type={get.type} count={get.count}/>;
}) : 'nothing';
const _gives = source.gives.length ? source.gives.map((give, index) => {
if (give.type === '*') {
return <span key={`give-bank-${index}`}><b>1</b> of any resource </span>;
}
return <Resource key={`give-${give.type}-${index}`} disabled label type={give.type} count={give.count}/>;
}) : 'nothing';
return (
<div className="TradeLine" key={`player-${item.name}-${index}`}>
<PlayerColor color={item.color}/>
<div className="TradeText">
{ item.self && <>
{ (_gets !== 'nothing' || _gives !== 'nothing') && <span>
You want {_gets} and will give {_gives}.
</span> }
{ youWereRejected && !isNewOffer && <span>
{ table.game.turn.name } rejected your offer.
</span> }
{ !youWereRejected && _gets === 'nothing' && _gives === 'nothing' && <span>
You have not made a trade offer.
</span>}
{ !isTurn && isSameOffer && isOfferValid && _gets !== 'nothing' && _gives !== 'nothing' && <span style={{fontWeight: 'bold'}}>
Your submitted offer agrees with {table.game.turn.name}'s terms.
</span> }
</> }
{ !item.self && <>
{ (!isTurn || !isSameOffer || isBank) && !youRejectedOffer && _gets !== 'nothing' && _gives !== 'nothing' && <span>
{item.name} wants {_gets} and will give {_gives}.
</span> }
{ !isBank && <>
{ isTurn && !isSameOffer && isOfferValid && !youRejectedOffer && _gets !== 'nothing' && _gives !== 'nothing' && <span style={{fontWeight: 'bold'}}>
This is a counter offer.
</span> }
{ isTurn && isSameOffer && !youRejectedOffer && _gets !== 'nothing' && _gives !== 'nothing' && <span>
{item.name} will meet your terms.
</span> }
{ (!isTurn || !youWereRejected) && (_gets === 'nothing' || _gives === 'nothing') && <span>
{item.name} has not submitted a trade offer.
</span> }
{ youRejectedOffer && <span>
You rejected {item.name}'s offer.
</span> }
{ isTurn && youWereRejected && <span>
{ item.name} rejected your offer.
</span> }
</> }
</>}
</div>
<div className="TradeActions">
{ !item.self && isTurn &&
<Button disabled={!item.valid}
onClick={() => acceptClicked(item)}>accept</Button>
}
{ !isTurn && item.color === table.game.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) &&
<Button disabled={!item.gets.length ||
!item.gives.length || youRejectedOffer }
onClick={() => rejectClicked(item)}>reject</Button>
}
{ item.self &&
<Button disabled={isOfferSubmitted || !isOfferValid} onClick={offerClicked}>Offer</Button>
}
{ item.self &&
<Button disabled onClick={() => cancelOffer(item)}>cancel</Button>
}
</div>
</div>
);
});
return (
<div className="Trade">
<Paper>
<div className="Title">
Trading negotiations {isTurn ? '' : `with ${table.game.turn.name}`}
</div>
<div className="PlayerList">
{ players }
</div>
{ !player.haveResources && <b>You have no resources to participate in this trade.</b> }
<Button disabled={isOfferSubmitted || !isOfferValid}
onClick={offerClicked}>Offer</Button>
{ player.haveResources &&
<div className="Transfers">
<div className="GiveGet"><div>Get</div><div>Give</div><div>Have</div></div>
{ transfers }
</div>
}
{ isTurn && <Button onClick={cancelClicked}>cancel</Button> }
</Paper>
</div>
);
};
export default Trade;