diff --git a/assets/img/cake.svg b/assets/img/cake.svg
new file mode 100644
index 0000000..c80f899
--- /dev/null
+++ b/assets/img/cake.svg
@@ -0,0 +1,45 @@
+
+
\ No newline at end of file
diff --git a/src/components/farm/Board.jsx b/src/components/farm/Board.jsx
index f9a3143..74386ee 100644
--- a/src/components/farm/Board.jsx
+++ b/src/components/farm/Board.jsx
@@ -47,7 +47,8 @@ import { setSelectedCard, setMessagePanelSpace, setMPDims, movePlayer,
setMovingSkip } from './actions.js'
import { buy, roll, endTurn, loan, trade, submitTradeAccept,
submitTradeDeny, submitTradeCancel, audit,
- buyUncleBert, skip, endAiTurn, startGame } from './interface.js'
+ buyUncleBert, skip, endAiTurn, startGame, readyToStart,
+ leaveGame } from './interface.js'
function netWorth(player) {
return ((player.assets.hay + player.assets.grain) * 2000) +
@@ -1810,6 +1811,11 @@ class AlertOverlay extends React.Component {
this.hide(e);
this.props.handler();
}
+
+ cancelButtonClick = e => {
+ e.preventDefault();
+ this.props.cancelHandler();
+ }
//
render() {
@@ -1823,7 +1829,17 @@ class AlertOverlay extends React.Component {
{this.props.children}
-
+
+
+ {this.props.cancelButtonText ? (
+ <>
+ {' '}
+
+ >
+ ): (<>>)}
+
{!this.props.preventHiding ? (
close) : (<>>)}
@@ -1916,6 +1932,44 @@ class Info extends React.Component {
}
}
+class StartGame extends React.Component {
+ render() {
+ const { auditThreshold, downPayment, loanInterest, maxDebt, startingOtbs, startingCash, startingDebt } = this.props.game.settings;
+ const { name } = this.props.game;
+ return (
+ <>
+ Lobby
+
+ Game: {name}
+
+ Players
+
+ - {this.props.player.name}
+ {this.props.game.otherPlayers.map((p, i) => (
+ -
+ {p.player.name}
+
+ ))}
+
+ Game Settings
+
+ - Audit Threshold: ${formatMoney(auditThreshold)}
+ - Max Debt: ${formatMoney(maxDebt)}
+ - Loan Interest: {loanInterest * 100}%
+ - Required Down Payment: ${formatMoney(downPayment)}
+ - Starting {itemCardShort}: {startingOtbs}
+ - Starting Cash: ${formatMoney(startingCash)}
+ - Starting Debt: ${formatMoney(startingDebt)}
+
+
+ >
+ );
+ }
+}
+
const SCREENS = { summary: 'summary', misc: 'misc', farms: 'farms',
cards: 'cards', trade: 'trade', loans: 'loans',
action: 'action', info: 'info', error: 'error' };
@@ -1933,7 +1987,8 @@ class BoardApp extends React.Component {
screen: SCREENS.summary,
card: props.ui.card,
timerId: false,
- currentPlayer: this.props.player
+ currentPlayer: this.props.player,
+ readyToStart: false
};
this.myRef = React.createRef();
this.actionRef = React.createRef();
@@ -2038,6 +2093,11 @@ class BoardApp extends React.Component {
}
}
+ startGameToggleReady = () => {
+ this.setState(state => { return { readyToStart: !state.readyToStart }; });
+ readyToStart();
+ }
+
render() {
let alertOverlay;
const alert = this.props.ui.unhandledAlert;
@@ -2079,21 +2139,16 @@ class BoardApp extends React.Component {
alertHandled={this.props.alertHandled}
buttonText='Start Game'
hideHandler={() => 'nothing'}
+ cancelButtonText='Leave Game'
+ cancelHandler={leaveGame}
+ cancelDisabled={this.state.readyToStart}
preventHiding={true}
+ disabled={!this.props.game.readyToStart}
handler={startGame}>
-
- Pre Game
- When all players have joined click 'Start Game'!
- Players
-
- - {this.props.player.name}
- {this.props.game.otherPlayers.map((p, i) => (
- -
- {p.player.name}
-
- ))}
-
-
+
);
} else if (alert && alert.type === ALERTS.proposedTrade) {
diff --git a/src/components/farm/interface.js b/src/components/farm/interface.js
index 635b106..ff98acf 100644
--- a/src/components/farm/interface.js
+++ b/src/components/farm/interface.js
@@ -31,7 +31,7 @@ import { itemCard, fateCard } from 'game.js'
export { initialize, buy, roll, endTurn, loan, trade, submitTradeAccept,
submitTradeDeny, submitTradeCancel, audit, handleMessage,
nextAction, buyUncleBert, actionsFinished, skip, endAiTurn,
- startGame }
+ startGame, readyToStart, leaveGame }
let store;
let movingTimer = 0;
@@ -46,6 +46,9 @@ function handleMessage(evt) {
return;
}
batch(() => {
+ if (data.event === 'left-game') {
+ window.location.href = window.location.pathname;
+ }
if (data.game.state === GAME_STATES.preGame) {
store.dispatch(alert(ALERTS.preGame, '', 'pre-game'));
}
@@ -80,7 +83,7 @@ function handleMessage(evt) {
store.dispatch(movePlayer(data.player.space, -1, data.player.color));
store.dispatch(setHarvestTable(data.harvestTable));
}
- // new player(s) added to game, put them on the board
+ // player(s) added or removed from game, put them on the board
if (data.game.otherPlayers.length !== store.getState().farm.game.otherPlayers.length) {
const otherPlayers = store.getState().farm.game.otherPlayers;
const newPlayers = data.game.otherPlayers.filter(
@@ -199,6 +202,14 @@ function startGame() {
sendCommand({ type: 'start-game' });
}
+function readyToStart() {
+ sendCommand({ type: 'ready-to-start' });
+}
+
+function leaveGame() {
+ sendCommand({ type: 'leave-game' });
+}
+
// TODO share with Board.jsx
// http://stackoverflow.com/questions/149055
function formatMoney(n) {
diff --git a/src/components/farm/reducers.js b/src/components/farm/reducers.js
index 865130b..0196ff0 100644
--- a/src/components/farm/reducers.js
+++ b/src/components/farm/reducers.js
@@ -105,10 +105,15 @@ const initialState = {
state: GAME_STATES.preTurn,
turn: 0,
oldMessages: [],
+ name: '',
settings: { downPayment: 0.2,
loanInterest: 0.2,
maxDebt: 50000,
- auditThreshold: 250000 }
+ auditThreshold: 250000,
+ startingOtbs: 2,
+ startingCash: 5000,
+ startingDebt: 5000 },
+ readyToStart: false
},
ui: { card: { type: 'no-card', contents: '', total: 0 },
cards: [],
diff --git a/src/server/db.scm b/src/server/db.scm
index 17138e6..417207d 100644
--- a/src/server/db.scm
+++ b/src/server/db.scm
@@ -127,6 +127,11 @@
(exec (sql db "insert into user_games(user_id, game_id) values (?, ?);")
user-id game-id)))
+(define (db-remove-user-game user-id game-id)
+ (with-db (db)
+ (exec (sql db "delete from user_games where user_id=? and game_id=?;")
+ user-id game-id)))
+
(define (db-fetch-user-games user-id)
(with-db (db)
(query fetch-column
diff --git a/src/server/farm.scm b/src/server/farm.scm
index 7cc723e..53370d2 100644
--- a/src/server/farm.scm
+++ b/src/server/farm.scm
@@ -127,7 +127,8 @@
(mutex initform: (make-mutex 'player) accessor: player-mutex)
(harvesting initform: #f accessor: player-harvesting)
(hay-doubled initform: #f accessor: player-hay-doubled)
- (corn-doubled initform: #f accessor: player-corn-doubled)))
+ (corn-doubled initform: #f accessor: player-corn-doubled)
+ (ready-to-start initform: #f accessor: player-ready-to-start)))
(define-class ()
((processing-turn initform: #f accessor: ai-processing-turn)))
@@ -564,11 +565,19 @@
#f))
(state . ,(symbol->string (game-state g)))
(turn . ,(game-turn g))
+ (name . ,(game-name 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))
- (trade . ,(game-setting 'trade g))))))))
+ (startingOtbs . ,(game-setting 'starting-otbs g))
+ (startingCash . ,(game-setting 'starting-cash g))
+ (startingDebt . ,(game-setting 'starting-debt g))
+ (trade . ,(game-setting 'trade g))))
+ (readyToStart . ,(fold (lambda (p r)
+ (and (player-ready-to-start p) r))
+ #t
+ (game-players g)))))))
(define (buy-crop crop unnormalized-crop amount cash-value player game)
(let ((total-cost (* amount (alist-ref unnormalized-crop
@@ -1424,6 +1433,41 @@
(session-set! (sid) 'game-id #f)
(session-set! (sid) 'user-id #f)
(create-start-response "start-init"))
+ ((string=? type "ready-to-start")
+ (safe-set! (player-ready-to-start (*player*)) (not (player-ready-to-start (*player*))))
+ (message-players! (*game*) (*player*) '() type: "update")
+ (create-ws-response (*player*) "update" '()))
+ ((string=? type "kick-player")
+ (let ((kicked-player (find (lambda (p)
+ (equal? (player-name p) (alist-ref 'name msg)))
+ (game-players (*game*)))))
+ (safe-set! (game-colors (*game*))
+ (cons (player-color kicked-player) (game-colors (*game*))))
+ (safe-set! (game-otbs (*game*))
+ (append (game-otbs (*game*))
+ (player-otbs kicked-player)))
+ (safe-set! (game-players (*game*))
+ (filter (lambda (p)
+ (eq? p kicked-player))
+ (game-players (*game*))))
+ (db-remove-user-game (player-user-id kicked-player) (game-id (*game*))))
+ (message-players! (*game*) (*player*) '() type: "update")
+ (create-ws-response (*player*) "update" '()))
+ ((string=? type "leave-game")
+ (safe-set! (game-colors (*game*))
+ (cons (player-color (*player*)) (game-colors (*game*))))
+ (safe-set! (game-otbs (*game*))
+ (append (game-otbs (*game*))
+ (player-otbs (*player*))))
+ (safe-set! (game-players (*game*))
+ (filter (lambda (p)
+ (not (eq? p (*player*))))
+ (game-players (*game*))))
+ (when (not (null? (game-players (*game*))))
+ (safe-set! (game-current-player (*game*)) (car (game-players (*game*)))))
+ (db-remove-user-game (player-user-id (*player*)) (game-id (*game*)))
+ (message-players! (*game*) (*player*) '() type: "left-game")
+ (create-ws-response (*player*) "left-game" '()))
((string=? type "start-game")
(safe-set! (game-state (*game*)) 'pre-turn)
(db-update-game (game-id (*game*)) (symbol->string (game-state (*game*)))
diff --git a/src/style.scss b/src/style.scss
index df63c66..ecc8bbe 100644
--- a/src/style.scss
+++ b/src/style.scss
@@ -798,6 +798,8 @@ $trade-margin: 3rem;
color: white; }
.alert-overlay-contents {
+ max-height: 90vh;
+ overflow: auto;
background: $light-color;
padding: 2rem;
display: flex;