-
Notifications
You must be signed in to change notification settings - Fork 9
/
processBattle.ts
118 lines (96 loc) · 4.02 KB
/
processBattle.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import delay from "delay";
import { take, call, put, select, takeEvery, takeLatest, delay as delayEffect } from "@redux-saga/core/effects";
import { eventChannel } from "redux-saga";
import { BoardActions } from "@common/board";
import { Models, GamePhase, Constants } from "@common";
import { isATeamDefeated } from "@common/is-a-team-defeated";
import { AppState } from "../../store/store";
import { GAME_PHASE_UPDATE } from "../../actiontypes/gameActionTypes";
import { GamePhaseUpdateAction } from "../../actions/gameActions";
import { log } from "../../log";
import { TurnSimulator } from "@common/match/combat/turnSimulator";
import { DefinitionProvider } from "@common/game/definitionProvider";
enum BattleActionType {
TURN,
FINISH
}
const BATTLE_TURN = "BATTLE_TURN";
type BATTLE_TURN = typeof BATTLE_TURN;
export const BATTLE_FINISHED = "BATTLE_FINISHED";
export type BATTLE_FINISHED = typeof BATTLE_FINISHED;
type BattleTurnAction = ({ type: BATTLE_TURN, payload: { pieces: Models.Piece[] } });
export type BattleFinishAction = ({ type: BATTLE_FINISHED });
type BattleAction = BattleTurnAction | BattleFinishAction;
const turnAction = (pieces: Models.Piece[]): BattleTurnAction => ({
type: BATTLE_TURN,
payload: {
pieces
}
});
const finishAction = (): BattleFinishAction => ({ type: BATTLE_FINISHED });
const startBattle = (turnSimulator: TurnSimulator, startPieces: Models.Piece[], maxTurns: number) => {
return eventChannel<BattleAction>(emit => {
let shouldStop = false;
let pieces = startPieces;
const run = async () => {
let turnCount = 0;
while (true) {
await delay(Constants.TURN_DURATION_MS);
const defeated = isATeamDefeated(pieces);
if (shouldStop) {
log(`Fight ended at turn ${turnCount} due to cancellation`);
emit(finishAction());
break;
}
if (defeated) {
log(`Fight ended at turn ${turnCount}`);
emit(finishAction());
break;
}
if (turnCount >= maxTurns) {
log(`Fight timed out at turn ${turnCount}`);
emit(finishAction());
break;
}
pieces = turnSimulator.simulateTurn(pieces);
emit(turnAction(pieces));
turnCount++;
}
};
run();
return () => {
shouldStop = true;
};
});
};
const isGamePhaseUpdate = (phase: GamePhase, action: GamePhaseUpdateAction) =>
action.type === GAME_PHASE_UPDATE && action.payload.phase === phase;
const turnSimulator = new TurnSimulator(new DefinitionProvider());
export const processBattle = function*() {
yield takeLatest<GamePhaseUpdateAction>(
action =>
isGamePhaseUpdate(GamePhase.PLAYING, action)
|| isGamePhaseUpdate(GamePhase.PREPARING, action)
|| isGamePhaseUpdate(GamePhase.READY, action),
function*(action) {
if (isGamePhaseUpdate(GamePhase.PREPARING, action) || isGamePhaseUpdate(GamePhase.READY, action)) {
// don't do anything, just cancel the old one
const pieces = (action.payload as any).payload.pieces;
yield put(BoardActions.piecesUpdated(pieces));
return;
}
const state: AppState = yield select();
const battleChannel = yield call(startBattle, turnSimulator, state.board, Constants.TURNS_IN_BATTLE);
yield takeEvery(battleChannel, function*(battleAction: BattleAction) {
switch (battleAction.type) {
case BATTLE_TURN:
yield put(BoardActions.piecesUpdated(battleAction.payload.pieces));
case BATTLE_FINISHED:
yield put(battleAction);
default:
return;
}
});
}
);
};