diff --git a/src/Home.tsx b/src/Home.tsx index 3e95d96..41d795b 100644 --- a/src/Home.tsx +++ b/src/Home.tsx @@ -13,6 +13,8 @@ import Button from "./components/Button"; import InfoPanel from "./components/InfoPanel"; import { CommonMancalaGame, MancalaGame, Pit } from "mancala.js"; import { GameMove } from "./models/GameMove"; +import PitAnimator from "./animation/PitAnimator"; +import BoardViewModel from "./viewmodel/BoardViewModel"; type ConnectionState = "connecting" | "error" | "connected" | "reconnecting"; @@ -25,19 +27,16 @@ const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => { const [searchingOpponent, setSearchingOpponent] = useState(false); const [game, setGame] = useState(undefined); - const gameRef = React.useRef(game); const [crashMessage, setCrashMessage] = useState(undefined); const [userKeyWhoLeave, setUserKeyWhoLeave] = useState(undefined); - const [animationPitIndex, setAnimationPitIndex] = useState(-1); + const [boardViewModel, setBoardViewModel] = useState(null); - const [intervalId, setIntervalId] = useState(-1); + const [boardId, setBoardId] = useState(); - useEffect(() => { - gameRef.current = game; - }); + const [pitAnimator, setPitAnimator] = useState(); const onConnectionDone = () => { setConnetionState("connected"); @@ -69,41 +68,20 @@ const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => { }); }; - const listenMessages = () => { + const listenMessages = (pitAnimator: PitAnimator) => { + context.rtmt.listenMessage("on_game_start", (message: Object) => { + const newGame: CommonMancalaGame = message as CommonMancalaGame; + const mancalaGame = MancalaGame.createFromMancalaGame(newGame); + setSearchingOpponent(false); + setGame(mancalaGame); + pitAnimator.setNewGame(mancalaGame); + }); + context.rtmt.listenMessage(channel_on_game_update, (message: Object) => { const newGame: CommonMancalaGame = message as CommonMancalaGame; const mancalaGame = MancalaGame.createFromMancalaGame(newGame); - if (gameRef.current && mancalaGame.history.length > 0) { - const lastHistoryItem = - mancalaGame.history[mancalaGame.history.length - 1]; - if (lastHistoryItem.gameSteps.length > 0) { - let stepIndex = 0; - if (intervalId) { - clearInterval(intervalId); - } - const id = setInterval(() => { - if (stepIndex === lastHistoryItem.gameSteps.length) { - clearInterval(id); - setAnimationPitIndex(-1); - setGame(mancalaGame); - } else { - const gameStep = lastHistoryItem.gameSteps[stepIndex]; - const index = mancalaGame.board.getPitIndexCircularly( - gameStep.index - ); - setAnimationPitIndex(index); - } - stepIndex++; - }, 250); - setIntervalId(intervalId); - } - } - }); - - context.rtmt.listenMessage("on_game_start", (message: Object) => { - const newGame: CommonMancalaGame = message as CommonMancalaGame; - setSearchingOpponent(false); - setGame(MancalaGame.createFromMancalaGame(newGame)); + setGame(mancalaGame); + pitAnimator.setUpdatedGame(mancalaGame); }); context.rtmt.listenMessage("on_game_crashed", (message: any) => { @@ -119,9 +97,19 @@ const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => { }); }; + const updateBoardViewModel = (boardViewModel: BoardViewModel) => { + setBoardId(boardViewModel.id); + setBoardViewModel(boardViewModel); + }; + React.useEffect(() => { - listenMessages(); + const pitAnimator = new PitAnimator(context, updateBoardViewModel); + setPitAnimator(pitAnimator); + listenMessages(pitAnimator); connectToServer("connecting"); + return () => { + pitAnimator.dispose(); + }; }, []); const resetGameState = () => { @@ -140,7 +128,12 @@ const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => { context.rtmt.sendMessage(channel_leave_game, {}); }; - const onHoleSelect = (index: number, hole: Pit) => { + const onHoleSelect = (index: number, pit: Pit) => { + //TODO : stoneCount comes from view model! + if (pit.stoneCount === 0) { + //TODO : warn user + return; + } const gameMove: GameMove = { index: index }; context.rtmt.sendMessage(channel_game_move, gameMove); }; @@ -240,12 +233,14 @@ const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => { userKeyWhoLeave={userKeyWhoLeave} searchingOpponent={searchingOpponent} /> - {game && ( + {game && boardViewModel && ( )} diff --git a/src/components/BoardView.tsx b/src/components/BoardView.tsx index 9feab8c..a64cf3f 100644 --- a/src/components/BoardView.tsx +++ b/src/components/BoardView.tsx @@ -1,30 +1,9 @@ import { Bank, MancalaGame, Pit } from "mancala.js"; import * as React from "react"; import { FunctionComponent, useState } from "react"; - -type Theme = { - background: string; - boardColor: string; - boardColorWhenPlayerTurn: string; - storeColor: string; - storeColorWhenPlayerTurn: string; - holeColor: string; - ballColor: string; - ballLightColor: string; - holeAnimateColor: string; -}; - -const theme: Theme = { - background: "#EEEEEE", - boardColor: "#4D606E", - boardColorWhenPlayerTurn: "#84b8a6", - storeColor: "#3FBAC2", - storeColorWhenPlayerTurn: "#6cab94", - holeColor: "#D3D4D8", - ballColor: "#393E46", - ballLightColor: "#393E46", - holeAnimateColor: "#afb3a4", -}; +import { Context } from "../context"; +import BoardViewModel from "../viewmodel/BoardViewModel"; +import PitViewModel from "../viewmodel/PitViewModel"; const BallView: FunctionComponent<{ color: string }> = ({ color }) => { return ( @@ -48,41 +27,19 @@ function range(size: number) { return ans; } -const PitContainer: FunctionComponent<{ - pit: Pit; - isAnimating: boolean; - onClick: () => void; -}> = ({ pit, isAnimating, onClick }) => { - if (isAnimating) { - pit.stoneCount += 1; - } - return ( - <> - - - ); -}; - const HoleView: FunctionComponent<{ - hole: Pit; - color: string; - stoneColor: string; + pitViewModel: PitViewModel; onClick: () => void; -}> = ({ hole, color, stoneColor, onClick }) => { - const balls = [...range(hole.stoneCount)].map((i) => ( - +}> = ({ pitViewModel, onClick }) => { + const balls = [...range(pitViewModel.stoneCount)].map((i) => ( + )); return (
= ({ store, color, stoneColor, gridColumn, gridRow }) => { - const balls = [...range(store.stoneCount)].map((i) => ( - +}> = ({ pitViewModel, gridColumn, gridRow }) => { + const balls = [...range(pitViewModel.stoneCount)].map((i) => ( + )); return (
void; - animationPitIndex: number; -}> = ({ game, userKey, onHoleSelect, animationPitIndex }) => { +}> = ({ game, context, boardId, boardViewModel, userKey, onHoleSelect }) => { + const createPitView = (pitViewModel: PitViewModel, onClick: () => void) => { + return ; + }; const player1Pits = game?.board.player1Pits.map((pit) => { - const isAnimating = pit.index === animationPitIndex; - return ( - { - if (game.turnPlayerId === game.player1Id) - onHoleSelect(game.board.player1Pits.indexOf(pit), pit); - }} - /> - ); + const pitViewModel = boardViewModel.pits[pit.index]; + return createPitView(pitViewModel, () => { + if (game.turnPlayerId === game.player1Id) + onHoleSelect(game.board.player1Pits.indexOf(pit), pit); + }); }); - const player2Pits = game!!.board.player2Pits.map((pit) => { - const isAnimating = pit.index === animationPitIndex; - return ( - { - if (game.turnPlayerId === game.player2Id) - onHoleSelect(game.board.player2Pits.indexOf(pit), pit); - }} - /> - ); + const pitViewModel = boardViewModel.pits[pit.index]; + return createPitView(pitViewModel, () => { + if (game.turnPlayerId === game.player2Id) + onHoleSelect(game.board.player2Pits.indexOf(pit), pit); + }); }); - const isUserTurn = game.checkIsPlayerTurn(userKey); - - const animatingPlayer1Bank = - game.board.player1Bank.index === animationPitIndex; - const animatingPlayer2Bank = - game.board.player2Bank.index === animationPitIndex; - - const storeColorPlayer1 = animatingPlayer1Bank - ? theme.holeAnimateColor - : isUserTurn - ? theme.storeColor - : theme.storeColorWhenPlayerTurn; - - const storeStoneColorPlayer1 = animatingPlayer1Bank - ? theme.ballLightColor - : theme.ballColor; - - const storeColorPlayer2 = animatingPlayer2Bank - ? theme.holeAnimateColor - : isUserTurn - ? theme.storeColor - : theme.storeColorWhenPlayerTurn; - - const storeStoneColorPlayer2 = animatingPlayer2Bank - ? theme.ballLightColor - : theme.ballColor; + const theme = context.themeManager.theme; + const player1BankViewModel = + boardViewModel.pits[game.board.player1BankIndex()]; + const player2BankViewModel = + boardViewModel.pits[game.board.player2BankIndex()]; + const player1Bank = ( + + ); + const player2Bank = ( + + ); return (
@@ -225,17 +161,13 @@ const BoardView: FunctionComponent<{ ) : ( <> {player2Pits.reverse()}