349 lines
10 KiB
JavaScript
349 lines
10 KiB
JavaScript
import React, { useState, useCallback } 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';
|
|
|
|
const ResourceCounter = ({type, count, onCount, max}) => {
|
|
count = count ? count : 0;
|
|
const plusClicked = (event) => {
|
|
if (max === undefined || max > count) {
|
|
if (onCount) {
|
|
onCount(type, count+1);
|
|
}
|
|
}
|
|
};
|
|
const minusClicked = (event) => {
|
|
if (count > 0) {
|
|
if (onCount) {
|
|
onCount(type, count-1);
|
|
}
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="ResourceCounter" style={{
|
|
display: 'flex',
|
|
flexDirection: 'column'
|
|
}}>
|
|
<Resource disabled type={type} count={1}/>
|
|
<div style={{
|
|
display: 'flex',
|
|
justifyContent: 'space-around',
|
|
width: '100%',
|
|
alignItems: 'center',
|
|
flexDirection: 'column'
|
|
}}>
|
|
<div style={{userSelect: 'none'}}>{count}</div>
|
|
<div>
|
|
<Button style={{
|
|
flexShrink: 1,
|
|
minWidth: 0
|
|
}}
|
|
disabled={count>=max}
|
|
onClick={plusClicked}>+</Button>
|
|
<Button style={{
|
|
flexShrink: 1,
|
|
minWidth: 0
|
|
}}
|
|
disabled={count===0}
|
|
onClick={minusClicked}>-</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const Trade = ({table}) => {
|
|
const [giveLine, setGiveLine] = useState('nothing');
|
|
const [getLine, setGetLine] = useState('nothing');
|
|
|
|
const [gives, setGives] = useState([]);
|
|
const [gets, setGets] = useState([]);
|
|
|
|
const giveCount = useCallback((type, count) => {
|
|
gives[type] = count;
|
|
if (!count) {
|
|
delete gives[type];
|
|
}
|
|
setGives(gives);
|
|
const items = [];
|
|
for (let key in gives) {
|
|
items.push(`${gives[key]} ${key}`);
|
|
}
|
|
if (items.length === 0) {
|
|
setGiveLine('nothing');
|
|
} else {
|
|
setGiveLine(items.join(', '));
|
|
}
|
|
}, [setGiveLine, setGives, gives]);
|
|
|
|
const getCount = useCallback((type, count) => {
|
|
gets[type] = count;
|
|
if (!count) {
|
|
delete gets[type];
|
|
}
|
|
setGets(gets);
|
|
const items = [];
|
|
for (let key in gets) {
|
|
items.push(`${gets[key]} ${key}`);
|
|
}
|
|
if (items.length === 0) {
|
|
setGetLine('nothing');
|
|
} else {
|
|
setGetLine(items.join(', '));
|
|
}
|
|
}, [setGetLine, setGets, gets]);
|
|
|
|
const meetClicked = useCallback((offer) => {
|
|
const trade = {
|
|
gives: offer.gets.slice(),
|
|
gets: offer.gives.slice()
|
|
};
|
|
trade.gives.forEach(give => giveCount(give.type, give.count));
|
|
trade.gets.forEach(get => getCount(get.type, get.count));
|
|
table.offerTrade(trade);
|
|
let tmp = {};
|
|
trade.gives.forEach(give => {
|
|
tmp[give.type] = give.count;
|
|
});
|
|
setGives(tmp);
|
|
tmp = {};
|
|
trade.gets.forEach(get => {
|
|
tmp[get.type] = get.count;
|
|
});
|
|
setGets(tmp);
|
|
}, [giveCount, getCount, setGives, setGets, table]);
|
|
|
|
if (!table.game) {
|
|
return (<></>);
|
|
}
|
|
|
|
const game = table.game;
|
|
|
|
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) {
|
|
trade.gives.push({type: key, count: gives[key]});
|
|
}
|
|
for (let key in gets) {
|
|
trade.gets.push({type: key, count: gets[key]});
|
|
}
|
|
table.offerTrade(trade);
|
|
}
|
|
|
|
const acceptClicked = (offer) => {
|
|
table.acceptTrade(offer);
|
|
};
|
|
|
|
const cancelClicked = (event) => {
|
|
table.cancelTrading();
|
|
}
|
|
|
|
/* Non-current player has rejected the active player's
|
|
* bid */
|
|
const rejectClicked = (trade) => {
|
|
table.rejectTrade(trade);
|
|
}
|
|
|
|
let players = [];
|
|
for (let color in table.game.players) {
|
|
const item = table.game.players[color],
|
|
name = getPlayerName(table.game.sessions, color);
|
|
if (name && table.game.name !== name) {
|
|
players.push({
|
|
name: name,
|
|
color: color,
|
|
valid: false,
|
|
gets: item.gets ? item.gets : [],
|
|
gives: item.gives ? item.gives : [],
|
|
offerRejected: item.offerRejected ? true : false
|
|
});
|
|
}
|
|
}
|
|
|
|
players.sort((A, B) => {
|
|
return A.name.localeCompare(B.name);
|
|
});
|
|
|
|
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
|
|
});
|
|
});
|
|
|
|
players.push({
|
|
name: `The bank`,
|
|
color: undefined,
|
|
gives: [ { count: 1, type: '*' } ],
|
|
gets: [ { count: 4, type: 'bank' } ],
|
|
valid: false
|
|
});
|
|
}
|
|
|
|
const player = (table.game && table.game.player) ? table.game.player : undefined;
|
|
if (!player) {
|
|
return <></>;
|
|
}
|
|
|
|
let canAccept = false;
|
|
|
|
if (table.game.turn.offer) {
|
|
players.forEach(trade => {
|
|
trade.valid = trade.gets.length
|
|
&& trade.gives.length
|
|
&& trade.gets.length === game.turn.offer.gives.length
|
|
&& trade.gives.length === game.turn.offer.gets.length;
|
|
if (!trade.valid) {
|
|
return;
|
|
}
|
|
trade.gets.forEach(get => {
|
|
if (!trade.valid) {
|
|
return;
|
|
}
|
|
if (get.type !== 'bank') {
|
|
const offer = table.game.turn.offer.gives.find(give => give.type === get.type);
|
|
trade.valid = offer && (offer.count === get.count);
|
|
} else {
|
|
/* Doesn't matter what the resource type is so long as there
|
|
* are enough of the one kind */
|
|
trade.valid = table.game.turn.offer.gives[0].count === get.count;
|
|
}
|
|
});
|
|
|
|
if (!trade.valid) {
|
|
return;
|
|
}
|
|
|
|
trade.gives.forEach(give => {
|
|
if (!trade.valid) {
|
|
return;
|
|
}
|
|
if (give.type !== '*') {
|
|
const offer = table.game.turn.offer.gets.find(get => give.type === get.type);
|
|
trade.valid = offer && (offer.count === give.count);
|
|
} else {
|
|
/* Doesn't matter what the resource type is so long as there
|
|
* are enough of the one kind */
|
|
trade.valid = table.game.turn.offer.gets[0].count === give.count;
|
|
}
|
|
})
|
|
});
|
|
|
|
canAccept = true;
|
|
table.game.turn.offer.gets.forEach(item => {
|
|
if (!canAccept) {
|
|
canAccept = (item.type in game.player);
|
|
}
|
|
if (!canAccept) {
|
|
return;
|
|
}
|
|
canAccept = (game.player[item.type] >= item.count);
|
|
});
|
|
}
|
|
|
|
players = players.map((item, index) => {
|
|
if (item.offerRejected) {
|
|
return <div className="TradePlayer" key={`player-${item.name}-${index}`}>
|
|
<PlayerColor color={item.color}/>
|
|
<div>{item.name}</div>
|
|
<div className='TradeLine'>
|
|
has rejected your offer.
|
|
</div>
|
|
</div>;
|
|
}
|
|
|
|
const gets = item.gets.map(get =>
|
|
`${get.count} ${(get.type === 'bank') ? 'of any one resource' : get.type}`)
|
|
.join(', '),
|
|
gives = item.gives.map(give =>
|
|
`${give.count} ${(give.type === '*') ? 'of any resource' : give.type}`)
|
|
.join(', ');
|
|
return (
|
|
<div className="TradePlayer" key={`player-${item.name}-${index}`}>
|
|
<PlayerColor color={item.color}/>
|
|
<div>{item.name}</div>
|
|
<div className='TradeLine'>
|
|
{ gets !== '' && gives !== '' &&
|
|
<div>wants {gets} and will give {gives}.
|
|
</div>
|
|
}
|
|
{ (gets === '' || gives === '') &&
|
|
<div>has not submitted a trade offer.
|
|
</div>
|
|
}
|
|
{ isTurn && <Button disabled={!item.valid}
|
|
onClick={() => acceptClicked(item)}>accept</Button> }
|
|
{ !isTurn && item.color === table.game.turn.color && <>
|
|
<Button disabled={!canAccept}
|
|
onClick={() => meetClicked(item)}>meet</Button>
|
|
<Button disabled={!item.gets.length ||
|
|
!item.gives.length || player.offerRejected}
|
|
onClick={() => rejectClicked(item)}>reject</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={getLine === 'nothing' || giveLine === 'nothing'}
|
|
onClick={offerClicked}>Offer to give {giveLine} in exchange for {getLine}</Button>
|
|
|
|
{ player.haveResources &&
|
|
<div className='TradeLine'
|
|
style={{
|
|
flexDirection: 'column',
|
|
alignItems: 'flex-start'
|
|
}}>
|
|
<div style={{display: 'flex' }}>
|
|
<b>You want to receive {getLine}:</b>
|
|
</div>
|
|
<div style={{display: 'flex' }}>
|
|
<ResourceCounter count={gets.brick} onCount={getCount} type='brick'/>
|
|
<ResourceCounter count={gets.wood} onCount={getCount} type='wood'/>
|
|
<ResourceCounter count={gets.wheat} onCount={getCount} type='wheat'/>
|
|
<ResourceCounter count={gets.sheep} onCount={getCount} type='sheep'/>
|
|
<ResourceCounter count={gets.stone} onCount={getCount} type='stone'/>
|
|
</div>
|
|
<div style={{display: 'flex' }}>
|
|
<b>You are willing to give {giveLine}:</b>
|
|
</div>
|
|
<div style={{display: 'flex' }}>
|
|
{ player.brick > 0 && <ResourceCounter count={gives.brick} onCount={giveCount} type='brick' max={player.brick}/> }
|
|
{ player.wood > 0 && <ResourceCounter count={gives.wood} onCount={giveCount} type='wood' max={player.wood}/> }
|
|
{ player.wheat > 0 && <ResourceCounter count={gives.wheat} onCount={giveCount} type='wheat' max={player.wheat}/> }
|
|
{ player.sheep > 0 && <ResourceCounter count={gives.sheep} onCount={giveCount} type='sheep' max={player.sheep}/> }
|
|
{ player.stone > 0 && <ResourceCounter count={gives.stone} onCount={giveCount} type='stone' max={player.stone}/> }
|
|
</div>
|
|
</div>
|
|
}
|
|
{ isTurn && <Button onClick={cancelClicked}>cancel</Button> }
|
|
</Paper>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Trade; |