mancala/src/animation/PitAnimator.ts

204 lines
6.4 KiB
TypeScript
Raw Normal View History

2022-05-15 02:00:13 +03:00
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,
2022-05-21 18:59:30 +03:00
GAME_STEP_DOUBLE_STONE_IN_PIT,
2022-05-15 02:00:13 +03:00
} from "mancala.js";
import { v4 } from "uuid";
import { Context } from "../context";
import BoardViewModelFactory from "../factory/BoardViewModelFactory";
import { PitViewModelFactory } from "../factory/PitViewModelFactory";
import { getColorByBrightness } from "../util/ColorUtil";
2022-05-15 02:00:13 +03:00
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;
2022-05-22 17:57:33 +03:00
this.onBoardViewModelUpdate?.(this.getBoardViewModelFromGame(this.game));
2022-05-15 02:00:13 +03:00
}
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();
2022-05-22 17:57:33 +03:00
this.onBoardViewModelUpdate?.(this.getBoardViewModelFromGame(this.game));
2022-05-15 02:00:13 +03:00
} else {
const gameStep = this.currentHistoryItem.gameSteps[this.animationIndex];
const index = this.game.board.getPitIndexCircularly(gameStep.index);
this.animatePit(index, this.oldBoardViewModel, gameStep);
2022-05-22 17:57:33 +03:00
this.onBoardViewModelUpdate?.(this.oldBoardViewModel);
2022-05-15 02:00:13 +03:00
}
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;
}
2022-05-21 18:59:30 +03:00
} else if (gameStep.type === GAME_STEP_DOUBLE_STONE_IN_PIT) {
const _index = this.game.board.getPitIndexCircularly(index);
const pitViewModel = boardViewModel.pits[_index];
pitViewModel.pitColor = theme.pitGetRivalStonePitAnimateColor;
pitViewModel.stoneCount = 0;
2022-05-15 02:00:13 +03:00
}
pitViewModel.stoneColor = getColorByBrightness(
2022-06-04 17:16:32 +03:00
pitViewModel.pitColor,
theme.stoneColor,
theme.stoneLightColor
);
2022-05-15 02:00:13 +03:00
}
startAnimationUpdateCyle() {
this.clearCurrentInterval();
this.currentIntervalID = setInterval(
() => this.onAnimate(),
animationUpdateInterval
);
}
stopCurrentAnimation() {
this.clearCurrentInterval();
if (this.oldGame) {
2022-05-22 17:57:33 +03:00
this.onBoardViewModelUpdate?.(
this.getBoardViewModelFromGame(this.oldGame)
);
2022-05-15 02:00:13 +03:00
}
this.resetAnimationState();
}
clearCurrentInterval() {
if (this.currentIntervalID) {
clearInterval(this.currentIntervalID);
}
}
2022-06-04 20:54:19 +03:00
public getBoardViewModelFromGame(game: MancalaGame): BoardViewModel {
2022-05-15 02:00:13 +03:00
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;
2022-06-04 17:16:32 +03:00
const stoneColor = theme.stoneColor;
2022-05-15 02:00:13 +03:00
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();
}
}