Adding multiplayer info bar, game settings, cleaning up loans.
This commit is contained in:
@@ -310,7 +310,7 @@ class PlayerTurnContainer extends React.Component {
|
|||||||
let view;
|
let view;
|
||||||
const player = this.props.player,
|
const player = this.props.player,
|
||||||
worth = netWorth(player),
|
worth = netWorth(player),
|
||||||
auditButton = (this.props.game.calledAudit === false && worth >= this.props.game.auditThreshold) ? (
|
auditButton = (this.props.game.calledAudit === false && worth >= this.props.game.settings.auditThreshold) ? (
|
||||||
<Button onClick={audit}>Call Audit</Button>) : '';
|
<Button onClick={audit}>Call Audit</Button>) : '';
|
||||||
if (player.state === GAME_STATES.preTurn) {
|
if (player.state === GAME_STATES.preTurn) {
|
||||||
view = (
|
view = (
|
||||||
@@ -505,7 +505,22 @@ class Messages extends React.Component {
|
|||||||
class Loans extends React.Component {
|
class Loans extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { repay: 0, takeOut: 0 };
|
this.state = { repay: 0, takeOut: 0,
|
||||||
|
maxLoan: this.getMaxLoan(props) };
|
||||||
|
}
|
||||||
|
|
||||||
|
getMaxLoan = props => {
|
||||||
|
let i = 0,
|
||||||
|
maxLoan = 0,
|
||||||
|
loanInterest = props.game.settings.loanInterest,
|
||||||
|
maxDebt = props.game.settings.maxDebt;
|
||||||
|
const max = maxDebt - props.player.debt;
|
||||||
|
while (maxLoan <= max) {
|
||||||
|
maxLoan = i * (loanInterest + 1);
|
||||||
|
i += 1000;
|
||||||
|
}
|
||||||
|
i -= 1000;
|
||||||
|
return i - 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInput = e => {
|
handleInput = e => {
|
||||||
@@ -520,10 +535,10 @@ class Loans extends React.Component {
|
|||||||
} else {
|
} else {
|
||||||
takeOut = value * 1000;
|
takeOut = value * 1000;
|
||||||
}
|
}
|
||||||
this.setState({ repay: Math.max(0, Math.floor(
|
this.setState({ repay: Math.max(0, Math.ceil(
|
||||||
Math.min(repay, this.props.player.debt, this.props.player.cash) / 1000)),
|
Math.min(repay, this.props.player.debt + 900, this.props.player.cash) / 1000)),
|
||||||
takeOut: Math.max(0, Math.floor(
|
takeOut: Math.max(0, Math.floor(
|
||||||
Math.min(takeOut, 50000 - this.props.player.debt) / 1000)) });
|
Math.min(takeOut, this.state.maxLoan) / 1000)) });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit = e => {
|
handleSubmit = e => {
|
||||||
@@ -532,11 +547,22 @@ class Loans extends React.Component {
|
|||||||
this.setState({ repay: 0, takeOut: 0 });
|
this.setState({ repay: 0, takeOut: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if (this.props.player.debt !== prevProps.player.debt ||
|
||||||
|
this.props.game.settings.loanInterest !== prevProps.game.settings.loanInterest ||
|
||||||
|
this.props.game.settings.maxDebt !== prevProps.game.settings.maxDebt) {
|
||||||
|
this.setState({ maxLoan: this.getMaxLoan(this.props) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<GroupBox title={'Loans'}>
|
<GroupBox title={'Loans'}>
|
||||||
<MoneySummary title='Cash' value={this.props.player.cash} /> {' '}
|
<MoneySummary title='Cash' value={this.props.player.cash} /> {' '}
|
||||||
<MoneySummary title='Debt' value={this.props.player.debt} />/$50,000
|
<MoneySummary title='Debt' value={this.props.player.debt} />
|
||||||
|
/${formatMoney(this.props.game.settings.maxDebt)}
|
||||||
|
<br />
|
||||||
|
{this.props.game.settings.loanInterest > 0 ? '(' + (this.props.game.settings.loanInterest * 100) + '% interest added to each loan)' : ''}
|
||||||
<br /><br />
|
<br /><br />
|
||||||
<form onSubmit={this.handleSubmit}>
|
<form onSubmit={this.handleSubmit}>
|
||||||
<Row collapse='true'>
|
<Row collapse='true'>
|
||||||
@@ -558,7 +584,7 @@ class Loans extends React.Component {
|
|||||||
$:
|
$:
|
||||||
<input onChange={this.handleInput} name='takeOut' type='number'
|
<input onChange={this.handleInput} name='takeOut' type='number'
|
||||||
value={this.state.takeOut === 0 ? '' : this.state.takeOut} />
|
value={this.state.takeOut === 0 ? '' : this.state.takeOut} />
|
||||||
{'\u00A0'}K / ${(50000 - this.props.player.debt) / 1000}K
|
{'\u00A0'}K / ${this.state.maxLoan / 1000}K
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
<Col width='4'>
|
<Col width='4'>
|
||||||
@@ -814,7 +840,6 @@ class Harvest extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.props.player.name === this.props.game.currentPlayer TODO
|
|
||||||
render() {
|
render() {
|
||||||
let view;
|
let view;
|
||||||
const isCurrentPlayer = this.props.player.name === this.props.game.currentPlayer;
|
const isCurrentPlayer = this.props.player.name === this.props.game.currentPlayer;
|
||||||
@@ -841,7 +866,7 @@ class Harvest extends React.Component {
|
|||||||
case 'roll':
|
case 'roll':
|
||||||
view = (<Die decay={true} num={this.props.rolled} ms={2000} roll={true}
|
view = (<Die decay={true} num={this.props.rolled} ms={2000} roll={true}
|
||||||
showScreen={() => this.nextView('income')}
|
showScreen={() => this.nextView('income')}
|
||||||
skip={true}
|
skip={this.props.player.name === this.props.game.currentPlayer}
|
||||||
autoSkip={this.props.autoSkip === 'die'}
|
autoSkip={this.props.autoSkip === 'die'}
|
||||||
showScreenDelay={2000} />);
|
showScreenDelay={2000} />);
|
||||||
break;
|
break;
|
||||||
@@ -1088,7 +1113,7 @@ class Action extends React.Component {
|
|||||||
<div>
|
<div>
|
||||||
{this.props.player.name === this.props.game.currentPlayer ? 'You' :
|
{this.props.player.name === this.props.game.currentPlayer ? 'You' :
|
||||||
this.props.game.currentPlayer} {this.props.ui.actionValue < 0 ? 'lost ' : 'gained '}
|
this.props.game.currentPlayer} {this.props.ui.actionValue < 0 ? 'lost ' : 'gained '}
|
||||||
${Math.abs(this.props.ui.actionValue)}!
|
${formatMoney(Math.abs(this.props.ui.actionValue))}!
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
@@ -1104,7 +1129,6 @@ class Action extends React.Component {
|
|||||||
break;
|
break;
|
||||||
case 'harvest':
|
case 'harvest':
|
||||||
view = (<Harvest rolled={this.props.ui.actionValue.rolled}
|
view = (<Harvest rolled={this.props.ui.actionValue.rolled}
|
||||||
autoSkip={this.props.ui.autoSkip}
|
|
||||||
player={this.props.player}
|
player={this.props.player}
|
||||||
game={this.props.game}
|
game={this.props.game}
|
||||||
income={this.props.ui.actionValue.income}
|
income={this.props.ui.actionValue.income}
|
||||||
@@ -1250,13 +1274,14 @@ class AlertOverlay extends React.Component {
|
|||||||
this.props.visible : true };
|
this.props.visible : true };
|
||||||
}
|
}
|
||||||
|
|
||||||
hide = () => {
|
hide = e => {
|
||||||
|
e.preventDefault();
|
||||||
this.setState({ visible: false });
|
this.setState({ visible: false });
|
||||||
this.props.hideHandler();
|
this.props.hideHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonClick = () => {
|
buttonClick = e => {
|
||||||
this.hide();
|
this.hide(e);
|
||||||
this.props.handler();
|
this.props.handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1271,13 +1296,27 @@ class AlertOverlay extends React.Component {
|
|||||||
<br />
|
<br />
|
||||||
<Button onClick={this.buttonClick}>{this.props.buttonText}</Button>
|
<Button onClick={this.buttonClick}>{this.props.buttonText}</Button>
|
||||||
<label><input type='checkbox' onClick={this.hidePermanent} /> {`Don't show again`}</label>
|
<label><input type='checkbox' onClick={this.hidePermanent} /> {`Don't show again`}</label>
|
||||||
<a href='#' onClick={this.hide}>close</a>
|
<a onClick={this.hide}>close</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class InfoBar extends React.Component {
|
||||||
|
render() {
|
||||||
|
if (this.props.players.length > 0) {
|
||||||
|
return (
|
||||||
|
<div className='info-bar'>
|
||||||
|
{(this.props.screen === SCREENS.action) ? '' : this.props.message}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (<Fragment />);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const SCREENS = { summary: 'summary', misc: 'misc', farms: 'farms',
|
const SCREENS = { summary: 'summary', misc: 'misc', farms: 'farms',
|
||||||
cards: 'cards', trade: 'trade', loans: 'loans',
|
cards: 'cards', trade: 'trade', loans: 'loans',
|
||||||
action: 'action' };
|
action: 'action' };
|
||||||
@@ -1329,12 +1368,29 @@ class BoardApp extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
iconOnClick = icon => {
|
iconOnClick = icon => {
|
||||||
return () => this.showScreen(this.iconToScreen[icon]);
|
return e => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.showScreen(this.iconToScreen[icon]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let alertOverlay;
|
let alertOverlay;
|
||||||
if (!this.props.ui.alertHandled && this.state.screen !== SCREENS.action) {
|
if (!this.props.ui.alertHandled && this.props.ui.alert === ALERTS.endOfGame) {
|
||||||
|
alertOverlay = (
|
||||||
|
<AlertOverlay visible={true}
|
||||||
|
buttonText='Close'
|
||||||
|
hideHandler={() => this.props.alert(false)}
|
||||||
|
handler={() => { return false; }}>
|
||||||
|
<Fragment>
|
||||||
|
<h1>Game Over!</h1>
|
||||||
|
{this.props.ui.alertContents.map((e, i) => (
|
||||||
|
<p key={i}>{e}</p>
|
||||||
|
))}
|
||||||
|
</Fragment>
|
||||||
|
</AlertOverlay>
|
||||||
|
);
|
||||||
|
} else if (!this.props.ui.alertHandled && this.state.screen !== SCREENS.action) {
|
||||||
switch (this.props.ui.alert) {
|
switch (this.props.ui.alert) {
|
||||||
case ALERTS.beginTurn:
|
case ALERTS.beginTurn:
|
||||||
alertOverlay = (
|
alertOverlay = (
|
||||||
@@ -1384,20 +1440,23 @@ class BoardApp extends React.Component {
|
|||||||
<div className='game-container'>
|
<div className='game-container'>
|
||||||
{alertOverlay}
|
{alertOverlay}
|
||||||
<Board spaces={this.props.spaces}>
|
<Board spaces={this.props.spaces}>
|
||||||
|
<InfoBar message={this.props.ui.message}
|
||||||
|
screen={this.state.screen}
|
||||||
|
players={this.props.game.otherPlayers} />
|
||||||
<div className='center-board-container'>
|
<div className='center-board-container'>
|
||||||
<ul className='horizontal menu icons icons-top'>
|
<ul className='horizontal menu icons icons-top'>
|
||||||
{[faUser, faTractor, faWindowRestore, faDollarSign, faUsers, faAsterisk]
|
{[faUser, faTractor, faWindowRestore, faDollarSign, faUsers, faAsterisk]
|
||||||
.map((icon, i) =>
|
.map((icon, i) =>
|
||||||
(<li key={i} className={this.iconClass(icon.iconName)}>
|
(<li key={i} className={this.iconClass(icon.iconName)}>
|
||||||
<div></div>
|
<div></div>
|
||||||
<a href='#' onClick={this.iconOnClick(icon.iconName)}>
|
<a onClick={this.iconOnClick(icon.iconName)}>
|
||||||
<FontAwesomeIcon icon={icon} /></a></li>))}
|
<FontAwesomeIcon icon={icon} /></a></li>))}
|
||||||
</ul>
|
</ul>
|
||||||
<ul className='vertical menu icons icons-top'>
|
<ul className='vertical menu icons icons-top'>
|
||||||
{[faUser, faTractor, faWindowRestore, faDollarSign, faUsers, faAsterisk]
|
{[faUser, faTractor, faWindowRestore, faDollarSign, faUsers, faAsterisk]
|
||||||
.map((icon, i) =>
|
.map((icon, i) =>
|
||||||
(<li key={i} className={this.iconClass(icon.iconName)}>
|
(<li key={i} className={this.iconClass(icon.iconName)}>
|
||||||
<a href='#' onClick={this.iconOnClick(icon.iconName)}>
|
<a onClick={this.iconOnClick(icon.iconName)}>
|
||||||
<FontAwesomeIcon icon={icon} /></a></li>))}
|
<FontAwesomeIcon icon={icon} /></a></li>))}
|
||||||
</ul>
|
</ul>
|
||||||
<div className='tab-container'>
|
<div className='tab-container'>
|
||||||
@@ -1426,14 +1485,14 @@ class BoardApp extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className='cell medium-auto'>
|
<div className='cell medium-auto'>
|
||||||
<Card ui={this.props.ui}
|
<Card ui={this.props.ui}
|
||||||
min={(this.props.ui.card.total * 0.2) / 1000}
|
min={(this.props.ui.card.total * this.props.game.settings.downPayment) / 1000}
|
||||||
max={Math.floor(Math.min(this.props.player.cash / 1000, this.props.ui.card.total / 1000))}
|
max={Math.floor(Math.min(this.props.player.cash / 1000, this.props.ui.card.total / 1000))}
|
||||||
cash={(this.props.ui.card.total * 0.2) / 1000} />
|
cash={(this.props.ui.card.total * this.props.game.settings.downPayment) / 1000} />
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
<div className={this.tabClass(SCREENS.loans)}>
|
<div className={this.tabClass(SCREENS.loans)}>
|
||||||
<Loans player={this.props.player} />
|
<Loans player={this.props.player} game={this.props.game} />
|
||||||
</div>
|
</div>
|
||||||
<div className={this.tabClass(SCREENS.farms)}>
|
<div className={this.tabClass(SCREENS.farms)}>
|
||||||
<FarmsContainer player={this.props.player}
|
<FarmsContainer player={this.props.player}
|
||||||
@@ -1505,9 +1564,12 @@ class Card extends React.Component {
|
|||||||
<div className='money'>
|
<div className='money'>
|
||||||
$:
|
$:
|
||||||
<input id='cash-input' type='number'
|
<input id='cash-input' type='number'
|
||||||
|
step='1'
|
||||||
|
min='0'
|
||||||
|
max={Math.ceil(this.props.max)}
|
||||||
disabled={this.props.max < this.props.min}
|
disabled={this.props.max < this.props.min}
|
||||||
onChange={this.handleInput}
|
onChange={this.handleInput}
|
||||||
value={this.state.cash} />
|
value={Math.ceil(this.state.cash)} />
|
||||||
{'\u00A0'},000
|
{'\u00A0'},000
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
@@ -35,3 +35,4 @@ export const MARK_ACTION_CHANGE_HANDLED = 'mark-action-change-handled'
|
|||||||
export const ALERT = 'alert'
|
export const ALERT = 'alert'
|
||||||
export const ALERT_HANDLED = 'alert-handled'
|
export const ALERT_HANDLED = 'alert-handled'
|
||||||
export const AUTO_SKIP = 'auto-skip'
|
export const AUTO_SKIP = 'auto-skip'
|
||||||
|
export const MESSAGE = 'message'
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ import { UPDATE_GAME, UPDATE_PLAYER, GAME_STATE, SET_SELECTED_CARD, SET_CARDS,
|
|||||||
SPACE_PUSH_PLAYER, SPACE_CLEAR_PLAYERS, SET_OLD_MESSAGES, MESSAGE_PANEL_SPACE,
|
SPACE_PUSH_PLAYER, SPACE_CLEAR_PLAYERS, SET_OLD_MESSAGES, MESSAGE_PANEL_SPACE,
|
||||||
MP_MOUSE, SET_MP_DIMS, MARK_ACTION_CHANGE_HANDLED, SET_NEXT_ACTION,
|
MP_MOUSE, SET_MP_DIMS, MARK_ACTION_CHANGE_HANDLED, SET_NEXT_ACTION,
|
||||||
MOVE_PLAYER, NEXT_UI_ACTION, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED,
|
MOVE_PLAYER, NEXT_UI_ACTION, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED,
|
||||||
AUTO_SKIP } from './actionTypes.js'
|
AUTO_SKIP, MESSAGE } from './actionTypes.js'
|
||||||
|
|
||||||
export { updateGame, updatePlayer, gameState, setSelectedCard, setCards,
|
export { updateGame, updatePlayer, gameState, setSelectedCard, setCards,
|
||||||
spacePushPlayer, spaceClearPlayers, setOldMessages, setMessagePanelSpace,
|
spacePushPlayer, spaceClearPlayers, setOldMessages, setMessagePanelSpace,
|
||||||
mpMouse, setMPDims, movePlayer, setNextAction, nextUIAction,
|
mpMouse, setMPDims, movePlayer, setNextAction, nextUIAction,
|
||||||
markActionChangeHandled, nextUIActionSilent, alert, alertHandled,
|
markActionChangeHandled, nextUIActionSilent, alert, alertHandled,
|
||||||
autoSkip }
|
autoSkip, message }
|
||||||
|
|
||||||
function updateGame(update) {
|
function updateGame(update) {
|
||||||
return { type: UPDATE_GAME,
|
return { type: UPDATE_GAME,
|
||||||
@@ -107,8 +107,8 @@ function markActionChangeHandled() {
|
|||||||
return { type: MARK_ACTION_CHANGE_HANDLED };
|
return { type: MARK_ACTION_CHANGE_HANDLED };
|
||||||
}
|
}
|
||||||
|
|
||||||
function alert(value) {
|
function alert(value, contents) {
|
||||||
return { type: ALERT, value };
|
return { type: ALERT, value, contents };
|
||||||
}
|
}
|
||||||
|
|
||||||
function alertHandled() {
|
function alertHandled() {
|
||||||
@@ -118,3 +118,7 @@ function alertHandled() {
|
|||||||
function autoSkip(component) {
|
function autoSkip(component) {
|
||||||
return { type: AUTO_SKIP, component };
|
return { type: AUTO_SKIP, component };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function message(message) {
|
||||||
|
return { type: MESSAGE, message };
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ import * as websocket from '../../websocket.js'
|
|||||||
import { updateGame, updatePlayer, gameState, setSelectedCard, setCards,
|
import { updateGame, updatePlayer, gameState, setSelectedCard, setCards,
|
||||||
movePlayer, setOldMessages, markActionChangeHandled,
|
movePlayer, setOldMessages, markActionChangeHandled,
|
||||||
mpMouse, rolled, setNextAction, nextUIAction, nextUIActionSilent, alert,
|
mpMouse, rolled, setNextAction, nextUIAction, nextUIActionSilent, alert,
|
||||||
autoSkip } from './actions.js'
|
autoSkip, message, alertHandled } from './actions.js'
|
||||||
|
import { itemCard, fateCard } from 'game.js'
|
||||||
|
|
||||||
export { initialize, buy, roll, endTurn, loan, trade, submitTradeAccept,
|
export { initialize, buy, roll, endTurn, loan, trade, submitTradeAccept,
|
||||||
submitTradeDeny, submitTradeCancel, audit, handleMessage,
|
submitTradeDeny, submitTradeCancel, audit, handleMessage,
|
||||||
@@ -47,10 +48,11 @@ function handleMessage(evt) {
|
|||||||
data.game.otherPlayers.length > 0 &&
|
data.game.otherPlayers.length > 0 &&
|
||||||
store.getState().farm.player.state !== GAME_STATES.preTurn) {
|
store.getState().farm.player.state !== GAME_STATES.preTurn) {
|
||||||
store.dispatch(alert(ALERTS.beginTurn));
|
store.dispatch(alert(ALERTS.beginTurn));
|
||||||
} else if (data.game.otherPlayers.length > 0 &&
|
|
||||||
data.game.currentPlayer !== store.getState().farm.game.currentPlayer) {
|
|
||||||
store.dispatch(alert(ALERTS.otherPlayersTurn));
|
|
||||||
}
|
}
|
||||||
|
// else if (data.game.otherPlayers.length > 0 &&
|
||||||
|
// data.game.currentPlayer !== store.getState().farm.game.currentPlayer) {
|
||||||
|
// store.dispatch(alert(ALERTS.otherPlayersTurn));
|
||||||
|
// }
|
||||||
// new turn clear actions
|
// new turn clear actions
|
||||||
if (data.event === 'update' &&
|
if (data.event === 'update' &&
|
||||||
data.game.currentPlayer !== store.getState().farm.game.currentPlayer) {
|
data.game.currentPlayer !== store.getState().farm.game.currentPlayer) {
|
||||||
@@ -93,10 +95,15 @@ function handleMessage(evt) {
|
|||||||
data.player.cash < 0 &&
|
data.player.cash < 0 &&
|
||||||
!store.getState().farm.ui.nextAction) {
|
!store.getState().farm.ui.nextAction) {
|
||||||
store.dispatch(alert(ALERTS.raiseMoney));
|
store.dispatch(alert(ALERTS.raiseMoney));
|
||||||
|
} else if (data.player.state === GAME_STATES.midTurn) {
|
||||||
|
store.dispatch(alertHandled());
|
||||||
}
|
}
|
||||||
if (data.event === 'auto-skip') {
|
if (data.event === 'auto-skip') {
|
||||||
store.dispatch(autoSkip(data.component));
|
store.dispatch(autoSkip(data.component));
|
||||||
}
|
}
|
||||||
|
if (data.event === 'end-of-game') {
|
||||||
|
store.dispatch(alert(ALERTS.endOfGame, data.results));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -158,10 +165,19 @@ function skip(component) {
|
|||||||
sendCommand({ type: 'skip', component });
|
sendCommand({ type: 'skip', component });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO share with Board.jsx
|
||||||
|
// http://stackoverflow.com/questions/149055
|
||||||
|
function formatMoney(n) {
|
||||||
|
return n.toFixed(1).replace(/(\d)(?=(\d{3})+\.)/g, '$1,').slice(0, -2); }
|
||||||
|
|
||||||
function initialize(st, sc) {
|
function initialize(st, sc) {
|
||||||
store = st;
|
store = st;
|
||||||
sendCommand = sc;
|
sendCommand = sc;
|
||||||
|
|
||||||
|
let lastAction = false,
|
||||||
|
lastAutoSkip = false,
|
||||||
|
rollMessage = '',
|
||||||
|
rollTimer = false;
|
||||||
const unsubscribe = store.subscribe(
|
const unsubscribe = store.subscribe(
|
||||||
() => {
|
() => {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
@@ -170,6 +186,73 @@ function initialize(st, sc) {
|
|||||||
store.dispatch(markActionChangeHandled());
|
store.dispatch(markActionChangeHandled());
|
||||||
nextAction();
|
nextAction();
|
||||||
}
|
}
|
||||||
|
if (state.farm.player.name !== state.farm.game.currentPlayer &&
|
||||||
|
lastAction !== state.farm.ui.action) {
|
||||||
|
lastAction = state.farm.ui.action;
|
||||||
|
switch (lastAction) {
|
||||||
|
case 'roll':
|
||||||
|
const roll = state.farm.ui.actionValue.to -
|
||||||
|
(state.farm.ui.actionValue.to < state.farm.ui.actionValue.from ?
|
||||||
|
state.farm.ui.actionValue.from - 49 : state.farm.ui.actionValue.from);
|
||||||
|
rollMessage = state.farm.game.currentPlayer + ' rolled a ' + roll;
|
||||||
|
rollTimer = setInterval(() => {
|
||||||
|
store.dispatch(message(rollMessage));
|
||||||
|
clearInterval(rollTimer);
|
||||||
|
}, 5000);
|
||||||
|
break;
|
||||||
|
case 'money':
|
||||||
|
store.dispatch(message(state.farm.game.currentPlayer +
|
||||||
|
(state.farm.ui.actionValue < 0 ? ' lost ' : ' gained ') +
|
||||||
|
'$' + formatMoney(Math.abs(state.farm.ui.actionValue))));
|
||||||
|
break;
|
||||||
|
case 'farmers-fate':
|
||||||
|
store.dispatch(message(state.farm.game.currentPlayer + ' drew a ' +
|
||||||
|
fateCard));
|
||||||
|
break;
|
||||||
|
case 'ff-uncle-bert':
|
||||||
|
store.dispatch(message(state.farm.game.currentPlayer + ' drew a ' +
|
||||||
|
fateCard));
|
||||||
|
break;
|
||||||
|
case 'otb':
|
||||||
|
store.dispatch(message(state.farm.game.currentPlayer + ' drew an ' +
|
||||||
|
itemCard));
|
||||||
|
break;
|
||||||
|
case 'harvest':
|
||||||
|
store.dispatch(message(state.farm.game.currentPlayer +
|
||||||
|
' is harvesting ' +
|
||||||
|
state.farm.ui.actionValue.acres + ' ' +
|
||||||
|
(state.farm.ui.actionValue.crop === 'cows' ? ' head of cow' : ' acres') +
|
||||||
|
' of ' + state.farm.ui.actionValue.crop));
|
||||||
|
break;
|
||||||
|
case false:
|
||||||
|
store.dispatch(message(''));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state.farm.player.name !== state.farm.game.currentPlayer &&
|
||||||
|
lastAutoSkip !== state.farm.ui.autoSkip) {
|
||||||
|
lastAutoSkip = state.farm.ui.autoSkip;
|
||||||
|
switch (lastAutoSkip) {
|
||||||
|
case 'die':
|
||||||
|
if (lastAction !== 'harvest') {
|
||||||
|
clearInterval(rollTimer);
|
||||||
|
store.dispatch(message(rollMessage));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'harvest|income':
|
||||||
|
store.dispatch(message(state.farm.game.currentPlayer +
|
||||||
|
' rolled a ' +
|
||||||
|
state.farm.ui.actionValue.rolled +
|
||||||
|
' and earned $' +
|
||||||
|
formatMoney(state.farm.ui.actionValue.income)));
|
||||||
|
break;
|
||||||
|
case 'harvest|expense-value':
|
||||||
|
store.dispatch(message(state.farm.game.currentPlayer +
|
||||||
|
' had operating expense of $' +
|
||||||
|
formatMoney(Math.abs(state.farm.ui.actionValue.operatingExpenseValue))));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// mpDims.mouseX = e.clientX
|
// mpDims.mouseX = e.clientX
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { UPDATE_GAME, UPDATE_PLAYER, GAME_STATE, SET_SELECTED_CARD, SET_CARDS,
|
|||||||
SET_OLD_MESSAGES, MESSAGE_PANEL_SPACE, MP_MOUSE,
|
SET_OLD_MESSAGES, MESSAGE_PANEL_SPACE, MP_MOUSE,
|
||||||
SET_MP_DIMS, MOVE_PLAYER, SET_NEXT_ACTION, NEXT_UI_ACTION,
|
SET_MP_DIMS, MOVE_PLAYER, SET_NEXT_ACTION, NEXT_UI_ACTION,
|
||||||
MARK_ACTION_CHANGE_HANDLED, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED,
|
MARK_ACTION_CHANGE_HANDLED, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED,
|
||||||
AUTO_SKIP } from './actionTypes.js'
|
AUTO_SKIP, MESSAGE } from './actionTypes.js'
|
||||||
import { GAME_STATES } from '../../constants.js'
|
import { GAME_STATES } from '../../constants.js'
|
||||||
import { spaceContent, corners } from 'game.js'
|
import { spaceContent, corners } from 'game.js'
|
||||||
|
|
||||||
@@ -92,14 +92,18 @@ const initialState = {
|
|||||||
space: 0,
|
space: 0,
|
||||||
trade: {}
|
trade: {}
|
||||||
},
|
},
|
||||||
game: { auditThreshold: 250000,
|
game: { calledAudit: false,
|
||||||
calledAudit: false,
|
|
||||||
currentPlayer: '',
|
currentPlayer: '',
|
||||||
messages: [],
|
messages: [],
|
||||||
otherPlayers: [],
|
otherPlayers: [],
|
||||||
state: GAME_STATES.preTurn,
|
state: GAME_STATES.preTurn,
|
||||||
turn: 0,
|
turn: 0,
|
||||||
oldMessages: [] },
|
oldMessages: [],
|
||||||
|
settings: { downPayment: 0.2,
|
||||||
|
loanInterest: 0.2,
|
||||||
|
maxDebt: 50000,
|
||||||
|
auditThreshold: 250000 }
|
||||||
|
},
|
||||||
ui: { card: { type: 'no-card', contents: '', total: 0 },
|
ui: { card: { type: 'no-card', contents: '', total: 0 },
|
||||||
cards: [],
|
cards: [],
|
||||||
action: false,
|
action: false,
|
||||||
@@ -107,7 +111,9 @@ const initialState = {
|
|||||||
nextAction: false,
|
nextAction: false,
|
||||||
nextActionValue: null,
|
nextActionValue: null,
|
||||||
actionChangeHandled: true,
|
actionChangeHandled: true,
|
||||||
|
message: '',
|
||||||
alert: false,
|
alert: false,
|
||||||
|
alertContents: false,
|
||||||
autoSkip: false,
|
autoSkip: false,
|
||||||
alertHandled: false },
|
alertHandled: false },
|
||||||
spaces: spaces,
|
spaces: spaces,
|
||||||
@@ -175,11 +181,14 @@ export default function(state = initialState, action) {
|
|||||||
case ALERT:
|
case ALERT:
|
||||||
return { ...state, ui: { ...state.ui,
|
return { ...state, ui: { ...state.ui,
|
||||||
alert: action.value,
|
alert: action.value,
|
||||||
|
alertContents: action.contents,
|
||||||
alertHandled: action.value === false ? true : false }};
|
alertHandled: action.value === false ? true : false }};
|
||||||
case ALERT_HANDLED:
|
case ALERT_HANDLED:
|
||||||
return { ...state, ui: { ...state.ui, alertHandled: true }};
|
return { ...state, ui: { ...state.ui, alertHandled: true }};
|
||||||
case AUTO_SKIP:
|
case AUTO_SKIP:
|
||||||
return { ...state, ui: { ...state.ui, autoSkip: action.component }};
|
return { ...state, ui: { ...state.ui, autoSkip: action.component }};
|
||||||
|
case MESSAGE:
|
||||||
|
return { ...state, ui: { ...state.ui, message: action.message }};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ class JoinGame extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleJoinAsExisting = e => {
|
handleJoinAsExisting = e => {
|
||||||
|
e.preventDefault();
|
||||||
this.props.startOrJoinGame({ type: 'join-as-existing',
|
this.props.startOrJoinGame({ type: 'join-as-existing',
|
||||||
playerName: e.target.text,
|
playerName: e.target.text,
|
||||||
gameId: this.state.game.id,
|
gameId: this.state.game.id,
|
||||||
@@ -78,7 +79,9 @@ class JoinGame extends React.Component {
|
|||||||
{this.props.games
|
{this.props.games
|
||||||
.map((g, i) =>
|
.map((g, i) =>
|
||||||
(<li key={i}>
|
(<li key={i}>
|
||||||
<a href='#' onClick={() => this.handleClickGame(g)}>{g.name}</a>
|
<a onClick={e => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.handleClickGame(g); }}>{g.name}</a>
|
||||||
</li>))}
|
</li>))}
|
||||||
</ul>)
|
</ul>)
|
||||||
: (
|
: (
|
||||||
@@ -88,7 +91,7 @@ class JoinGame extends React.Component {
|
|||||||
<ul>
|
<ul>
|
||||||
{this.state.game.players.map((p, i) =>
|
{this.state.game.players.map((p, i) =>
|
||||||
(<li key={i}>
|
(<li key={i}>
|
||||||
<a href='#' onClick={this.handleJoinAsExisting}>
|
<a onClick={this.handleJoinAsExisting}>
|
||||||
{p}
|
{p}
|
||||||
</a>
|
</a>
|
||||||
</li>))}
|
</li>))}
|
||||||
|
|||||||
@@ -25,16 +25,44 @@ import { startOrJoinGame } from '../start/actions.js'
|
|||||||
import { start } from '../app/actions.js'
|
import { start } from '../app/actions.js'
|
||||||
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faArrowCircleLeft } from '@fortawesome/free-solid-svg-icons'
|
import { faArrowCircleLeft, faCog } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
class InputRow extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Row>
|
||||||
|
<Col width='12'>
|
||||||
|
<label>
|
||||||
|
{this.props.label}
|
||||||
|
<input type='number'
|
||||||
|
min={this.props.min}
|
||||||
|
max={this.props.max}
|
||||||
|
step={this.props.step}
|
||||||
|
name={this.props.name}
|
||||||
|
value={this.props.value}
|
||||||
|
onChange={this.props.onChange} />
|
||||||
|
</label>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class NewGame extends React.Component {
|
class NewGame extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
showSettings: false,
|
||||||
playerName: '',
|
playerName: '',
|
||||||
checkedColor: props.colors[0],
|
checkedColor: props.colors[0],
|
||||||
gameId: typeof props.gameId === 'undefined' ? -1 : props.gameId,
|
gameId: typeof props.gameId === 'undefined' ? -1 : props.gameId,
|
||||||
gameName: props.gameName || ''
|
gameName: props.gameName || '',
|
||||||
|
downPayment: 0.2,
|
||||||
|
loanInterest: 0.2,
|
||||||
|
maxDebt: 50000,
|
||||||
|
auditThreshold: 250000,
|
||||||
|
startingCash: 5000,
|
||||||
|
startingDebt: 5000
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,21 +82,50 @@ class NewGame extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleBack = e => {
|
handleBack = e => {
|
||||||
|
e.preventDefault();
|
||||||
this.props.start();
|
this.props.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleSettings = e => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.setState({ showSettings: !this.state.showSettings });
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let playerNameInput;
|
let playerNameInput,
|
||||||
return (
|
titleBar = !this.props.hideBack ? (
|
||||||
<GroupBox title={!this.props.hideBack ? (
|
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<a href="#" onClick={this.handleBack}>
|
<a onClick={this.handleBack}>
|
||||||
<FontAwesomeIcon icon={faArrowCircleLeft} />
|
<FontAwesomeIcon icon={faArrowCircleLeft} />
|
||||||
</a>
|
</a>
|
||||||
{this.props.title}
|
{this.props.title}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) : this.props.title}>
|
) : this.props.title,
|
||||||
|
colors = this.props.colors.map(c => (
|
||||||
|
<label key={c} className={'player player-selectable player-' + c + (this.state.checkedColor === c ? ' player-selected' : '')}>
|
||||||
|
<input type='checkbox'
|
||||||
|
checked={this.state.checkedColor === c}
|
||||||
|
onChange={this.handleInputChange}
|
||||||
|
name={c} />
|
||||||
|
</label>
|
||||||
|
)
|
||||||
|
),
|
||||||
|
gameName = this.props.showGameName && (
|
||||||
|
<Row>
|
||||||
|
<Col width='12'>
|
||||||
|
<label>Game Name
|
||||||
|
<input type='text' name='gameName' value={this.state.gameName}
|
||||||
|
onChange={this.handleInputChange} />
|
||||||
|
</label>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
),
|
||||||
|
settingsClass = this.state.showSettings ? '' : 'hidden',
|
||||||
|
mainScreenClass = !this.state.showSettings ? '' : 'hidden';
|
||||||
|
return (
|
||||||
|
<GroupBox title={titleBar}>
|
||||||
<form onSubmit={this.handleSubmit}>
|
<form onSubmit={this.handleSubmit}>
|
||||||
|
<div className={mainScreenClass}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col width='12'>
|
<Col width='12'>
|
||||||
<label>Your Name
|
<label>Your Name
|
||||||
@@ -81,31 +138,62 @@ class NewGame extends React.Component {
|
|||||||
<Row>
|
<Row>
|
||||||
<Col width='12'>
|
<Col width='12'>
|
||||||
<label>Your Color</label>
|
<label>Your Color</label>
|
||||||
{this.props.colors
|
{colors}
|
||||||
.map(c =>
|
|
||||||
(<label key={c} className={'player player-selectable player-' + c + (this.state.checkedColor === c ? ' player-selected' : '')}>
|
|
||||||
<input type='checkbox'
|
|
||||||
checked={this.state.checkedColor === c}
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
name={c} />
|
|
||||||
</label>))
|
|
||||||
}
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{this.props.showGameName && (
|
{gameName}
|
||||||
<Row>
|
</div>
|
||||||
<Col width='12'>
|
<div className={settingsClass}>
|
||||||
<label>Game Name
|
<InputRow label='Down Payment'
|
||||||
<input type='text' name='gameName' value={this.state.gameName}
|
name='downPayment'
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.1}
|
||||||
|
value={this.state.downPayment}
|
||||||
onChange={this.handleInputChange} />
|
onChange={this.handleInputChange} />
|
||||||
</label>
|
<InputRow label='Loan Interest'
|
||||||
</Col>
|
name='loanInterest'
|
||||||
</Row>
|
min={0}
|
||||||
)}
|
max={1}
|
||||||
|
step={0.1}
|
||||||
|
value={this.state.loanInterest}
|
||||||
|
onChange={this.handleInputChange} />
|
||||||
|
<InputRow label='Maximum Debt'
|
||||||
|
name='maxDebt'
|
||||||
|
min={0}
|
||||||
|
step={5000}
|
||||||
|
value={this.state.maxDebt}
|
||||||
|
onChange={this.handleInputChange} />
|
||||||
|
<InputRow label='Audit Threshold'
|
||||||
|
name='auditThreshold'
|
||||||
|
min={0}
|
||||||
|
step={25000}
|
||||||
|
value={this.state.auditThreshold}
|
||||||
|
onChange={this.handleInputChange} />
|
||||||
|
<InputRow label='Starting Cash'
|
||||||
|
name='startingCash'
|
||||||
|
min={0}
|
||||||
|
step={1000}
|
||||||
|
value={this.state.startingCash}
|
||||||
|
onChange={this.handleInputChange} />
|
||||||
|
<InputRow label='Starting Debt'
|
||||||
|
name='startingDebt'
|
||||||
|
min={0}
|
||||||
|
step={1000}
|
||||||
|
value={this.state.startingDebt}
|
||||||
|
onChange={this.handleInputChange} />
|
||||||
|
</div>
|
||||||
<Row>
|
<Row>
|
||||||
<Col width='12'>
|
<Col width='12'>
|
||||||
|
<div className='new-game-submit-container'>
|
||||||
<Button type='submit'>{this.props.button} Game</Button>
|
<Button type='submit'>{this.props.button} Game</Button>
|
||||||
|
{this.props.showGameName ? (
|
||||||
|
<a onClick={this.toggleSettings}>
|
||||||
|
<FontAwesomeIcon icon={faCog} size='lg' />
|
||||||
|
</a>
|
||||||
|
) : (<Fragment />)}
|
||||||
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -31,4 +31,5 @@ export const rootId = 'initial-element';
|
|||||||
export const messagePanelId = 'message-panel';
|
export const messagePanelId = 'message-panel';
|
||||||
export const ALERTS = { beginTurn: 'begin-turn',
|
export const ALERTS = { beginTurn: 'begin-turn',
|
||||||
otherPlayersTurn: 'other-players-turn',
|
otherPlayersTurn: 'other-players-turn',
|
||||||
|
endOfGame: 'end-of-game',
|
||||||
raiseMoney: 'raise-money' }
|
raiseMoney: 'raise-money' }
|
||||||
|
|||||||
@@ -91,11 +91,21 @@
|
|||||||
(colors initform: '() accessor: game-colors)
|
(colors initform: '() accessor: game-colors)
|
||||||
(last-updated initform: 0 accessor: game-last-updated)
|
(last-updated initform: 0 accessor: game-last-updated)
|
||||||
(called-audit initform: #f accessor: game-called-audit)
|
(called-audit initform: #f accessor: game-called-audit)
|
||||||
(audit-threshold initform: 250000 accessor: game-audit-threshold)
|
|
||||||
(state initform: 'playing accessor: game-state)
|
(state initform: 'playing accessor: game-state)
|
||||||
(name initform: "game" accessor: game-name)
|
(name initform: "game" accessor: game-name)
|
||||||
(turn initform: 1 accessor: game-turn)
|
(turn initform: 1 accessor: game-turn)
|
||||||
(actions initform: '() accessor: game-actions)))
|
(actions initform: '() accessor: game-actions)
|
||||||
|
(settings initform:
|
||||||
|
'((down-payment . 0.2)
|
||||||
|
(loan-interest . 0.2)
|
||||||
|
(max-debt . 50000)
|
||||||
|
(audit-threshold . 250000)
|
||||||
|
(starting-cash . 5000)
|
||||||
|
(starting-debt . 5000))
|
||||||
|
accessor: game-settings)))
|
||||||
|
|
||||||
|
(define (game-setting setting game)
|
||||||
|
(alist-ref setting (game-settings game)))
|
||||||
|
|
||||||
(define-class <app> ()
|
(define-class <app> ()
|
||||||
((games initform: '() accessor: app-games)
|
((games initform: '() accessor: app-games)
|
||||||
@@ -205,7 +215,10 @@
|
|||||||
color))
|
color))
|
||||||
|
|
||||||
(define (add-player-to-game game color name)
|
(define (add-player-to-game game color name)
|
||||||
(let ((player (make <player> 'cash 5000 'color color
|
(let ((player (make <player>
|
||||||
|
'cash (game-setting 'starting-cash game)
|
||||||
|
'debt (game-setting 'starting-debt game)
|
||||||
|
'color color
|
||||||
'name name
|
'name name
|
||||||
'state (if (= (length (game-players game)) 0)
|
'state (if (= (length (game-players game)) 0)
|
||||||
'pre-turn 'turn-ended))))
|
'pre-turn 'turn-ended))))
|
||||||
@@ -322,9 +335,12 @@
|
|||||||
(calledAudit . ,(if (game-called-audit g)
|
(calledAudit . ,(if (game-called-audit g)
|
||||||
(player-name (game-called-audit g))
|
(player-name (game-called-audit g))
|
||||||
#f))
|
#f))
|
||||||
(auditThreshold . ,(game-audit-threshold g))
|
|
||||||
(state . ,(symbol->string (game-state g)))
|
(state . ,(symbol->string (game-state g)))
|
||||||
(turn . ,(game-turn g))))))
|
(turn . ,(game-turn g))
|
||||||
|
(settings . ((downPayment . ,(game-setting 'down-payment g))
|
||||||
|
(loanInterest . ,(game-setting 'loan-interest g))
|
||||||
|
(maxDebt . ,(game-setting 'max-debt g))
|
||||||
|
(auditThreshold . ,(game-setting 'audit-threshold g))))))))
|
||||||
|
|
||||||
(define (push-message player msg #!key (game (session-ref (sid) 'game)))
|
(define (push-message player msg #!key (game (session-ref (sid) 'game)))
|
||||||
(if player
|
(if player
|
||||||
@@ -361,11 +377,11 @@
|
|||||||
((> cash-value (player-cash player))
|
((> cash-value (player-cash player))
|
||||||
(push-message player (conc "Could not buy " unnormalized-crop ". Not enough cash."))
|
(push-message player (conc "Could not buy " unnormalized-crop ". Not enough cash."))
|
||||||
#f)
|
#f)
|
||||||
((< cash-value (* total-cost 0.2))
|
((< cash-value (* total-cost (game-setting 'down-payment game)))
|
||||||
(push-message player
|
(push-message player
|
||||||
(conc "Could not buy " unnormalized-crop ". Not enough down payment."))
|
(conc "Could not buy " unnormalized-crop ". Not enough down payment."))
|
||||||
#f)
|
#f)
|
||||||
((> (- total-cost cash-value) (- 50000 (player-debt player)))
|
((> (- total-cost cash-value) (- (game-setting 'max-debt game) (player-debt player)))
|
||||||
(push-message player
|
(push-message player
|
||||||
(conc "Could not buy " unnormalized-crop ". Not enough credit."))
|
(conc "Could not buy " unnormalized-crop ". Not enough credit."))
|
||||||
#f)
|
#f)
|
||||||
@@ -611,16 +627,19 @@
|
|||||||
(* (player-debt player) -1)))
|
(* (player-debt player) -1)))
|
||||||
|
|
||||||
(define (do-end-of-game game)
|
(define (do-end-of-game game)
|
||||||
(push-message #f "Game over!")
|
(message-players!
|
||||||
(for-each (lambda (p i)
|
game
|
||||||
(push-message #f
|
#f
|
||||||
(conc i ". " (player-name p) " with $"
|
`((results
|
||||||
(player-net-worth p))))
|
. ,(list->vector
|
||||||
|
(map (lambda (p i)
|
||||||
|
(conc i ". " (player-name p) " with $" (player-net-worth p)))
|
||||||
(sort (game-players game)
|
(sort (game-players game)
|
||||||
(lambda (p1 p2)
|
(lambda (p1 p2)
|
||||||
(> (player-net-worth p1)
|
(> (player-net-worth p1)
|
||||||
(player-net-worth p2))))
|
(player-net-worth p2))))
|
||||||
(iota (length (game-players game)) 1)))
|
(iota (length (game-players game)) 1)))))
|
||||||
|
type: "end-of-game"))
|
||||||
|
|
||||||
(define (create-ws-response player event misc)
|
(define (create-ws-response player event misc)
|
||||||
(append `((event . ,event) ,@misc)
|
(append `((event . ,event) ,@misc)
|
||||||
@@ -646,6 +665,26 @@
|
|||||||
|
|
||||||
(define *next-roll* #f)
|
(define *next-roll* #f)
|
||||||
|
|
||||||
|
(define (->number x default)
|
||||||
|
(if (number? x)
|
||||||
|
x
|
||||||
|
(if (string? x)
|
||||||
|
(or (string->number x)
|
||||||
|
default)
|
||||||
|
default)))
|
||||||
|
|
||||||
|
(define (->pct x default)
|
||||||
|
(let ((n (->number x default)))
|
||||||
|
(if (or (> n 1) (< n 0))
|
||||||
|
default
|
||||||
|
n)))
|
||||||
|
|
||||||
|
(define (->i x default)
|
||||||
|
(let ((n (inexact->exact (floor (->number x default)))))
|
||||||
|
(if (< n 0)
|
||||||
|
default
|
||||||
|
(- n (modulo n 1000)))))
|
||||||
|
|
||||||
(define (process-message player game type msg)
|
(define (process-message player game type msg)
|
||||||
(when game
|
(when game
|
||||||
(set! (game-messages game) '())
|
(set! (game-messages game) '())
|
||||||
@@ -842,19 +881,25 @@
|
|||||||
(let ((amount (* (alist-ref 'amount msg) 1000)))
|
(let ((amount (* (alist-ref 'amount msg) 1000)))
|
||||||
(if (> amount 0)
|
(if (> amount 0)
|
||||||
;; taking out loan
|
;; taking out loan
|
||||||
(if (> (+ (player-debt player) amount) 50000)
|
(if (> (+ (player-debt player)
|
||||||
|
(farming-round (+ amount (* amount (game-setting 'loan-interest game)))))
|
||||||
|
(game-setting 'max-debt game))
|
||||||
(push-message player "Exceeds max loan.")
|
(push-message player "Exceeds max loan.")
|
||||||
(begin (set! (player-cash player) (+ (player-cash player) amount))
|
(begin (set! (player-cash player) (+ (player-cash player) amount))
|
||||||
(set! (player-debt player) (+ (player-debt player) amount))
|
(set! (player-debt player) (+ (player-debt player)
|
||||||
|
(farming-round
|
||||||
|
(+ amount (* amount (game-setting 'loan-interest game))))))
|
||||||
(push-message player (conc "Loan of $" amount " taken out."))))
|
(push-message player (conc "Loan of $" amount " taken out."))))
|
||||||
;; repaying loan
|
;; repaying loan
|
||||||
(cond ((> amount (player-cash player))
|
(cond ((> (abs amount) (player-cash player))
|
||||||
(push-message player "Not enough cash to repay loan."))
|
(push-message player "Not enough cash to repay loan."))
|
||||||
((> amount (player-debt player))
|
|
||||||
(push-message player "Repayment exceeds total loan amount."))
|
|
||||||
(else
|
(else
|
||||||
(set! (player-cash player) (+ (player-cash player) amount))
|
(set! (player-cash player) (+ (player-cash player) amount))
|
||||||
(set! (player-debt player) (+ (player-debt player) amount))
|
(set! (player-debt player) (+ (player-debt player) amount))
|
||||||
|
(when (< (player-debt player) 0)
|
||||||
|
(set! (player-cash player) (+ (player-cash player)
|
||||||
|
(abs (player-debt player))))
|
||||||
|
(set! (player-debt player) 0))
|
||||||
(push-message player (conc "Loan of $" (abs amount) " repayed."))))
|
(push-message player (conc "Loan of $" (abs amount) " repayed."))))
|
||||||
))
|
))
|
||||||
(create-ws-response player "loan" '()))
|
(create-ws-response player "loan" '()))
|
||||||
@@ -908,7 +953,17 @@
|
|||||||
'id (next-game-id *app*)
|
'id (next-game-id *app*)
|
||||||
'otbs (setup-otbs)
|
'otbs (setup-otbs)
|
||||||
'operating-expenses (setup-operating-expenses)
|
'operating-expenses (setup-operating-expenses)
|
||||||
'farmers-fates (setup-farmers-fates)))
|
'farmers-fates (setup-farmers-fates)
|
||||||
|
'settings
|
||||||
|
`((down-payment . ,(->pct (alist-ref 'downPayment msg) 0.2))
|
||||||
|
(loan-interest . ,(->pct (alist-ref 'loanInterest msg) 0.2))
|
||||||
|
(max-debt . ,(->i (alist-ref 'maxDebt msg) 50000))
|
||||||
|
(audit-threshold . ,(->i (alist-ref 'auditThreshold msg)
|
||||||
|
250000))
|
||||||
|
(starting-cash . ,(->i (alist-ref 'startingCash msg)
|
||||||
|
5000))
|
||||||
|
(starting-debt . ,(->i (alist-ref 'startingDebt msg)
|
||||||
|
5000)))))
|
||||||
(player (add-player-to-game game
|
(player (add-player-to-game game
|
||||||
color
|
color
|
||||||
(alist-ref 'playerName msg))))
|
(alist-ref 'playerName msg))))
|
||||||
@@ -930,6 +985,7 @@
|
|||||||
(session-set! (sid) 'player player)
|
(session-set! (sid) 'player player)
|
||||||
(session-set! (sid) 'game game)
|
(session-set! (sid) 'game game)
|
||||||
(set-startup-otbs game player 2)
|
(set-startup-otbs game player 2)
|
||||||
|
(message-players! game player '() type: "update")
|
||||||
(create-start-response "new-game-started")))
|
(create-start-response "new-game-started")))
|
||||||
((string=? type "join-as-existing")
|
((string=? type "join-as-existing")
|
||||||
(let* ((name (alist-ref 'gameName msg))
|
(let* ((name (alist-ref 'gameName msg))
|
||||||
@@ -993,8 +1049,8 @@
|
|||||||
(set! game (session-ref (sid) 'game)))
|
(set! game (session-ref (sid) 'game)))
|
||||||
(when (not player)
|
(when (not player)
|
||||||
(set! player (session-ref (sid) 'player)))
|
(set! player (session-ref (sid) 'player)))
|
||||||
(when (< (player-last-updated player)
|
;; when (< (player-last-updated player)
|
||||||
(game-last-updated game))
|
;; (game-last-updated game))
|
||||||
(handle-exceptions
|
(handle-exceptions
|
||||||
exn
|
exn
|
||||||
(send-message
|
(send-message
|
||||||
@@ -1015,7 +1071,7 @@
|
|||||||
(create-ws-response player
|
(create-ws-response player
|
||||||
(alist-ref 'type msg)
|
(alist-ref 'type msg)
|
||||||
(alist-ref 'value msg))
|
(alist-ref 'value msg))
|
||||||
)))))
|
))))
|
||||||
(loop (mailbox-receive! (player-mailbox player))))))))
|
(loop (mailbox-receive! (player-mailbox player))))))))
|
||||||
|
|
||||||
(define (otb-spec->otb-cards spec id)
|
(define (otb-spec->otb-cards spec id)
|
||||||
|
|||||||
@@ -762,3 +762,21 @@ $intro-time: 6s;
|
|||||||
.fa-arrow-circle-left {
|
.fa-arrow-circle-left {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-right: 0.5rem; }
|
margin-right: 0.5rem; }
|
||||||
|
|
||||||
|
.info-bar {
|
||||||
|
@include breakpoint(landscape) {
|
||||||
|
position: absolute; }
|
||||||
|
text-align: center;
|
||||||
|
padding: $tab-margin;
|
||||||
|
height: 2rem; }
|
||||||
|
|
||||||
|
.new-game-submit-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.button {
|
||||||
|
margin: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none; }
|
||||||
|
|||||||
Reference in New Issue
Block a user