Build out pregame interface
This commit is contained in:
parent
e96fbc27ba
commit
d1dc1db234
@ -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>
|
||||
}
|
||||
|
||||
@ -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}>
|
||||
|
||||
@ -1,28 +1,29 @@
|
||||
/* 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) {
|
||||
case 0: return arr.slice(2, -1)
|
||||
case arr.length - 1: return arr.slice(1, -2)
|
||||
default: return [...arr.slice(idx + 2), ...arr.slice(0, idx - 1)]
|
||||
}
|
||||
switch (idx) {
|
||||
case 0: return arr.slice(2, -1)
|
||||
case arr.length - 1: return arr.slice(1, -2)
|
||||
default: return [...arr.slice(idx + 2), ...arr.slice(0, idx - 1)]
|
||||
}
|
||||
}
|
||||
|
||||
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 */}
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,23 +22,26 @@ 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 => {
|
||||
const errors: Partial<typeof values> = {}
|
||||
if (values.newUser.length === 0) {
|
||||
errors.newUser = 'Name cannot be blank.'
|
||||
}
|
||||
return errors
|
||||
}}
|
||||
onSubmit={(values) => {
|
||||
selectUser(values.newUser)
|
||||
}}
|
||||
>{() => <Form>
|
||||
<button type="submit">Play as</button>
|
||||
<Field type="text" name="newUser" placeholder={namePlaceholder}/> <ErrorMessage name="newUser"/>
|
||||
</Form>}
|
||||
initialValues={{ newUser: '' }}
|
||||
validate={values => {
|
||||
const errors: Partial<typeof values> = {}
|
||||
if (values.newUser.length === 0) {
|
||||
errors.newUser = 'Name cannot be blank.'
|
||||
}
|
||||
return errors
|
||||
}}
|
||||
onSubmit={(values) => {
|
||||
selectUser(values.newUser)
|
||||
}}
|
||||
>{() => <Form>
|
||||
<button type="submit">Play as</button>
|
||||
{' '}
|
||||
<Field type="text" name="newUser" placeholder={namePlaceholder} />
|
||||
{' '}
|
||||
<ErrorMessage name="newUser" />
|
||||
</Form>}
|
||||
</Formik>
|
||||
}
|
||||
</Row>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user