diff --git a/src/components/farm/Board.jsx b/src/components/farm/Board.jsx index f878afa..0229347 100644 --- a/src/components/farm/Board.jsx +++ b/src/components/farm/Board.jsx @@ -310,7 +310,7 @@ class PlayerTurnContainer extends React.Component { let view; const player = this.props.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) ? ( ) : ''; if (player.state === GAME_STATES.preTurn) { view = ( @@ -505,7 +505,22 @@ class Messages extends React.Component { class Loans extends React.Component { constructor(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 => { @@ -520,10 +535,10 @@ class Loans extends React.Component { } else { takeOut = value * 1000; } - this.setState({ repay: Math.max(0, Math.floor( - Math.min(repay, this.props.player.debt, this.props.player.cash) / 1000)), + this.setState({ repay: Math.max(0, Math.ceil( + Math.min(repay, this.props.player.debt + 900, this.props.player.cash) / 1000)), takeOut: Math.max(0, Math.floor( - Math.min(takeOut, 50000 - this.props.player.debt) / 1000)) }); + Math.min(takeOut, this.state.maxLoan) / 1000)) }); } handleSubmit = e => { @@ -532,11 +547,22 @@ class Loans extends React.Component { 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 () { return ( {' '} - /$50,000 + + /${formatMoney(this.props.game.settings.maxDebt)} +
+ {this.props.game.settings.loanInterest > 0 ? '(' + (this.props.game.settings.loanInterest * 100) + '% interest added to each loan)' : ''}

@@ -558,7 +584,7 @@ class Loans extends React.Component { $: - {'\u00A0'}K / ${(50000 - this.props.player.debt) / 1000}K + {'\u00A0'}K / ${this.state.maxLoan / 1000}K @@ -814,7 +840,6 @@ class Harvest extends React.Component { } } - // this.props.player.name === this.props.game.currentPlayer TODO render() { let view; const isCurrentPlayer = this.props.player.name === this.props.game.currentPlayer; @@ -841,7 +866,7 @@ class Harvest extends React.Component { case 'roll': view = ( this.nextView('income')} - skip={true} + skip={this.props.player.name === this.props.game.currentPlayer} autoSkip={this.props.autoSkip === 'die'} showScreenDelay={2000} />); break; @@ -1088,7 +1113,7 @@ class Action extends React.Component {
{this.props.player.name === this.props.game.currentPlayer ? 'You' : this.props.game.currentPlayer} {this.props.ui.actionValue < 0 ? 'lost ' : 'gained '} - ${Math.abs(this.props.ui.actionValue)}! + ${formatMoney(Math.abs(this.props.ui.actionValue))}!
); @@ -1104,7 +1129,6 @@ class Action extends React.Component { break; case 'harvest': view = ( { + hide = e => { + e.preventDefault(); this.setState({ visible: false }); this.props.hideHandler(); } - buttonClick = () => { - this.hide(); + buttonClick = e => { + this.hide(e); this.props.handler(); } @@ -1271,13 +1296,27 @@ class AlertOverlay extends React.Component {
- close + close ); } } +class InfoBar extends React.Component { + render() { + if (this.props.players.length > 0) { + return ( +
+ {(this.props.screen === SCREENS.action) ? '' : this.props.message} +
+ ); + } else { + return (); + } + } +} + const SCREENS = { summary: 'summary', misc: 'misc', farms: 'farms', cards: 'cards', trade: 'trade', loans: 'loans', action: 'action' }; @@ -1329,12 +1368,29 @@ class BoardApp extends React.Component { } iconOnClick = icon => { - return () => this.showScreen(this.iconToScreen[icon]); + return e => { + e.preventDefault(); + this.showScreen(this.iconToScreen[icon]); + } } render() { let alertOverlay; - if (!this.props.ui.alertHandled && this.state.screen !== SCREENS.action) { + if (!this.props.ui.alertHandled && this.props.ui.alert === ALERTS.endOfGame) { + alertOverlay = ( + this.props.alert(false)} + handler={() => { return false; }}> + +

Game Over!

+ {this.props.ui.alertContents.map((e, i) => ( +

{e}

+ ))} +
+
+ ); + } else if (!this.props.ui.alertHandled && this.state.screen !== SCREENS.action) { switch (this.props.ui.alert) { case ALERTS.beginTurn: alertOverlay = ( @@ -1384,20 +1440,23 @@ class BoardApp extends React.Component {
{alertOverlay} +
    {[faUser, faTractor, faWindowRestore, faDollarSign, faUsers, faAsterisk] .map((icon, i) => (
  • - +
  • ))}
    {[faUser, faTractor, faWindowRestore, faDollarSign, faUsers, faAsterisk] .map((icon, i) => (
  • - +
  • ))}
@@ -1426,14 +1485,14 @@ class BoardApp extends React.Component {
+ cash={(this.props.ui.card.total * this.props.game.settings.downPayment) / 1000} />
- +
$: + value={Math.ceil(this.state.cash)} /> {'\u00A0'},000
diff --git a/src/components/farm/actionTypes.js b/src/components/farm/actionTypes.js index c27ada2..18f334c 100644 --- a/src/components/farm/actionTypes.js +++ b/src/components/farm/actionTypes.js @@ -35,3 +35,4 @@ export const MARK_ACTION_CHANGE_HANDLED = 'mark-action-change-handled' export const ALERT = 'alert' export const ALERT_HANDLED = 'alert-handled' export const AUTO_SKIP = 'auto-skip' +export const MESSAGE = 'message' diff --git a/src/components/farm/actions.js b/src/components/farm/actions.js index 889b293..21626de 100644 --- a/src/components/farm/actions.js +++ b/src/components/farm/actions.js @@ -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, MP_MOUSE, SET_MP_DIMS, MARK_ACTION_CHANGE_HANDLED, SET_NEXT_ACTION, 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, spacePushPlayer, spaceClearPlayers, setOldMessages, setMessagePanelSpace, mpMouse, setMPDims, movePlayer, setNextAction, nextUIAction, markActionChangeHandled, nextUIActionSilent, alert, alertHandled, - autoSkip } + autoSkip, message } function updateGame(update) { return { type: UPDATE_GAME, @@ -107,8 +107,8 @@ function markActionChangeHandled() { return { type: MARK_ACTION_CHANGE_HANDLED }; } -function alert(value) { - return { type: ALERT, value }; +function alert(value, contents) { + return { type: ALERT, value, contents }; } function alertHandled() { @@ -118,3 +118,7 @@ function alertHandled() { function autoSkip(component) { return { type: AUTO_SKIP, component }; } + +function message(message) { + return { type: MESSAGE, message }; +} diff --git a/src/components/farm/interface.js b/src/components/farm/interface.js index 7080369..ecae5bd 100644 --- a/src/components/farm/interface.js +++ b/src/components/farm/interface.js @@ -24,7 +24,8 @@ import * as websocket from '../../websocket.js' import { updateGame, updatePlayer, gameState, setSelectedCard, setCards, movePlayer, setOldMessages, markActionChangeHandled, 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, submitTradeDeny, submitTradeCancel, audit, handleMessage, @@ -47,10 +48,11 @@ function handleMessage(evt) { data.game.otherPlayers.length > 0 && store.getState().farm.player.state !== GAME_STATES.preTurn) { 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 if (data.event === 'update' && data.game.currentPlayer !== store.getState().farm.game.currentPlayer) { @@ -93,10 +95,15 @@ function handleMessage(evt) { data.player.cash < 0 && !store.getState().farm.ui.nextAction) { store.dispatch(alert(ALERTS.raiseMoney)); + } else if (data.player.state === GAME_STATES.midTurn) { + store.dispatch(alertHandled()); } if (data.event === 'auto-skip') { 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 }); } +// 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) { store = st; sendCommand = sc; + let lastAction = false, + lastAutoSkip = false, + rollMessage = '', + rollTimer = false; const unsubscribe = store.subscribe( () => { const state = store.getState(); @@ -170,6 +186,73 @@ function initialize(st, sc) { store.dispatch(markActionChangeHandled()); 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 diff --git a/src/components/farm/reducers.js b/src/components/farm/reducers.js index ec58f1a..95f53cc 100644 --- a/src/components/farm/reducers.js +++ b/src/components/farm/reducers.js @@ -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_MP_DIMS, MOVE_PLAYER, SET_NEXT_ACTION, NEXT_UI_ACTION, 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 { spaceContent, corners } from 'game.js' @@ -92,14 +92,18 @@ const initialState = { space: 0, trade: {} }, - game: { auditThreshold: 250000, - calledAudit: false, + game: { calledAudit: false, currentPlayer: '', messages: [], otherPlayers: [], state: GAME_STATES.preTurn, turn: 0, - oldMessages: [] }, + oldMessages: [], + settings: { downPayment: 0.2, + loanInterest: 0.2, + maxDebt: 50000, + auditThreshold: 250000 } + }, ui: { card: { type: 'no-card', contents: '', total: 0 }, cards: [], action: false, @@ -107,7 +111,9 @@ const initialState = { nextAction: false, nextActionValue: null, actionChangeHandled: true, + message: '', alert: false, + alertContents: false, autoSkip: false, alertHandled: false }, spaces: spaces, @@ -175,11 +181,14 @@ export default function(state = initialState, action) { case ALERT: return { ...state, ui: { ...state.ui, alert: action.value, + alertContents: action.contents, alertHandled: action.value === false ? true : false }}; case ALERT_HANDLED: return { ...state, ui: { ...state.ui, alertHandled: true }}; case AUTO_SKIP: return { ...state, ui: { ...state.ui, autoSkip: action.component }}; + case MESSAGE: + return { ...state, ui: { ...state.ui, message: action.message }}; default: return state; } diff --git a/src/components/join-game/JoinGame.jsx b/src/components/join-game/JoinGame.jsx index aeb010e..463b529 100644 --- a/src/components/join-game/JoinGame.jsx +++ b/src/components/join-game/JoinGame.jsx @@ -55,6 +55,7 @@ class JoinGame extends React.Component { } handleJoinAsExisting = e => { + e.preventDefault(); this.props.startOrJoinGame({ type: 'join-as-existing', playerName: e.target.text, gameId: this.state.game.id, @@ -78,7 +79,9 @@ class JoinGame extends React.Component { {this.props.games .map((g, i) => (
  • - this.handleClickGame(g)}>{g.name} + { + e.preventDefault(); + this.handleClickGame(g); }}>{g.name}
  • ))} ) : ( @@ -88,7 +91,7 @@ class JoinGame extends React.Component {
      {this.state.game.players.map((p, i) => (
    • - + {p}
    • ))} diff --git a/src/components/new-game/NewGame.jsx b/src/components/new-game/NewGame.jsx index 259a1bc..86ad6de 100644 --- a/src/components/new-game/NewGame.jsx +++ b/src/components/new-game/NewGame.jsx @@ -25,16 +25,44 @@ import { startOrJoinGame } from '../start/actions.js' import { start } from '../app/actions.js' 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 ( + + + + + + ); + } +} class NewGame extends React.Component { constructor(props) { super(props); this.state = { + showSettings: false, playerName: '', checkedColor: props.colors[0], 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,46 +82,35 @@ class NewGame extends React.Component { } handleBack = e => { + e.preventDefault(); this.props.start(); } + toggleSettings = e => { + e.preventDefault(); + this.setState({ showSettings: !this.state.showSettings }); + } + render() { - let playerNameInput; - return ( - - + {this.props.title} - ) : this.props.title}> - - - - - - - - - - {this.props.colors - .map(c => - ()) - } -

      - -
      - {this.props.showGameName && ( + ) : this.props.title, + colors = this.props.colors.map(c => ( + + ) + ), + gameName = this.props.showGameName && ( - )} + ), + settingsClass = this.state.showSettings ? '' : 'hidden', + mainScreenClass = !this.state.showSettings ? '' : 'hidden'; + return ( + + +
      + + + + + + + + + {colors} +

      + +
      + {gameName} +
      +
      + + + + + + +
      - +
      + + {this.props.showGameName ? ( + + + + ) : ()} +
      diff --git a/src/constants.js b/src/constants.js index 4cea1a6..9e82850 100644 --- a/src/constants.js +++ b/src/constants.js @@ -31,4 +31,5 @@ export const rootId = 'initial-element'; export const messagePanelId = 'message-panel'; export const ALERTS = { beginTurn: 'begin-turn', otherPlayersTurn: 'other-players-turn', + endOfGame: 'end-of-game', raiseMoney: 'raise-money' } diff --git a/src/server/farm.scm b/src/server/farm.scm index 6643870..16ed0b8 100644 --- a/src/server/farm.scm +++ b/src/server/farm.scm @@ -91,11 +91,21 @@ (colors initform: '() accessor: game-colors) (last-updated initform: 0 accessor: game-last-updated) (called-audit initform: #f accessor: game-called-audit) - (audit-threshold initform: 250000 accessor: game-audit-threshold) (state initform: 'playing accessor: game-state) (name initform: "game" accessor: game-name) (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 () ((games initform: '() accessor: app-games) @@ -205,10 +215,13 @@ color)) (define (add-player-to-game game color name) - (let ((player (make 'cash 5000 'color color - 'name name - 'state (if (= (length (game-players game)) 0) - 'pre-turn 'turn-ended)))) + (let ((player (make + 'cash (game-setting 'starting-cash game) + 'debt (game-setting 'starting-debt game) + 'color color + 'name name + 'state (if (= (length (game-players game)) 0) + 'pre-turn 'turn-ended)))) (set! (game-players game) (append (game-players game) (list player))) player)) @@ -322,9 +335,12 @@ (calledAudit . ,(if (game-called-audit g) (player-name (game-called-audit g)) #f)) - (auditThreshold . ,(game-audit-threshold 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))) (if player @@ -361,11 +377,11 @@ ((> cash-value (player-cash player)) (push-message player (conc "Could not buy " unnormalized-crop ". Not enough cash.")) #f) - ((< cash-value (* total-cost 0.2)) + ((< cash-value (* total-cost (game-setting 'down-payment game))) (push-message player (conc "Could not buy " unnormalized-crop ". Not enough down payment.")) #f) - ((> (- total-cost cash-value) (- 50000 (player-debt player))) + ((> (- total-cost cash-value) (- (game-setting 'max-debt game) (player-debt player))) (push-message player (conc "Could not buy " unnormalized-crop ". Not enough credit.")) #f) @@ -611,16 +627,19 @@ (* (player-debt player) -1))) (define (do-end-of-game game) - (push-message #f "Game over!") - (for-each (lambda (p i) - (push-message #f - (conc i ". " (player-name p) " with $" - (player-net-worth p)))) - (sort (game-players game) - (lambda (p1 p2) - (> (player-net-worth p1) - (player-net-worth p2)))) - (iota (length (game-players game)) 1))) + (message-players! + game + #f + `((results + . ,(list->vector + (map (lambda (p i) + (conc i ". " (player-name p) " with $" (player-net-worth p))) + (sort (game-players game) + (lambda (p1 p2) + (> (player-net-worth p1) + (player-net-worth p2)))) + (iota (length (game-players game)) 1))))) + type: "end-of-game")) (define (create-ws-response player event misc) (append `((event . ,event) ,@misc) @@ -646,6 +665,26 @@ (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) (when game (set! (game-messages game) '()) @@ -842,19 +881,25 @@ (let ((amount (* (alist-ref 'amount msg) 1000))) (if (> amount 0) ;; 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.") (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.")))) ;; repaying loan - (cond ((> amount (player-cash player)) + (cond ((> (abs amount) (player-cash player)) (push-message player "Not enough cash to repay loan.")) - ((> amount (player-debt player)) - (push-message player "Repayment exceeds total loan amount.")) (else (set! (player-cash player) (+ (player-cash 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.")))) )) (create-ws-response player "loan" '())) @@ -908,7 +953,17 @@ 'id (next-game-id *app*) 'otbs (setup-otbs) '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 color (alist-ref 'playerName msg)))) @@ -930,6 +985,7 @@ (session-set! (sid) 'player player) (session-set! (sid) 'game game) (set-startup-otbs game player 2) + (message-players! game player '() type: "update") (create-start-response "new-game-started"))) ((string=? type "join-as-existing") (let* ((name (alist-ref 'gameName msg)) @@ -993,29 +1049,29 @@ (set! game (session-ref (sid) 'game))) (when (not player) (set! player (session-ref (sid) 'player))) - (when (< (player-last-updated player) - (game-last-updated game)) - (handle-exceptions - exn - (send-message - (json->string + ;; when (< (player-last-updated player) + ;; (game-last-updated game)) + (handle-exceptions + exn + (send-message + (json->string + `((exn . ,(with-output-to-string + (lambda () + (print-call-chain) + (print-error-message exn))))))) + (send-message + (json->string + (handle-exceptions + exn `((exn . ,(with-output-to-string (lambda () (print-call-chain) - (print-error-message exn))))))) - (send-message - (json->string - (handle-exceptions - exn - `((exn . ,(with-output-to-string - (lambda () - (print-call-chain) - (print-error-message exn)))) - (event . "error")) - (create-ws-response player - (alist-ref 'type msg) - (alist-ref 'value msg)) - ))))) + (print-error-message exn)))) + (event . "error")) + (create-ws-response player + (alist-ref 'type msg) + (alist-ref 'value msg)) + )))) (loop (mailbox-receive! (player-mailbox player)))))))) (define (otb-spec->otb-cards spec id) diff --git a/src/style.scss b/src/style.scss index f822e11..632b6ec 100644 --- a/src/style.scss +++ b/src/style.scss @@ -762,3 +762,21 @@ $intro-time: 6s; .fa-arrow-circle-left { cursor: pointer; 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; }