Build out pregame interface

This commit is contained in:
Kenneth Allen 2021-10-17 02:35:47 +11:00
parent e96fbc27ba
commit d1dc1db234
4 changed files with 94 additions and 36 deletions

View File

@ -25,12 +25,17 @@ export default function App() {
}
}
function resetGame() {
dispatch(resetAction())
setUser(undefined)
}
return <Container fluid>
<UserSelector user={user} selectUser={selectUser} removeUser={removeUser} users={state?.players.map(p => p.name) ?? []} locked={state !== undefined && state.stage !== 'starting'} />
{state
? <>
<Game state={state} dispatch={dispatch} playerName={user} />
<button onClick={() => window.confirm('Reset game?') && dispatch(resetAction())}> Reset</button>
<button onClick={() => window.confirm('Reset game?') && resetGame()}> Reset</button>
</>
: <Row>Loading...</Row>
}

View File

@ -21,8 +21,10 @@ const wonderBgExts = ['jpg', 'webp']
export default function Civ({ player, style }: { player: Player, style: 'player' | 'neighbor' | 'compact' }) {
const pStats = useMemo(() => playerStats(player), [player])
const outerStyle = {
backgroundImage: player.wonder && wonderBgExts.map(ext => `url(/assets/wonders/${player.wonder}.${ext})`).join(', '),
const outerStyle: React.CSSProperties = {
backgroundImage: player.wonder && wonderBgExts.map(ext => `url("/assets/wonders/${player.wonder}.${ext}")`).join(', '),
backgroundSize: player.wonder && 'cover',
backgroundPosition: player.wonder && 'center',
}
return <div className='wonder-civ-outer' style={outerStyle}>

View File

@ -1,10 +1,11 @@
/* eslint-disable react/style-prop-object */
import { Dispatch } from 'ketchup-react'
import { Action, Player, State, structures } from 'wonders-common'
import { Action, chooseWonderAction, maxPlayers, minPlayers, Player, startGameAction, State, structures, wonders } from 'wonders-common'
import Civ from '../Civ/Civ'
import Card from '../Card/Card'
import Row from 'react-bootstrap/Row'
import Col from 'react-bootstrap/Col'
import { ErrorMessage, Field, Form, Formik } from 'formik'
function getDistant<T>(arr: T[], idx: number) {
switch (idx) {
@ -17,12 +18,12 @@ function getDistant<T>(arr: T[], idx: number) {
function DistantCivs({ players }: { players: Player[] }) {
return <Row>{players.map(p =>
<Col key={p.name}>
<Civ player={p} style='compact'/>
<Civ player={p} style='compact' />
</Col>
)}</Row>
}
export default function Game({ state, playerName }: { state: State, playerName?: string, dispatch: Dispatch<Action> }) {
export default function Game({ state, playerName, dispatch }: { state: State, playerName?: string, dispatch: Dispatch<Action> }) {
const playerIdx: number | undefined = state.players.findIndex(p => p.name === playerName)
const player: Player | undefined = state.players[playerIdx]
const started = state.stage !== 'play' ? undefined : {
@ -31,28 +32,74 @@ export default function Game({ state, playerName }: { state: State, playerName?:
distant: getDistant(state.players, playerIdx),
}
if (started && player) {
function isWonderInUse(wonder: string) {
return state.players.filter(p => p.name !== playerName).map(p => p.wonder).includes(wonder)
}
if (!player) {
return <DistantCivs players={state.players} />
} else if (started) {
return <>
<DistantCivs players={started.distant}/>
<DistantCivs players={started.distant} />
<Row>
<Col><Civ player={started.left} style='neighbor'/></Col>
<Col><Civ player={started.left} style='neighbor' /></Col>
<Col>
<div>{state?.age === undefined ? '' : `Age ${state.age + 1} ${state.age % 2 === 0 ? '🔃' : '🔄'}`}</div>
<div><button>Discards</button></div>
</Col>
<Col><Civ player={started.right} style='neighbor'/></Col>
<Col><Civ player={started.right} style='neighbor' /></Col>
</Row>
<Row>
<Civ player={player} style='player'/>
<Civ player={player} style='player' />
</Row>
<Row>{player.hand.map(s => structures.get(s)!).map(s =>
<Row>{player.hand?.map(s => structures.get(s)!)?.map(s =>
<Col>
<Card structure={s}/>
<Card structure={s} />
</Col>
)}</Row>
</>
} else {
return <DistantCivs players={state.players}/>
// TODO Pick wonder
return <>
<Row>
<DistantCivs players={state.players} />
</Row>
<Row>
<Formik
initialValues={{ wonder: player.wonder ?? '' }}
validate={values => {
const errors: Partial<typeof values> = {}
if (values.wonder.length === 0) {
errors.wonder = 'Wonder not selected.'
}
if (isWonderInUse(values.wonder)) {
errors.wonder = 'Wonder already in use.'
}
return errors
}}
onSubmit={({ wonder }) => dispatch(chooseWonderAction(player.name, wonder))}
>{() => <Form>
<button type="submit">Choose wonder</button>
{' '}
<Field as="select" name="wonder">
{['', ...wonders.keys()].map(w =>
<option key={w} disabled={w.length === 0 || isWonderInUse(w)}>{w}</option>
)}
</Field>
{' '}
<ErrorMessage name="wonder" />
</Form>}
</Formik>
</Row>
<Row>
<Col><button onClick={() => dispatch(startGameAction(state.players.length))}
disabled={started !== undefined
|| state.players.length < minPlayers
|| state.players.length > maxPlayers
|| state.players.some(p => p.wonder === undefined)}>
Start Game
</button></Col>
</Row>
{/* TODO Pick wonder */}
</>
}
}

View File

@ -2,6 +2,7 @@ import { ErrorMessage, Field, Form, Formik } from 'formik';
import { Dispatch, useState } from 'react';
import sample from 'lodash/sample'
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
const namePlaceholders = [
'Alexander the Great',
@ -21,7 +22,7 @@ export default function UserSelector({ users, user, selectUser, removeUser, lock
const [namePlaceholder] = useState(() => sample(namePlaceholders))
return <Row>
{user
? <>Playing as {user} <button onClick={() => selectUser(undefined)}>Change</button> <button onClick={removeUser} disabled={locked}>Remove</button></>
? <Col>Playing as {user} <button onClick={() => selectUser(undefined)}>Change</button> <button onClick={removeUser} disabled={locked}>Remove</button></Col>
: <Formik
initialValues={{ newUser: '' }}
validate={values => {
@ -36,7 +37,10 @@ export default function UserSelector({ users, user, selectUser, removeUser, lock
}}
>{() => <Form>
<button type="submit">Play as</button>
<Field type="text" name="newUser" placeholder={namePlaceholder}/> <ErrorMessage name="newUser"/>
{' '}
<Field type="text" name="newUser" placeholder={namePlaceholder} />
{' '}
<ErrorMessage name="newUser" />
</Form>}
</Formik>
}