add PitAnimator
This commit is contained in:
parent
aede204b40
commit
a8287cf800
194
src/animation/PitAnimator.ts
Normal file
194
src/animation/PitAnimator.ts
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
import {
|
||||||
|
MancalaGame,
|
||||||
|
GameStep,
|
||||||
|
HistoryItem,
|
||||||
|
GAME_STEP_GAME_MOVE,
|
||||||
|
GAME_STEP_LAST_STONE_IN_EMPTY_PIT,
|
||||||
|
GAME_STEP_BOARD_CLEARED,
|
||||||
|
GAME_STEP_LAST_STONE_IN_BANK,
|
||||||
|
} from "mancala.js";
|
||||||
|
import { v4 } from "uuid";
|
||||||
|
import { Context } from "../context";
|
||||||
|
import BoardViewModelFactory from "../factory/BoardViewModelFactory";
|
||||||
|
import { PitViewModelFactory } from "../factory/PitViewModelFactory";
|
||||||
|
import BoardViewModel from "../viewmodel/BoardViewModel";
|
||||||
|
|
||||||
|
const animationUpdateInterval = 300;
|
||||||
|
|
||||||
|
export default class PitAnimator {
|
||||||
|
context: Context;
|
||||||
|
game: MancalaGame;
|
||||||
|
oldGame: MancalaGame;
|
||||||
|
currentIntervalID: number;
|
||||||
|
onBoardViewModelUpdate: (boardViewModel: BoardViewModel) => void;
|
||||||
|
boardViewModel: BoardViewModel;
|
||||||
|
oldBoardViewModel: BoardViewModel;
|
||||||
|
animationIndex: number = 0;
|
||||||
|
currentHistoryItem: HistoryItem;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
context: Context,
|
||||||
|
onBoardViewModelUpdate: (boardViewModel: BoardViewModel) => void
|
||||||
|
) {
|
||||||
|
this.context = context;
|
||||||
|
this.onBoardViewModelUpdate = onBoardViewModelUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setNewGame(game: MancalaGame) {
|
||||||
|
this.reset();
|
||||||
|
this.game = game;
|
||||||
|
this.updateBoardViewModel(this.getBoardViewModelFromGame(this.game));
|
||||||
|
}
|
||||||
|
|
||||||
|
public setUpdatedGame(game: MancalaGame, forceClear = false) {
|
||||||
|
this.resetAnimationState();
|
||||||
|
if (!this.game) {
|
||||||
|
this.setNewGame(game);
|
||||||
|
} else {
|
||||||
|
this.oldGame = this.game;
|
||||||
|
this.game = game;
|
||||||
|
this.onGameMoveAnimationStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onGameMoveAnimationStart() {
|
||||||
|
this.stopCurrentAnimation();
|
||||||
|
if (this.game.history.length > 0) {
|
||||||
|
const lastHistoryItem = this.game.history[this.game.history.length - 1];
|
||||||
|
if (lastHistoryItem.gameSteps.length > 0) {
|
||||||
|
this.animationIndex = 0;
|
||||||
|
this.currentHistoryItem = lastHistoryItem;
|
||||||
|
this.boardViewModel = this.getBoardViewModelFromGame(this.game);
|
||||||
|
this.oldBoardViewModel = this.getBoardViewModelFromGame(this.oldGame);
|
||||||
|
this.startAnimationUpdateCyle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAnimate() {
|
||||||
|
if (this.animationIndex === this.currentHistoryItem.gameSteps.length) {
|
||||||
|
this.clearCurrentInterval();
|
||||||
|
this.updateBoardViewModel(this.getBoardViewModelFromGame(this.game));
|
||||||
|
} else {
|
||||||
|
const gameStep = this.currentHistoryItem.gameSteps[this.animationIndex];
|
||||||
|
const index = this.game.board.getPitIndexCircularly(gameStep.index);
|
||||||
|
this.animatePit(index, this.oldBoardViewModel, gameStep);
|
||||||
|
this.updateBoardViewModel(this.oldBoardViewModel);
|
||||||
|
}
|
||||||
|
this.animationIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
getGameMoveStepCount(historyItem: HistoryItem) {
|
||||||
|
return historyItem.gameSteps.filter(
|
||||||
|
(gameStep) => gameStep.type === GAME_STEP_GAME_MOVE
|
||||||
|
).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
animatePit(
|
||||||
|
index: number,
|
||||||
|
boardViewModel: BoardViewModel,
|
||||||
|
gameStep: GameStep
|
||||||
|
) {
|
||||||
|
const pitViewModel = boardViewModel.pits[index];
|
||||||
|
if (this.animationIndex === 0) {
|
||||||
|
//This one stone move case, TODO : beautify it later
|
||||||
|
if (this.getGameMoveStepCount(this.currentHistoryItem) === 1) {
|
||||||
|
const previousPitIndex = gameStep.index - 1;
|
||||||
|
if (previousPitIndex > 0) {
|
||||||
|
boardViewModel.pits[previousPitIndex].stoneCount = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pitViewModel.stoneCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const theme = this.context.themeManager.theme;
|
||||||
|
if (gameStep.type === GAME_STEP_GAME_MOVE) {
|
||||||
|
pitViewModel.stoneCount += 1;
|
||||||
|
pitViewModel.pitColor = theme.pitGameMoveAnimateColor;
|
||||||
|
} else if (gameStep.type === GAME_STEP_LAST_STONE_IN_EMPTY_PIT) {
|
||||||
|
pitViewModel.pitColor = theme.pitGetRivalStonePitAnimateColor;
|
||||||
|
pitViewModel.stoneCount = 0;
|
||||||
|
const oppositeIndex = this.game.board.getPitIndexCircularly(
|
||||||
|
gameStep.data.oppositeIndex
|
||||||
|
);
|
||||||
|
const oppositePitViewModel = boardViewModel.pits[oppositeIndex];
|
||||||
|
oppositePitViewModel.pitColor = theme.pitGetRivalStonePitAnimateColor;
|
||||||
|
oppositePitViewModel.stoneCount = 0;
|
||||||
|
} else if (gameStep.type === GAME_STEP_LAST_STONE_IN_BANK) {
|
||||||
|
pitViewModel.pitColor = theme.pitLastStoneInBankPitAnimateColor;
|
||||||
|
} else if (gameStep.type === GAME_STEP_BOARD_CLEARED) {
|
||||||
|
for (const index of gameStep.data.pitIndexesThatHasStone) {
|
||||||
|
const oppositeIndex = this.game.board.getPitIndexCircularly(index);
|
||||||
|
const oppositePitViewModel = boardViewModel.pits[oppositeIndex];
|
||||||
|
oppositePitViewModel.pitColor = theme.pitGetRivalStonePitAnimateColor;
|
||||||
|
oppositePitViewModel.stoneCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startAnimationUpdateCyle() {
|
||||||
|
this.clearCurrentInterval();
|
||||||
|
this.currentIntervalID = setInterval(
|
||||||
|
() => this.onAnimate(),
|
||||||
|
animationUpdateInterval
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopCurrentAnimation() {
|
||||||
|
this.clearCurrentInterval();
|
||||||
|
if (this.oldGame) {
|
||||||
|
this.updateBoardViewModel(this.getBoardViewModelFromGame(this.oldGame));
|
||||||
|
}
|
||||||
|
this.resetAnimationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCurrentInterval() {
|
||||||
|
if (this.currentIntervalID) {
|
||||||
|
clearInterval(this.currentIntervalID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBoardViewModel(boardViewModel: BoardViewModel) {
|
||||||
|
boardViewModel.id = v4();
|
||||||
|
this.onBoardViewModelUpdate?.(boardViewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getBoardViewModelFromGame(game: MancalaGame): BoardViewModel {
|
||||||
|
const pitViewModels = this.createPitViewModelsFromGame(game);
|
||||||
|
return BoardViewModelFactory.create(v4(), pitViewModels);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createPitViewModelsFromGame(game: MancalaGame) {
|
||||||
|
return game.board.pits.map((pit) => {
|
||||||
|
const theme = this.context.themeManager.theme;
|
||||||
|
const stoneCount = pit.stoneCount;
|
||||||
|
const stoneColor = theme.ballColor;
|
||||||
|
const pitColor = theme.holeColor;
|
||||||
|
const id = pit.index.toString();
|
||||||
|
return PitViewModelFactory.create({
|
||||||
|
id,
|
||||||
|
stoneCount,
|
||||||
|
stoneColor,
|
||||||
|
pitColor,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public resetAnimationState() {
|
||||||
|
this.animationIndex = -1;
|
||||||
|
this.currentHistoryItem = null;
|
||||||
|
this.boardViewModel = null;
|
||||||
|
this.oldBoardViewModel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset() {
|
||||||
|
this.resetAnimationState();
|
||||||
|
this.game = null;
|
||||||
|
this.oldGame = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
this.resetAnimationState();
|
||||||
|
this.clearCurrentInterval();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user