computer mouse problems
This commit is contained in:
@@ -5,6 +5,7 @@ import { useEffect, useState } from 'react'
|
|||||||
import Gameboard from './components/Gameboard/Gameboard'
|
import Gameboard from './components/Gameboard/Gameboard'
|
||||||
import GameConstructor from './components/GameConstructor';
|
import GameConstructor from './components/GameConstructor';
|
||||||
import './App.css'
|
import './App.css'
|
||||||
|
import ResumeGame from './components/ResumeGame';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [state, setState] = useState(initialState);
|
const [state, setState] = useState(initialState);
|
||||||
@@ -18,9 +19,11 @@ function App() {
|
|||||||
<h1>SPLENDOR</h1>
|
<h1>SPLENDOR</h1>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Routes>
|
<Routes>
|
||||||
{/* @ts-ignore */}a
|
{/* @ts-ignore */}
|
||||||
<Route path="/" element={<GameConstructor state={state} setState={setState} />} />
|
<Route path="/" element={<GameConstructor state={state} setState={setState} />} />
|
||||||
{/* @ts-ignore */}
|
{/* @ts-ignore */}
|
||||||
|
<Route path="/resume-game" element={<ResumeGame 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,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { useNavigate } from "react-router-dom"
|
import { Link, useNavigate } from "react-router-dom"
|
||||||
import { CardData, NobleData, PlayerData } from "../util/types";
|
import { CardData, NobleData, PlayerData } from "../util/types";
|
||||||
import { StateProps } from '../util/propTypes';
|
import { StateProps } from '../util/propTypes';
|
||||||
|
|
||||||
@@ -103,7 +103,9 @@ export default function GameConstructor({ state, setState }: StateProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="game-constructor App">
|
<div className="game-constructor App">
|
||||||
<h1>Configure a new game:</h1>
|
<strong>Start a new game</strong>
|
||||||
|
<h2>OR</h2>
|
||||||
|
<strong>Enter your previous game data <Link to={'/resume-game'}>here</Link> to pick up where you left off.</strong>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="P1-NAME">Player 1 Name:</label>
|
<label htmlFor="P1-NAME">Player 1 Name:</label>
|
||||||
@@ -162,7 +164,7 @@ export default function GameConstructor({ state, setState }: StateProps) {
|
|||||||
</input>
|
</input>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
id="P1-START"
|
id="P4-START"
|
||||||
onChange={() => handleRadio(4)}
|
onChange={() => handleRadio(4)}
|
||||||
checked={starter === 3 && input.playerFour.name.length > 0}>
|
checked={starter === 3 && input.playerFour.name.length > 0}>
|
||||||
</input>
|
</input>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { AppState, ResourceCost } from '../../util/types';
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { getChipsActions } from '../Player/ActionMethods';
|
import { getChipsActions } from '../Player/ActionMethods';
|
||||||
import { StateProps } from '../../util/propTypes';
|
import { StateProps } from '../../util/propTypes';
|
||||||
const { validateChips } = getChipsActions;
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import Nobles from './Nobles';
|
import Nobles from './Nobles';
|
||||||
@@ -12,6 +12,7 @@ import AvailableChips from '../Resources/AvailableChips';
|
|||||||
import AllPlayers from '../Player/AllPlayers';
|
import AllPlayers from '../Player/AllPlayers';
|
||||||
import CardRow from '../Card/CardRow';
|
import CardRow from '../Card/CardRow';
|
||||||
import SelectionView from '../Resources/SelectionView';
|
import SelectionView from '../Resources/SelectionView';
|
||||||
|
const { validateChips } = getChipsActions;
|
||||||
|
|
||||||
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>);
|
||||||
@@ -50,17 +51,13 @@ export default function Gameboard({ state, setState }: StateProps) {
|
|||||||
setCardRows(state);
|
setCardRows(state);
|
||||||
}, [state])
|
}, [state])
|
||||||
|
|
||||||
useEffect(() => {
|
// displays state of board if data is populated, otherwise points to game constructor
|
||||||
console.log(state)
|
|
||||||
}, [state])
|
|
||||||
|
|
||||||
// displays state of board if data is populated
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!state.players.length) {
|
if (!state.players.length) {
|
||||||
setView(
|
setView(
|
||||||
<div className="error-page">
|
<div className="error-page">
|
||||||
<strong>Sorry! It appears we've lost track of your game data.</strong>
|
<strong>Sorry! It appears we've lost track of your game data.</strong>
|
||||||
<p>Please head back to the <a href="/">home page</a> to start a fresh game.</p>
|
<p>Please head back to the <Link to="/">home page</Link> to start a fresh game.</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
144
src/components/Gameboard/Nobles.test.ts
Normal file
144
src/components/Gameboard/Nobles.test.ts
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import { describe, test } from "vitest";
|
||||||
|
import { CardData, NobleData, PlayerData } from "../../util/types";
|
||||||
|
|
||||||
|
export const firstNoble: NobleData = {
|
||||||
|
points: 3,
|
||||||
|
resourceCost: {
|
||||||
|
ruby: 0,
|
||||||
|
sapphire: 0,
|
||||||
|
emerald: 0,
|
||||||
|
onyx: 0,
|
||||||
|
diamond: 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const secondNoble: NobleData = {
|
||||||
|
points: 3,
|
||||||
|
resourceCost: {
|
||||||
|
ruby: 3,
|
||||||
|
sapphire: 0,
|
||||||
|
emerald: 0,
|
||||||
|
onyx: 0,
|
||||||
|
diamond: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const exampleOneCards: CardData[] = [
|
||||||
|
{
|
||||||
|
gemValue: "diamond",
|
||||||
|
tier: 3,
|
||||||
|
points: 0,
|
||||||
|
resourceCost: {
|
||||||
|
ruby: 0,
|
||||||
|
sapphire: 0,
|
||||||
|
emerald: 0,
|
||||||
|
onyx: 0,
|
||||||
|
diamond: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gemValue: "diamond",
|
||||||
|
tier: 3,
|
||||||
|
points: 1,
|
||||||
|
resourceCost: {
|
||||||
|
ruby: 0,
|
||||||
|
sapphire: 0,
|
||||||
|
emerald: 0,
|
||||||
|
onyx: 0,
|
||||||
|
diamond: 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gemValue: "diamond",
|
||||||
|
tier: 2,
|
||||||
|
points: 2,
|
||||||
|
resourceCost: {
|
||||||
|
ruby: 0,
|
||||||
|
sapphire: 0,
|
||||||
|
emerald: 0,
|
||||||
|
onyx: 0,
|
||||||
|
diamond: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const legalPlayer: PlayerData = {
|
||||||
|
name: "First example",
|
||||||
|
id: 1,
|
||||||
|
starter: true,
|
||||||
|
turnActive: true,
|
||||||
|
points: 5,
|
||||||
|
nobles: [],
|
||||||
|
cards: exampleOneCards,
|
||||||
|
reservedCards: [],
|
||||||
|
inventory: {
|
||||||
|
ruby: 0,
|
||||||
|
sapphire: 0,
|
||||||
|
emerald: 0,
|
||||||
|
onyx: 0,
|
||||||
|
diamond: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const exampleTwoCards: CardData[] = [
|
||||||
|
{
|
||||||
|
gemValue: "ruby",
|
||||||
|
tier: 2,
|
||||||
|
points: 2,
|
||||||
|
resourceCost: {
|
||||||
|
ruby: 5,
|
||||||
|
sapphire: 0,
|
||||||
|
emerald: 0,
|
||||||
|
onyx: 0,
|
||||||
|
diamond: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gemValue: "ruby",
|
||||||
|
tier: 3,
|
||||||
|
points: 1,
|
||||||
|
resourceCost: {
|
||||||
|
ruby: 4,
|
||||||
|
sapphire: 0,
|
||||||
|
emerald: 0,
|
||||||
|
onyx: 0,
|
||||||
|
diamond: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gemValue: "ruby",
|
||||||
|
tier: 3,
|
||||||
|
points: 0,
|
||||||
|
resourceCost: {
|
||||||
|
ruby: 3,
|
||||||
|
sapphire: 0,
|
||||||
|
emerald: 0,
|
||||||
|
onyx: 0,
|
||||||
|
diamond: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const doesNotIncludeInventory: PlayerData = {
|
||||||
|
name: "second example",
|
||||||
|
id: 2,
|
||||||
|
starter: true,
|
||||||
|
turnActive: true,
|
||||||
|
points: 5,
|
||||||
|
nobles: [],
|
||||||
|
cards: exampleTwoCards,
|
||||||
|
reservedCards: [],
|
||||||
|
inventory: {
|
||||||
|
ruby: 3,
|
||||||
|
sapphire: 3,
|
||||||
|
emerald: 3,
|
||||||
|
onyx: 3,
|
||||||
|
diamond: 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('canPickUpNoble', () => {
|
||||||
|
test('detects noble eligibility by card count', () => {
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import { useEffect } from "react";
|
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import { NobleData, ResourceCost } from "../../util/types";
|
import { NobleData, PlayerData, ResourceCost } from "../../util/types";
|
||||||
import { StateProps } from "../../util/propTypes";
|
import { StateProps } from "../../util/propTypes";
|
||||||
import "./Nobles.css"
|
import "./Nobles.css"
|
||||||
|
import getTotalBuyingPower from "../../util/getTotalBuyingPower";
|
||||||
|
import { useCurrentPlayer } from "../../util/useCurrentPlayer";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
export default function Nobles({ state, setState }: StateProps) {
|
export default function Nobles({ state, setState }: StateProps) {
|
||||||
const removeNoble = (noble: NobleData) => {
|
const removeNoble = (noble: NobleData) => {
|
||||||
@@ -18,6 +20,38 @@ export default function Nobles({ state, setState }: StateProps) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const canPickUpNoble = (player: PlayerData, noble: NobleData): boolean => {
|
||||||
|
const nobleCost = noble.resourceCost;
|
||||||
|
|
||||||
|
const totalBuyingPower = getTotalBuyingPower(player);
|
||||||
|
const playerInventory = player.inventory;
|
||||||
|
|
||||||
|
|
||||||
|
for (let key of Object.keys(totalBuyingPower)) {
|
||||||
|
const typedKey = key as keyof ResourceCost;
|
||||||
|
let coinValue = playerInventory[typedKey] || 0;
|
||||||
|
|
||||||
|
if (!noble.resourceCost[typedKey]) continue;
|
||||||
|
// @ts-ignore
|
||||||
|
if ((totalBuyingPower[typedKey] - coinValue) >= noble.resourceCost[typedKey]) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const currentPlayer = useCurrentPlayer(state);
|
||||||
|
if (!currentPlayer) return;
|
||||||
|
|
||||||
|
for (let each of state.gameboard.nobles) {
|
||||||
|
console.log(`${currentPlayer.name} can pick up noble ${state.gameboard.nobles.indexOf(each) + 1}? ${canPickUpNoble(currentPlayer, each) ? "yes" : "no"}`)
|
||||||
|
}
|
||||||
|
}, [state])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="nobles-panel">
|
<div className="nobles-panel">
|
||||||
<strong>NOBLES</strong>
|
<strong>NOBLES</strong>
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
import cardTierToKey from "../../../util/cardTierToKey";
|
|
||||||
import { initialActions } from "../../../util/stateSetters";
|
|
||||||
import { turnOrderUtil } from "../../../util/turnOrderUtil";
|
|
||||||
import { AppState, CardData, PlayerData, ResourceCost, setStateType } from "../../../util/types";
|
|
||||||
import { useCurrentPlayer } from "../../../util/useCurrentPlayer";
|
|
||||||
import { getTotalBuyingPower } from "./buyCardActions";
|
|
||||||
|
|
||||||
export const buyCard = (card: CardData, state: AppState, setState: setStateType) => {
|
|
||||||
/**
|
|
||||||
* functionality: adds target card's data to current player's collection of cards,
|
|
||||||
* removes the card from the active state of the gameboard, replaces it with
|
|
||||||
* a new card in the correct tier, and runs turn order utility
|
|
||||||
*
|
|
||||||
* @param card -> the target card, @param state -> current app state
|
|
||||||
*/
|
|
||||||
|
|
||||||
let currentPlayer = useCurrentPlayer(state);
|
|
||||||
|
|
||||||
setState((prev: AppState) => {
|
|
||||||
if (!currentPlayer) return prev;
|
|
||||||
|
|
||||||
const { newPlayers, roundIncrement } = turnOrderUtil(prev, currentPlayer);
|
|
||||||
let newPlayerInventory = currentPlayer.inventory;
|
|
||||||
let newResourcePool = prev.gameboard.tradingResources;
|
|
||||||
const totalBuyingPower = getTotalBuyingPower(state);
|
|
||||||
|
|
||||||
// iterate through cost values of card to purchase
|
|
||||||
for (let [gem, cost] of Object.entries(card.resourceCost)) {
|
|
||||||
if (cost < 1) continue;
|
|
||||||
let inventoryValue = newPlayerInventory[gem as keyof ResourceCost];
|
|
||||||
let globalResource = newResourcePool[gem as keyof ResourceCost];
|
|
||||||
|
|
||||||
if (!inventoryValue || !globalResource) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
let i = cost;
|
|
||||||
|
|
||||||
// prevents duplication of resources when purchasing a card using permanent resources from cards
|
|
||||||
if (totalBuyingPower[gem as keyof ResourceCost] !== inventoryValue) {
|
|
||||||
console.log('caught');
|
|
||||||
}
|
|
||||||
|
|
||||||
while (i > 0) {
|
|
||||||
inventoryValue -= 1;
|
|
||||||
globalResource += 1;
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
|
|
||||||
newResourcePool[gem as keyof ResourceCost] = globalResource;
|
|
||||||
newPlayerInventory[gem as keyof ResourceCost] = inventoryValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let updatedPlayer: PlayerData = {
|
|
||||||
...currentPlayer,
|
|
||||||
cards: [...currentPlayer.cards, card],
|
|
||||||
inventory: newPlayerInventory
|
|
||||||
}
|
|
||||||
|
|
||||||
let newScore = 0;
|
|
||||||
for (let each of updatedPlayer.cards) {
|
|
||||||
newScore += each.points || 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedPlayer.points = newScore;
|
|
||||||
const idx = newPlayers.findIndex((one: PlayerData) => one.id === currentPlayer?.id);
|
|
||||||
newPlayers[idx] = updatedPlayer;
|
|
||||||
let updatedRows = prev.gameboard.cardRows;
|
|
||||||
|
|
||||||
if (card.tier) {
|
|
||||||
const tierKey = cardTierToKey(card.tier);
|
|
||||||
updatedRows[tierKey] = prev.gameboard.cardRows[tierKey].filter((found: CardData) => found.resourceCost !== card.resourceCost);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
round: (roundIncrement ? prev.round + 1 : prev.round),
|
|
||||||
players: newPlayers,
|
|
||||||
gameboard: {
|
|
||||||
...prev.gameboard,
|
|
||||||
tradingResources: prev.gameboard.tradingResources,
|
|
||||||
cardRows: updatedRows
|
|
||||||
},
|
|
||||||
actions: initialActions
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
12
src/components/ResumeGame.tsx
Normal file
12
src/components/ResumeGame.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { StateProps } from "../util/propTypes";
|
||||||
|
|
||||||
|
export default function ResumeGame({ state, setState }: StateProps) {
|
||||||
|
return (
|
||||||
|
<div className="resume-game App">
|
||||||
|
<h2>Congrats! You've found an in-progress feature.</h2>
|
||||||
|
<p>Check back in here later to enter a save game token to pick up a game from where you left off.</p>
|
||||||
|
<p>In the meantime, click <Link to="/">here</Link> to head back home.</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user