Lobby improvements; leave game.

This commit is contained in:
2020-04-22 18:57:15 -07:00
parent 7ba6f19133
commit 2a6a0b038e
9 changed files with 217 additions and 25 deletions

View File

@@ -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();
}
// <label><input type='checkbox' onClick={this.hidePermanent} /> {`Don't show again`}</label>
render() {
@@ -1823,7 +1829,17 @@ class AlertOverlay extends React.Component {
<div className='alert-overlay-contents'>
{this.props.children}
<br />
<Button onClick={this.buttonClick}>{this.props.buttonText}</Button>
<div>
<Button onClick={this.buttonClick} disabled={!!this.props.disabled}>{this.props.buttonText}</Button>
{this.props.cancelButtonText ? (
<>
{' '}
<Button onClick={this.cancelButtonClick} disabled={!!this.props.cancelDisabled}>
{this.props.cancelButtonText}
</Button>
</>
): (<></>)}
</div>
{!this.props.preventHiding ? (<a onClick={this.hide}>close</a>) : (<></>)}
</div>
</div>
@@ -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 (
<>
<h1>Lobby</h1>
<p>
<b>Game</b>: {name}
</p>
<h3>Players</h3>
<ul>
<li>{this.props.player.name}</li>
{this.props.game.otherPlayers.map((p, i) => (
<li key={i}>
{p.player.name}
</li>
))}
</ul>
<h4>Game Settings</h4>
<ul>
<li><b>Audit Threshold</b>: ${formatMoney(auditThreshold)}</li>
<li><b>Max Debt</b>: ${formatMoney(maxDebt)}</li>
<li><b>Loan Interest</b>: {loanInterest * 100}%</li>
<li><b>Required Down Payment</b>: ${formatMoney(downPayment)}</li>
<li><b>Starting {itemCardShort}</b>: {startingOtbs}</li>
<li><b>Starting Cash</b>: ${formatMoney(startingCash)}</li>
<li><b>Starting Debt</b>: ${formatMoney(startingDebt)}</li>
</ul>
<label>
<input type="checkbox" onChange={this.props.toggleReady} />
Ready to start
</label>
</>
);
}
}
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}>
<Fragment>
<h1>Pre Game</h1>
<p>When all players have joined click 'Start Game'!</p>
<h3>Players</h3>
<ul>
<li>{this.props.player.name}</li>
{this.props.game.otherPlayers.map((p, i) => (
<li key={i}>
{p.player.name}
</li>
))}
</ul>
</Fragment>
<StartGame player={this.props.player}
game={this.props.game}
toggleReady={this.startGameToggleReady}
/>
</AlertOverlay>
);
} else if (alert && alert.type === ALERTS.proposedTrade) {

View File

@@ -40,3 +40,4 @@ export const MESSAGE = 'message'
export const SET_HARVEST_TABLE = 'set-harvest-table'
export const SET_MOVING_SKIP = 'set-moving-skip'
export const SERVER_ERROR = 'server-error'
export const REMOVE_PLAYER = 'remove-player'

View File

@@ -21,14 +21,14 @@ import { UPDATE_GAME, UPDATE_PLAYER, GAME_STATE, SET_SELECTED_CARD, SET_CARDS,
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, MESSAGE, SET_HARVEST_TABLE, SET_CARD_ERROR,
SET_MOVING_SKIP, SERVER_ERROR } from './actionTypes.js'
SET_MOVING_SKIP, SERVER_ERROR, REMOVE_PLAYER } from './actionTypes.js'
export { updateGame, updatePlayer, gameState, setSelectedCard, setCards,
spacePushPlayer, spaceClearPlayers, setOldMessages, setMessagePanelSpace,
mpMouse, setMPDims, movePlayer, setNextAction, nextUIAction,
markActionChangeHandled, nextUIActionSilent, alert, alertHandled,
autoSkip, message, setHarvestTable, setCardError, setMovingSkip,
serverError }
serverError, removePlayer }
function updateGame(update) {
return { type: UPDATE_GAME,
@@ -96,6 +96,11 @@ function movePlayer(newSpace, oldSpace, player) {
newSpace, oldSpace, player };
}
function removePlayer(color) {
return { type: REMOVE_PLAYER,
color };
}
function nextUIAction() {
return { type: NEXT_UI_ACTION };
}

View File

@@ -25,13 +25,13 @@ import { updateGame, updatePlayer, gameState, setSelectedCard, setCards,
movePlayer, setOldMessages, markActionChangeHandled,
mpMouse, rolled, setNextAction, nextUIAction, nextUIActionSilent, alert,
autoSkip, message, alertHandled, setHarvestTable,
setCardError, setMovingSkip, serverError } from './actions.js'
setCardError, setMovingSkip, serverError, removePlayer } from './actions.js'
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,12 @@ function handleMessage(evt) {
return;
}
batch(() => {
if (data.event === 'left-game') {
window.location.href = window.location.pathname;
}
if (data.event === 'player-left-game') {
store.dispatch(removePlayer(data.color));
}
if (data.game.state === GAME_STATES.preGame) {
store.dispatch(alert(ALERTS.preGame, '', 'pre-game'));
}
@@ -80,7 +86,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 +205,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) {

View File

@@ -22,7 +22,7 @@ import { UPDATE_GAME, UPDATE_PLAYER, GAME_STATE, SET_SELECTED_CARD, SET_CARDS,
SET_MP_DIMS, MOVE_PLAYER, SET_NEXT_ACTION, NEXT_UI_ACTION,
MARK_ACTION_CHANGE_HANDLED, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED,
AUTO_SKIP, MESSAGE, SET_HARVEST_TABLE, SET_CARD_ERROR,
SET_MOVING_SKIP, SERVER_ERROR } from './actionTypes.js'
SET_MOVING_SKIP, SERVER_ERROR, REMOVE_PLAYER } from './actionTypes.js'
import { GAME_STATES } from '../../constants.js'
import { spaceContent, corners } from 'game.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: [],
@@ -171,6 +176,21 @@ export default function(state = initialState, action) {
[action.player]: action.newSpace }}
};
}
case REMOVE_PLAYER:
const playerSpace = state.ui.playerSpaces[action.color];
return { ...state,
spaces: state.spaces.map((item, index) => {
if (index === playerSpace) {
return { ...item,
players: item.players
.filter(x => x !== action.color) };
}
return item;
}),
ui: { ...state.ui,
playerSpaces: { ...state.ui.playerSpaces,
[action.player]: -1 }}
};
case SET_OLD_MESSAGES:
return { ...state, oldMessages: action.messages };
case MESSAGE_PANEL_SPACE:

View File

@@ -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

View File

@@ -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 <ai> (<player>)
((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,42 @@
(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*) `((color . ,(symbol->string (player-color (*player*)))))
type: "player-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*)))

View File

@@ -798,6 +798,8 @@ $trade-margin: 3rem;
color: white; }
.alert-overlay-contents {
max-height: 90vh;
overflow: auto;
background: $light-color;
padding: 2rem;
display: flex;