Merge pull request #1 from innocuous-symmetry/state-mgmt-backtrack
merge into master, turn order functionality and resource handling
This commit is contained in:
42
src/App.tsx
42
src/App.tsx
@@ -1,51 +1,21 @@
|
|||||||
import { BrowserRouter, Routes, Route } from 'react-router-dom'
|
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
import { BrowserRouter, Routes, Route } from 'react-router-dom'
|
||||||
import Gameboard from './components/Gameboard/Gameboard'
|
import Gameboard from './components/Gameboard/Gameboard'
|
||||||
import GameConstructor from './util/GameConstructor';
|
import GameConstructor from './components/GameConstructor';
|
||||||
import { PlayerData, NobleData, CardData, AppState } from './util/types';
|
import { initialState } from './util/stateSetters';
|
||||||
import CardDeck from './data/cards.json';
|
|
||||||
import './App.css'
|
import './App.css'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [state, setState] = useState<AppState>({
|
const [state, setState] = useState(initialState);
|
||||||
gameboard: {
|
|
||||||
nobles: new Array<NobleData>,
|
|
||||||
cardRows: {
|
|
||||||
tierOne: new Array<CardData>,
|
|
||||||
tierTwo: new Array<CardData>,
|
|
||||||
tierThree: new Array<CardData>,
|
|
||||||
},
|
|
||||||
tradingResources: {
|
|
||||||
ruby: 7,
|
|
||||||
sapphire: 7,
|
|
||||||
emerald: 7,
|
|
||||||
diamond: 7,
|
|
||||||
onyx: 7,
|
|
||||||
gold: 5
|
|
||||||
},
|
|
||||||
deck: CardDeck,
|
|
||||||
},
|
|
||||||
round: 1,
|
|
||||||
players: new Array<PlayerData>,
|
|
||||||
actions: {
|
|
||||||
getChips: {
|
|
||||||
active: false
|
|
||||||
},
|
|
||||||
buyCard: {
|
|
||||||
active: false
|
|
||||||
},
|
|
||||||
reserveCard: {
|
|
||||||
active: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<h1>SPLENDOR</h1>
|
<h1>SPLENDOR</h1>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Routes>
|
<Routes>
|
||||||
|
{/* @ts-ignore */}
|
||||||
<Route path="/" element={<GameConstructor state={state} setState={setState} />} />
|
<Route path="/" element={<GameConstructor state={state} setState={setState} />} />
|
||||||
|
{/* @ts-ignore */}
|
||||||
<Route path="/game" element={<Gameboard state={state} setState={setState} />} />
|
<Route path="/game" element={<Gameboard state={state} setState={setState} />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { useNavigate } from "react-router-dom"
|
import { useNavigate } from "react-router-dom"
|
||||||
import { v4 } from "uuid";
|
import { CardData, NobleData, PlayerData, StateProps } from "../util/types";
|
||||||
import { CardData, NobleData, PlayerData, StateProps } from "./types";
|
|
||||||
|
|
||||||
interface InputState {
|
interface InputState {
|
||||||
playerOne: PlayerInput
|
playerOne: PlayerInput
|
||||||
@@ -1,21 +1,71 @@
|
|||||||
|
// types, data, utils
|
||||||
|
import { AppState, PlayerData, ResourceCost, SetActionType, StateProps } from '../../util/types';
|
||||||
|
import { setStateBuyCard, setStateGetChips, setStateReserveCard } from '../../util/stateSetters';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { AppState, FullDeck, NobleData, StateProps } from '../../util/types';
|
|
||||||
import AllPlayers from '../Player/AllPlayers';
|
|
||||||
import AvailableChips from '../Resources/AvailableChips';
|
|
||||||
import CardRow from './CardRow';
|
|
||||||
import Nobles from './Nobles';
|
import Nobles from './Nobles';
|
||||||
import NobleStore from '../../data/nobles.json';
|
|
||||||
import useActionStatus from '../../util/useActionStatus';
|
// components
|
||||||
|
import initializeBoard from '../../util/initializeBoard';
|
||||||
|
import AvailableChips from '../Resources/AvailableChips';
|
||||||
|
import AllPlayers from '../Player/AllPlayers';
|
||||||
|
import CardRow from '../Card/CardRow';
|
||||||
|
import { validateChips } from '../Player/ActionMethods';
|
||||||
|
import SelectionView from '../Resources/SelectionView';
|
||||||
|
|
||||||
export default function Gameboard({ state, setState }: StateProps) {
|
export default function Gameboard({ state, setState }: StateProps) {
|
||||||
const [view, setView] = useState(<p>Loading...</p>)
|
const [view, setView] = useState(<p>Loading...</p>);
|
||||||
const [selection, setSelection] = useState<Array<String>>([]);
|
|
||||||
const chipSelection = { selection, setSelection };
|
|
||||||
|
|
||||||
|
// callbacks for lifting state
|
||||||
|
const liftSelection = useCallback((value: keyof ResourceCost) => {
|
||||||
|
if (!state.actions.getChips.active) return;
|
||||||
|
|
||||||
|
setState((prev: AppState) => {
|
||||||
|
let newSelection = prev.actions.getChips.selection;
|
||||||
|
newSelection?.push(value);
|
||||||
|
|
||||||
|
let newState = {
|
||||||
|
...prev,
|
||||||
|
actions: {
|
||||||
|
...state.actions,
|
||||||
|
getChips: {
|
||||||
|
active: true,
|
||||||
|
selection: newSelection,
|
||||||
|
valid: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = validateChips(newState);
|
||||||
|
newState.actions.getChips.valid = result;
|
||||||
|
|
||||||
|
return newState;
|
||||||
|
})
|
||||||
|
}, [state]);
|
||||||
|
|
||||||
|
const setActionState = useCallback((value: SetActionType, player: PlayerData) => {
|
||||||
|
if (!player?.turnActive) return;
|
||||||
|
|
||||||
|
switch (value) {
|
||||||
|
case 0:
|
||||||
|
if (!state.actions.getChips.active) setState((prev) => setStateGetChips(prev));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (!state.actions.buyCard.active) setState((prev) => setStateBuyCard(prev));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (!state.actions.reserveCard.active) setState((prev) => setStateReserveCard(prev));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// util functions, setup on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initializeBoard();
|
initializeBoard(state, setState);
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
// displays state of board if data is populated
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!state.players.length) {
|
if (!state.players.length) {
|
||||||
setView(
|
setView(
|
||||||
@@ -32,61 +82,15 @@ export default function Gameboard({ state, setState }: StateProps) {
|
|||||||
<CardRow tier={3} cards={state.gameboard.cardRows.tierThree} />
|
<CardRow tier={3} cards={state.gameboard.cardRows.tierThree} />
|
||||||
<CardRow tier={2} cards={state.gameboard.cardRows.tierTwo} />
|
<CardRow tier={2} cards={state.gameboard.cardRows.tierTwo} />
|
||||||
<CardRow tier={1} cards={state.gameboard.cardRows.tierOne} />
|
<CardRow tier={1} cards={state.gameboard.cardRows.tierOne} />
|
||||||
<AvailableChips liftFromChildren={liftFromChildren} chipSelection={chipSelection} state={state} setState={setState} />
|
<SelectionView state={state} setState={setState} />
|
||||||
<AllPlayers liftFromChildren={liftFromChildren} chipSelection={chipSelection} state={state} setState={setState} />
|
<AvailableChips state={state} setState={setState} liftSelection={liftSelection} />
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
<AllPlayers state={state} setState={setState} setActionState={setActionState} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, [state]);
|
}, [state]);
|
||||||
|
|
||||||
const shuffleDeck = () => {
|
// render
|
||||||
if (!state.gameboard.deck) return;
|
|
||||||
let newDeck: FullDeck = state.gameboard.deck;
|
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(newDeck)) {
|
|
||||||
for (let i = value.length - 1; i > 0; i--) {
|
|
||||||
const j = Math.floor(Math.random() * (i + 1))
|
|
||||||
const temp = value[i];
|
|
||||||
value[i] = value[j];
|
|
||||||
value[j] = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setState({ ...state, gameboard: { ...state.gameboard, deck: newDeck }})
|
|
||||||
}
|
|
||||||
|
|
||||||
const setNobles = () => {
|
|
||||||
let newNobles = NobleStore.nobles;
|
|
||||||
let shuffledNobles = new Array<NobleData>;
|
|
||||||
|
|
||||||
while (shuffledNobles.length < 4) {
|
|
||||||
const rand = Math.floor(Math.random() * newNobles.length);
|
|
||||||
const randNoble = newNobles.splice(rand,1)[0];
|
|
||||||
shuffledNobles.push(randNoble);
|
|
||||||
}
|
|
||||||
|
|
||||||
setState({ ...state, gameboard: { ...state.gameboard, nobles: shuffledNobles }})
|
|
||||||
}
|
|
||||||
|
|
||||||
const initializeBoard = () => {
|
|
||||||
shuffleDeck();
|
|
||||||
|
|
||||||
let newDeck = state.gameboard.cardRows;
|
|
||||||
for (const [key, value] of Object.entries(state.gameboard.deck)) {
|
|
||||||
while (newDeck[key as keyof FullDeck].length < 4) {
|
|
||||||
// @ts-ignore
|
|
||||||
const nextCard = value.shift();
|
|
||||||
newDeck[key as keyof FullDeck].push(nextCard);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setState({ ...state, gameboard: { ...state.gameboard, cardRows: newDeck } })
|
|
||||||
setNobles();
|
|
||||||
}
|
|
||||||
|
|
||||||
const liftFromChildren = useCallback((childState: AppState) => {
|
|
||||||
setState(childState);
|
|
||||||
}, [state]);
|
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
103
src/components/Player/ActionMethods.ts
Normal file
103
src/components/Player/ActionMethods.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import { AppState, PlayerData, ResourceCost, setStateType } from "../../util/types";
|
||||||
|
import { turnOrderUtil } from "../../util/TurnOrderUtil";
|
||||||
|
import { initialActions } from "../../util/stateSetters";
|
||||||
|
|
||||||
|
export const _getChips = (resource: string | Array<keyof ResourceCost>, dynamic: PlayerData | undefined, setState: setStateType) => {
|
||||||
|
if (!dynamic || !dynamic?.turnActive) return;
|
||||||
|
|
||||||
|
setState((prev: AppState) => {
|
||||||
|
const { newPlayers, roundIncrement } = turnOrderUtil(prev, dynamic);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
round: (roundIncrement ? prev.round + 1 : prev.round),
|
||||||
|
gameboard: {
|
||||||
|
...prev.gameboard,
|
||||||
|
tradingResources: {
|
||||||
|
...prev.gameboard.tradingResources,
|
||||||
|
[resource as keyof ResourceCost]: prev.gameboard.tradingResources[resource as keyof ResourceCost] -= 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
players: newPlayers
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const validateChips = (state: AppState): boolean => {
|
||||||
|
if (!state.actions.getChips.active || !state.actions.getChips.selection) return false;
|
||||||
|
|
||||||
|
const selection = state.actions.getChips.selection;
|
||||||
|
|
||||||
|
if (selection.length === 0 || selection.length > 3) return false;
|
||||||
|
const unique = new Set(selection);
|
||||||
|
|
||||||
|
if (selection.length === 3 && selection.length > unique.size) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getChips = (state: AppState, setState: setStateType): boolean => {
|
||||||
|
/**
|
||||||
|
* features:
|
||||||
|
* update their inventory state
|
||||||
|
* update the total available resources
|
||||||
|
*/
|
||||||
|
|
||||||
|
let targetPlayer: PlayerData;
|
||||||
|
|
||||||
|
for (let each in state.players) {
|
||||||
|
if (state.players[each].turnActive) {
|
||||||
|
targetPlayer = state.players[each];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setState((prev) => {
|
||||||
|
const { newPlayers, roundIncrement } = turnOrderUtil(state, targetPlayer);
|
||||||
|
const idx = newPlayers.indexOf(targetPlayer);
|
||||||
|
const resources = state.actions.getChips.selection;
|
||||||
|
let newResources = prev.gameboard.tradingResources;
|
||||||
|
|
||||||
|
if (resources) {
|
||||||
|
for (let value of resources) {
|
||||||
|
|
||||||
|
// update player inventory
|
||||||
|
for (let each in newPlayers[idx].inventory) {
|
||||||
|
if (value === each) {
|
||||||
|
newPlayers[idx].inventory[value] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update globally available resources
|
||||||
|
for (let each in newResources) {
|
||||||
|
if (value === each) {
|
||||||
|
newResources[value] -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
round: (roundIncrement ? prev.round + 1 : prev.round),
|
||||||
|
gameboard: {
|
||||||
|
...prev.gameboard,
|
||||||
|
tradingResources: newResources
|
||||||
|
},
|
||||||
|
players: newPlayers,
|
||||||
|
actions: initialActions
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(state.players);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const buyCard = () => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const reserveCard = () => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
@@ -1,26 +1,17 @@
|
|||||||
import Player from "./Player";
|
import Player from "./Player";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import { PlayerData, StateProps } from "../../util/types";
|
|
||||||
import { Dispatch, SetStateAction, useEffect } from "react";
|
|
||||||
import "./AllPlayers.css"
|
import "./AllPlayers.css"
|
||||||
|
import { PlayerData, SetActionType, StateProps } from "../../util/types";
|
||||||
|
|
||||||
interface AllPlayersProps extends StateProps {
|
interface AllPlayersProps extends StateProps {
|
||||||
liftFromChildren?: any,
|
setActionState: (value: SetActionType, player?: PlayerData) => void
|
||||||
chipSelection: {
|
|
||||||
selection: String[],
|
|
||||||
setSelection: Dispatch<SetStateAction<Array<String>>>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AllPlayers({ state, setState, chipSelection, liftFromChildren }: AllPlayersProps) {
|
export default function AllPlayers({ state, setState, setActionState }: AllPlayersProps) {
|
||||||
useEffect(() => {
|
|
||||||
return;
|
|
||||||
}, [state])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="all-players">
|
<div className="all-players">
|
||||||
{
|
{
|
||||||
state.players?.map((player: PlayerData) => <Player key={v4()} liftFromChildren={liftFromChildren} chipSelection={chipSelection} player={player} state={state} setState={setState} />)
|
state.players?.map((player: PlayerData) => <Player key={v4()} player={player} state={state} setState={setState} setActionState={setActionState} />)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,85 +1,32 @@
|
|||||||
import { AppState, ActionPrompts, GameActions, PlayerData, ResourceCost, StateProps } from "../../util/types";
|
import { PlayerData, SetActionType, StateProps } from "../../util/types"
|
||||||
import { Dispatch, SetStateAction, useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { TurnOrderUtil } from "../../util/TurnOrderUtil";
|
|
||||||
import useActionType from "../../util/useActionType";
|
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
|
|
||||||
interface PlayerProps extends StateProps {
|
interface PlayerProps extends StateProps {
|
||||||
player: PlayerData
|
player: PlayerData,
|
||||||
chipSelection: {
|
setActionState: (value: SetActionType, player?: PlayerData) => void
|
||||||
selection: String[],
|
|
||||||
setSelection: Dispatch<SetStateAction<Array<String>>>
|
|
||||||
},
|
|
||||||
liftFromChildren: any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Player({ player, state, setState, chipSelection, liftFromChildren }: PlayerProps) {
|
export default function Player({ player, state, setState, setActionState }: PlayerProps) {
|
||||||
const [actionPrompt, setActionPrompt] = useState(ActionPrompts[0]);
|
const [dynamic, setDynamic] = useState<PlayerData>();
|
||||||
const [actionType, setActionType] = useState<GameActions>(GameActions.AWAIT);
|
const [prompt, setPrompt] = useState("Your turn! Select an action type below.");
|
||||||
const [dynamic, setDynamic] = useState<PlayerData | undefined>();
|
const [actionSelection, setActionSelection] = useState(-1);
|
||||||
const { selection, setSelection } = chipSelection;
|
|
||||||
|
useEffect(() => setDynamic(state.players.find((element: PlayerData) => element.id === player.id)), [state]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return;
|
setActionState(actionSelection, dynamic);
|
||||||
}, [selection, setSelection])
|
|
||||||
|
|
||||||
useEffect(() => {
|
if (state.actions.getChips.active) {
|
||||||
setDynamic(state.players.find((element: PlayerData) => element.id === player.id));
|
setPrompt('Make your selection of up to three chips.');
|
||||||
}, [state, setState]);
|
} else if (state.actions.buyCard.active) {
|
||||||
|
setPrompt('Choose a card above to purchase.');
|
||||||
useEffect(() => {
|
} else if (state.actions.reserveCard.active) {
|
||||||
const newState = useActionType(state, actionType);
|
setPrompt('Choose a card above to reserve.');
|
||||||
|
} else {
|
||||||
switch (actionType) {
|
setPrompt("Your turn! Select an action type below.");
|
||||||
case GameActions.GETCHIPS:
|
|
||||||
setActionPrompt(ActionPrompts[1]);
|
|
||||||
getChips(newState);
|
|
||||||
setSelection([]);
|
|
||||||
break;
|
|
||||||
case GameActions.BUYCARD:
|
|
||||||
setActionPrompt(ActionPrompts[2]);
|
|
||||||
break;
|
|
||||||
case GameActions.RESERVECARD:
|
|
||||||
setActionPrompt(ActionPrompts[3]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}, [actionType]);
|
}, [actionSelection])
|
||||||
|
|
||||||
const getChips = (newState: AppState) => {
|
|
||||||
if (!dynamic?.turnActive) return;
|
|
||||||
setActionPrompt(ActionPrompts[1]);
|
|
||||||
|
|
||||||
if (selection.length < 3) return;
|
|
||||||
|
|
||||||
setState(() => {
|
|
||||||
const { newPlayers, roundIncrement } = TurnOrderUtil(newState, dynamic);
|
|
||||||
console.log(newPlayers)
|
|
||||||
let newResources = newState.gameboard.tradingResources;
|
|
||||||
|
|
||||||
for (let item of selection) {
|
|
||||||
for (let [key, value] of Object.entries(newResources)) {
|
|
||||||
if (key === item) {
|
|
||||||
newResources[key as keyof ResourceCost] = value - 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...newState,
|
|
||||||
round: (roundIncrement ? newState.round + 1 : newState.round),
|
|
||||||
players: newPlayers,
|
|
||||||
gameboard: {
|
|
||||||
...newState.gameboard,
|
|
||||||
tradingResources: newResources
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
liftFromChildren(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="player-ui" key={v4()}>
|
<div className="player-ui" key={v4()}>
|
||||||
@@ -89,11 +36,10 @@ export default function Player({ player, state, setState, chipSelection, liftFro
|
|||||||
<p>Is {player.starter || "not"} round starter</p>
|
<p>Is {player.starter || "not"} round starter</p>
|
||||||
|
|
||||||
{/* Dynamic data from state */}
|
{/* Dynamic data from state */}
|
||||||
<p>{dynamic?.turnActive ? actionPrompt : "..."}</p>
|
<p>{dynamic?.turnActive ? prompt : '...'}</p>
|
||||||
|
<button onClick={() => setActionSelection(0)}>Get Chips</button>
|
||||||
<button onClick={() => setActionType(GameActions.GETCHIPS)}>Get Chips</button>
|
<button onClick={() => setActionSelection(1)}>Buy Card</button>
|
||||||
<button onClick={()=> setActionType(GameActions.BUYCARD)}>Buy a Card</button>
|
<button onClick={() => setActionSelection(2)}>Reserve Card</button>
|
||||||
<button onClick={()=> setActionType(GameActions.RESERVECARD)}>Reserve a Card</button>
|
|
||||||
<div className="player-cards"></div>
|
<div className="player-cards"></div>
|
||||||
<div className="player-resources"></div>
|
<div className="player-resources"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,59 +1,31 @@
|
|||||||
import { GameActions, ResourceCost, StateProps } from "../../util/types";
|
import { ResourceCost, StateProps } from "../../util/types";
|
||||||
|
import { useEffect } from "react";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import "./AvailableChips.css"
|
import "./AvailableChips.css"
|
||||||
import { Dispatch, SetStateAction, useEffect, useState } from "react";
|
import { setStateGetChips } from "../../util/stateSetters";
|
||||||
import useActionStatus from "../../util/useActionStatus";
|
|
||||||
|
|
||||||
interface AvailableChipsProps extends StateProps {
|
interface ResourceProps extends StateProps {
|
||||||
liftFromChildren: any,
|
liftSelection: (value: keyof ResourceCost) => void
|
||||||
chipSelection: {
|
|
||||||
selection: String[],
|
|
||||||
setSelection: Dispatch<SetStateAction<Array<String>>>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AvailableChips({ state, chipSelection, liftFromChildren }: AvailableChipsProps) {
|
export default function AvailableChips({ state, setState, liftSelection }: ResourceProps) {
|
||||||
const { selection, setSelection } = chipSelection;
|
|
||||||
|
|
||||||
const handleSelection = (key: string) => {
|
|
||||||
console.log(key)
|
|
||||||
console.log(state);
|
|
||||||
|
|
||||||
if (selection.length > 3) return;
|
|
||||||
|
|
||||||
setSelection((prev) => {
|
|
||||||
let newValue = prev;
|
|
||||||
newValue.push(key);
|
|
||||||
return newValue;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
useActionStatus(state);
|
return;
|
||||||
}, [state])
|
}, [state])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="available-chips">
|
<div className="available-chips">
|
||||||
|
|
||||||
<div className="current-selection">
|
|
||||||
<strong>Selection:</strong>
|
|
||||||
{ selection.map((each) => <p key={v4()}>{each}</p>) }
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Object.keys(state.gameboard.tradingResources).map((key: string) => {
|
Object.keys(state.gameboard.tradingResources).map((key: string | keyof ResourceCost) => {
|
||||||
return (
|
return (
|
||||||
<div key={v4()} className={`chips-${key}`}>
|
<div key={v4()} className={`chips-${key}`}>
|
||||||
<button
|
<button key={v4()} value={key} onClick={() => liftSelection(key as keyof ResourceCost)}>
|
||||||
onClick={() => handleSelection(key)}
|
|
||||||
value={key}>
|
|
||||||
{key}: {state.gameboard.tradingResources[key as keyof ResourceCost]}
|
{key}: {state.gameboard.tradingResources[key as keyof ResourceCost]}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
38
src/components/Resources/SelectionView.tsx
Normal file
38
src/components/Resources/SelectionView.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { v4 } from "uuid";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { ResourceCost, StateProps } from "../../util/types";
|
||||||
|
import { setStateGetChips } from "../../util/stateSetters";
|
||||||
|
import { GetChipsHTML } from "./ViewHTML";
|
||||||
|
|
||||||
|
export default function SelectionView({ state, setState }: StateProps) {
|
||||||
|
const actionTypes = [
|
||||||
|
state.actions.getChips,
|
||||||
|
state.actions.buyCard,
|
||||||
|
state.actions.reserveCard
|
||||||
|
]
|
||||||
|
const [view, setView] = useState(<></>);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setView(() => {
|
||||||
|
switch (true) {
|
||||||
|
case (actionTypes[0].active):
|
||||||
|
return <GetChipsHTML state={state} setState={setState} />
|
||||||
|
case (actionTypes[1].active):
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{actionTypes[1].active && <strong>Your selection is {actionTypes[1].valid || "not"} valid</strong>}
|
||||||
|
<p>Card will display here</p>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [state])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="selection-view">
|
||||||
|
{ view }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
36
src/components/Resources/ViewHTML.tsx
Normal file
36
src/components/Resources/ViewHTML.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { v4 } from "uuid";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { setStateGetChips } from "../../util/stateSetters";
|
||||||
|
import { ResourceCost, StateProps } from "../../util/types";
|
||||||
|
import { getChips } from "../Player/ActionMethods";
|
||||||
|
|
||||||
|
export const GetChipsHTML = ({ state, setState }: StateProps) => {
|
||||||
|
const [prompt, setPrompt] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!state.actions.getChips.active) setPrompt("");
|
||||||
|
if (state.actions.getChips.selection?.length === 0) {
|
||||||
|
setPrompt("Please make a selection.");
|
||||||
|
} else {
|
||||||
|
setPrompt(`Your selection is ${state.actions.getChips.valid ? '' : "not"} valid`);
|
||||||
|
}
|
||||||
|
}, [state])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="selection-view">
|
||||||
|
<strong>{prompt}</strong>
|
||||||
|
<div className="current-selections">
|
||||||
|
{
|
||||||
|
state.actions.getChips.active &&
|
||||||
|
state.actions.getChips.selection?.map((each: keyof ResourceCost) => <p key={v4()}>{each}</p>)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
state.actions.getChips.valid ?
|
||||||
|
<button onClick={() => getChips(state, setState)}>Confirm Selection</button>
|
||||||
|
:
|
||||||
|
<button key={v4()} onClick={() => setState((prev) => setStateGetChips(prev))}>Reset Selection</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import { AppState, PlayerData } from "./types";
|
import { AppState, PlayerData } from "./types";
|
||||||
|
|
||||||
export const TurnOrderUtil = (prev: AppState, dynamic: PlayerData) => {
|
export const turnOrderUtil = (prev: AppState, dynamic: PlayerData) => {
|
||||||
let roundIncrement = false;
|
let roundIncrement = false;
|
||||||
const newPlayers = prev.players;
|
const newPlayers = prev.players;
|
||||||
|
|
||||||
for (let each of newPlayers) {
|
for (let each of newPlayers) {
|
||||||
|
console.log(each);
|
||||||
if (each.id === dynamic.id) {
|
if (each.id === dynamic.id) {
|
||||||
each.turnActive = false;
|
each.turnActive = false;
|
||||||
} else if (each.id === dynamic.id + 1) {
|
} else if (each.id === dynamic.id + 1) {
|
||||||
|
|||||||
47
src/util/initializeBoard.ts
Normal file
47
src/util/initializeBoard.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { AppState, FullDeck, NobleData, setStateType } from "./types";
|
||||||
|
import NobleStore from '../data/nobles.json';
|
||||||
|
|
||||||
|
const shuffleDeck = (state: AppState, setState: setStateType) => {
|
||||||
|
if (!state.gameboard.deck) return;
|
||||||
|
let newDeck: FullDeck = state.gameboard.deck;
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(newDeck)) {
|
||||||
|
for (let i = value.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1))
|
||||||
|
const temp = value[i];
|
||||||
|
value[i] = value[j];
|
||||||
|
value[j] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setState({ ...state, gameboard: { ...state.gameboard, deck: newDeck }})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setNobles = (state: AppState, setState: setStateType) => {
|
||||||
|
let newNobles = NobleStore.nobles;
|
||||||
|
let shuffledNobles = new Array<NobleData>;
|
||||||
|
|
||||||
|
while (shuffledNobles.length < 4) {
|
||||||
|
const rand = Math.floor(Math.random() * newNobles.length);
|
||||||
|
const randNoble = newNobles.splice(rand,1)[0];
|
||||||
|
shuffledNobles.push(randNoble);
|
||||||
|
}
|
||||||
|
|
||||||
|
setState({ ...state, gameboard: { ...state.gameboard, nobles: shuffledNobles }})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function initializeBoard(state: AppState, setState: setStateType) {
|
||||||
|
shuffleDeck(state, setState);
|
||||||
|
|
||||||
|
let newDeck = state.gameboard.cardRows;
|
||||||
|
for (const [key, value] of Object.entries(state.gameboard.deck)) {
|
||||||
|
while (newDeck[key as keyof FullDeck].length < 4) {
|
||||||
|
// @ts-ignore
|
||||||
|
const nextCard = value.shift();
|
||||||
|
newDeck[key as keyof FullDeck].push(nextCard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setState({ ...state, gameboard: { ...state.gameboard, cardRows: newDeck } })
|
||||||
|
setNobles(state, setState);
|
||||||
|
}
|
||||||
79
src/util/stateSetters.ts
Normal file
79
src/util/stateSetters.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { AppState, CardData, NobleData, PlayerData, ResourceCost } from "./types";
|
||||||
|
import CardDeck from '../data/cards.json';
|
||||||
|
|
||||||
|
export const initialActions = {
|
||||||
|
buyCard: { active: false },
|
||||||
|
getChips: { active: false },
|
||||||
|
reserveCard: { active: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initialState = {
|
||||||
|
gameboard: {
|
||||||
|
nobles: new Array<NobleData>,
|
||||||
|
cardRows: {
|
||||||
|
tierOne: new Array<CardData>,
|
||||||
|
tierTwo: new Array<CardData>,
|
||||||
|
tierThree: new Array<CardData>,
|
||||||
|
},
|
||||||
|
tradingResources: {
|
||||||
|
ruby: 7,
|
||||||
|
sapphire: 7,
|
||||||
|
emerald: 7,
|
||||||
|
diamond: 7,
|
||||||
|
onyx: 7,
|
||||||
|
gold: 5
|
||||||
|
},
|
||||||
|
deck: CardDeck,
|
||||||
|
},
|
||||||
|
round: 1,
|
||||||
|
players: new Array<PlayerData>,
|
||||||
|
actions: initialActions
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setStateAwaitAction = (prev: AppState) => {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
actions: initialActions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setStateGetChips = (prev: AppState) => {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
actions: {
|
||||||
|
...initialState.actions,
|
||||||
|
getChips: {
|
||||||
|
active: true,
|
||||||
|
selection: new Array<keyof ResourceCost>,
|
||||||
|
valid: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setStateBuyCard = (prev: AppState) => {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
actions: {
|
||||||
|
...initialState.actions,
|
||||||
|
buyCard: {
|
||||||
|
active: true,
|
||||||
|
valid: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setStateReserveCard = (prev: AppState) => {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
actions: {
|
||||||
|
...initialState.actions,
|
||||||
|
reserveCard: {
|
||||||
|
active: true,
|
||||||
|
includeGold: false,
|
||||||
|
valid: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,45 +13,44 @@ export interface AppState {
|
|||||||
},
|
},
|
||||||
round: number,
|
round: number,
|
||||||
players: Array<PlayerData>,
|
players: Array<PlayerData>,
|
||||||
actions: ActionTypes
|
actions: StateActions
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StateProps {
|
interface StateActions {
|
||||||
state: AppState,
|
setAction?: (arg: SetActionType) => void
|
||||||
setState: Dispatch<SetStateAction<AppState>>
|
getChips: {
|
||||||
|
active: boolean
|
||||||
|
selection?: Array<keyof ResourceCost>
|
||||||
|
valid?: boolean
|
||||||
|
confirm?: () => void
|
||||||
|
}
|
||||||
|
buyCard: {
|
||||||
|
active: boolean
|
||||||
|
selection?: CardData
|
||||||
|
valid?: boolean
|
||||||
|
confirm?: () => void
|
||||||
|
}
|
||||||
|
reserveCard: {
|
||||||
|
active: boolean
|
||||||
|
selection?: CardData
|
||||||
|
includeGold?: boolean
|
||||||
|
valid?: boolean
|
||||||
|
confirm?: () => void
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum GameActions {
|
export enum SetActionType {
|
||||||
GETCHIPS,
|
GETCHIPS,
|
||||||
BUYCARD,
|
BUYCARD,
|
||||||
RESERVECARD,
|
RESERVECARD,
|
||||||
AWAIT
|
AWAIT
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ActionTypes {
|
export type setStateType = Dispatch<SetStateAction<AppState>>
|
||||||
getChips: {
|
|
||||||
active: boolean
|
|
||||||
chips?: Array<keyof ResourceCost>
|
|
||||||
valid?: boolean
|
|
||||||
}
|
|
||||||
buyCard: {
|
|
||||||
active: boolean
|
|
||||||
card?: CardData | null
|
|
||||||
}
|
|
||||||
reserveCard: {
|
|
||||||
active: boolean
|
|
||||||
card?: CardData | null
|
|
||||||
includeGold?: boolean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ActionPrompts {
|
export interface StateProps {
|
||||||
"Choose your action type below:",
|
state: AppState,
|
||||||
"Make a selection of three different available resources, or two of the same.",
|
setState: setStateType
|
||||||
"Choose a card to purchase above.",
|
|
||||||
"Select any card above to reserve. You will also automatically take a gold chip.",
|
|
||||||
"Select any card above to reserve. You have the maximum allowed number of chips, so you cannnot take a gold chip.",
|
|
||||||
"It is not your turn."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GameInformation {
|
export interface GameInformation {
|
||||||
|
|||||||
Reference in New Issue
Block a user