2017-02-09 00:00:35 +00:00
|
|
|
module TS.SpaceTac.UI {
|
2017-05-29 23:15:32 +00:00
|
|
|
/**
|
|
|
|
* Processor of events coming from the battle log
|
|
|
|
*
|
|
|
|
* This will bind to the battle log to receive new events, and update
|
|
|
|
* the battle view accordingly.
|
|
|
|
*
|
|
|
|
* It is also possible to go back/forward in time.
|
|
|
|
*/
|
2014-12-31 00:00:00 +00:00
|
|
|
export class LogProcessor {
|
|
|
|
// Link to the battle view
|
2017-05-30 16:24:55 +00:00
|
|
|
private view: BattleView
|
2014-12-31 00:00:00 +00:00
|
|
|
|
|
|
|
// Link to the battle
|
2017-05-30 16:24:55 +00:00
|
|
|
private battle: Battle
|
2014-12-31 00:00:00 +00:00
|
|
|
|
|
|
|
// Link to the battle log
|
2017-05-30 16:24:55 +00:00
|
|
|
private log: BattleLog
|
2014-12-31 00:00:00 +00:00
|
|
|
|
2017-02-19 21:33:07 +00:00
|
|
|
// Forward events to other subscribers
|
2017-06-13 20:48:43 +00:00
|
|
|
private forwarding: ((event: BaseBattleEvent) => number)[] = []
|
2017-05-30 16:24:55 +00:00
|
|
|
|
|
|
|
// Current position in the battle log
|
2017-06-13 20:48:43 +00:00
|
|
|
private cursor = -1
|
|
|
|
|
|
|
|
// Indicator that the log is being played continuously
|
|
|
|
private playing = false
|
2017-02-19 21:33:07 +00:00
|
|
|
|
2017-08-23 17:59:22 +00:00
|
|
|
// Time at which the last action was applied
|
|
|
|
private last_action: number
|
|
|
|
|
2014-12-31 00:00:00 +00:00
|
|
|
constructor(view: BattleView) {
|
|
|
|
this.view = view;
|
|
|
|
this.battle = view.battle;
|
|
|
|
this.log = view.battle.log;
|
2017-05-29 23:15:32 +00:00
|
|
|
|
2017-05-30 16:24:55 +00:00
|
|
|
view.inputs.bindCheat("PageUp", "Step backward", () => {
|
2017-05-29 23:15:32 +00:00
|
|
|
this.stepBackward();
|
|
|
|
});
|
|
|
|
view.inputs.bindCheat("PageDown", "Step forward", () => {
|
|
|
|
this.stepForward();
|
|
|
|
});
|
|
|
|
view.inputs.bindCheat("Home", "Jump to beginning", () => {
|
|
|
|
this.jumpToStart();
|
|
|
|
});
|
|
|
|
view.inputs.bindCheat("End", "Jump to end", () => {
|
|
|
|
this.jumpToEnd();
|
2017-05-30 16:24:55 +00:00
|
|
|
});
|
2017-02-21 22:38:31 +00:00
|
|
|
}
|
2014-12-31 00:00:00 +00:00
|
|
|
|
2017-02-21 22:38:31 +00:00
|
|
|
/**
|
|
|
|
* Start log processing
|
|
|
|
*/
|
|
|
|
start() {
|
2017-05-30 18:23:35 +00:00
|
|
|
this.cursor = this.log.events.length - 1;
|
2017-05-29 23:15:32 +00:00
|
|
|
this.battle.getBootstrapEvents().forEach(event => this.processBattleEvent(event));
|
2017-06-13 20:48:43 +00:00
|
|
|
|
|
|
|
this.playing = true;
|
|
|
|
if (!this.view.gameui.headless) {
|
|
|
|
this.playContinuous();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Play the log continuously
|
|
|
|
*/
|
|
|
|
playContinuous() {
|
|
|
|
let delay = 0;
|
|
|
|
|
|
|
|
if (this.playing && !this.view.game.paused) {
|
|
|
|
delay = this.stepForward();
|
|
|
|
if (delay == 0) {
|
|
|
|
delay = this.transferControl();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.view.timer.schedule(Math.max(delay, 50), () => this.playContinuous());
|
2017-02-19 21:33:07 +00:00
|
|
|
}
|
|
|
|
|
2017-05-30 16:24:55 +00:00
|
|
|
/**
|
|
|
|
* Make a step backward in time
|
|
|
|
*/
|
|
|
|
stepBackward() {
|
2017-05-30 18:23:35 +00:00
|
|
|
if (!this.atStart()) {
|
2017-05-30 16:24:55 +00:00
|
|
|
this.cursor -= 1;
|
2017-06-13 20:48:43 +00:00
|
|
|
this.playing = false;
|
2017-05-30 18:23:35 +00:00
|
|
|
this.processBattleEvent(this.log.events[this.cursor + 1].getReverse());
|
2017-05-30 16:24:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make a step forward in time
|
2017-06-13 20:48:43 +00:00
|
|
|
*
|
|
|
|
* Returns the duration of the step processing
|
2017-05-30 16:24:55 +00:00
|
|
|
*/
|
2017-06-13 20:48:43 +00:00
|
|
|
stepForward(): number {
|
2017-05-30 18:23:35 +00:00
|
|
|
if (!this.atEnd()) {
|
2017-05-30 16:24:55 +00:00
|
|
|
this.cursor += 1;
|
2017-06-13 20:48:43 +00:00
|
|
|
let result = this.processBattleEvent(this.log.events[this.cursor]);
|
|
|
|
|
|
|
|
if (this.atEnd()) {
|
|
|
|
this.playing = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return 0;
|
2017-05-30 16:24:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Jump to the start of the log
|
|
|
|
*
|
|
|
|
* This will rewind all applied event
|
|
|
|
*/
|
|
|
|
jumpToStart() {
|
2017-05-30 18:23:35 +00:00
|
|
|
while (!this.atStart()) {
|
|
|
|
this.stepBackward();
|
|
|
|
}
|
2017-05-30 16:24:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Jump to the end of the log
|
|
|
|
*
|
|
|
|
* This will apply all remaining event
|
|
|
|
*/
|
|
|
|
jumpToEnd() {
|
2017-05-30 18:23:35 +00:00
|
|
|
while (!this.atEnd()) {
|
|
|
|
this.stepForward();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if we are currently at the start of the log
|
|
|
|
*/
|
|
|
|
atStart(): boolean {
|
|
|
|
return this.cursor < 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if we are currently at the end of the log
|
|
|
|
*/
|
|
|
|
atEnd(): boolean {
|
|
|
|
return this.cursor >= this.log.events.length - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if we need a player or AI to interact at this point
|
|
|
|
*/
|
|
|
|
getPlayerNeeded(): Player | null {
|
|
|
|
if (this.atEnd()) {
|
|
|
|
return this.battle.playing_ship ? this.battle.playing_ship.getPlayer() : null;
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
2017-05-30 16:24:55 +00:00
|
|
|
}
|
|
|
|
|
2017-02-19 21:33:07 +00:00
|
|
|
/**
|
|
|
|
* Register a sub-subscriber.
|
|
|
|
*
|
|
|
|
* The difference with registering directly to the BattleLog is that events may be delayed
|
|
|
|
* for animations.
|
2017-05-22 18:06:33 +00:00
|
|
|
*
|
|
|
|
* The callback may return the duration it needs to display the change.
|
2017-02-19 21:33:07 +00:00
|
|
|
*/
|
2017-05-29 23:15:32 +00:00
|
|
|
register(callback: (event: BaseBattleEvent) => number) {
|
2017-06-13 20:48:43 +00:00
|
|
|
this.forwarding.push(callback);
|
2014-12-31 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
2017-05-17 18:14:45 +00:00
|
|
|
/**
|
|
|
|
* Register a sub-subscriber, to receive events for a specific ship
|
|
|
|
*/
|
2017-05-22 18:06:33 +00:00
|
|
|
registerForShip(ship: Ship, callback: (event: BaseLogShipEvent) => number) {
|
2017-05-17 18:14:45 +00:00
|
|
|
this.register(event => {
|
|
|
|
if (event instanceof BaseLogShipEvent && event.ship === ship) {
|
2017-05-22 18:06:33 +00:00
|
|
|
return callback(event);
|
|
|
|
} else {
|
|
|
|
return 0;
|
2017-05-17 18:14:45 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-02-14 00:30:50 +00:00
|
|
|
/**
|
|
|
|
* Process a single event
|
|
|
|
*/
|
2017-06-13 20:48:43 +00:00
|
|
|
processBattleEvent(event: BaseBattleEvent): number {
|
2014-12-31 00:00:00 +00:00
|
|
|
console.log("Battle event", event);
|
2015-01-02 00:00:00 +00:00
|
|
|
|
2017-06-13 20:48:43 +00:00
|
|
|
let durations = this.forwarding.map(subscriber => subscriber(event));
|
2017-08-23 17:59:22 +00:00
|
|
|
let t = (new Date()).getTime()
|
|
|
|
|
|
|
|
if (event instanceof ActionAppliedEvent) {
|
|
|
|
if (event.ship.getPlayer() != this.view.player) {
|
|
|
|
// AI is playing, do not make it play too fast
|
|
|
|
let since_last = t - this.last_action;
|
|
|
|
if (since_last < 2000) {
|
|
|
|
durations.push(2000 - since_last);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.last_action = t;
|
|
|
|
} else if (event instanceof ShipChangeEvent) {
|
2017-06-13 20:48:43 +00:00
|
|
|
durations.push(this.processShipChangeEvent(event));
|
2017-02-09 00:00:35 +00:00
|
|
|
} else if (event instanceof DeathEvent) {
|
2017-06-13 20:48:43 +00:00
|
|
|
durations.push(this.processDeathEvent(event));
|
2017-02-09 00:00:35 +00:00
|
|
|
} else if (event instanceof FireEvent) {
|
2017-06-13 20:48:43 +00:00
|
|
|
durations.push(this.processFireEvent(event));
|
2017-02-09 00:00:35 +00:00
|
|
|
} else if (event instanceof DamageEvent) {
|
2017-06-13 20:48:43 +00:00
|
|
|
durations.push(this.processDamageEvent(event));
|
2017-02-09 00:00:35 +00:00
|
|
|
} else if (event instanceof EndBattleEvent) {
|
2017-06-13 20:48:43 +00:00
|
|
|
durations.push(this.processEndBattleEvent(event));
|
2017-02-09 00:00:35 +00:00
|
|
|
} else if (event instanceof DroneDeployedEvent) {
|
2017-06-13 20:48:43 +00:00
|
|
|
durations.push(this.processDroneDeployedEvent(event));
|
2017-02-09 00:00:35 +00:00
|
|
|
} else if (event instanceof DroneDestroyedEvent) {
|
2017-06-13 20:48:43 +00:00
|
|
|
durations.push(this.processDroneDestroyedEvent(event));
|
2017-02-15 21:15:31 +00:00
|
|
|
} else if (event instanceof DroneAppliedEvent) {
|
2017-06-13 20:48:43 +00:00
|
|
|
durations.push(this.processDroneAppliedEvent(event));
|
2014-12-31 00:00:00 +00:00
|
|
|
}
|
2017-05-30 18:23:35 +00:00
|
|
|
|
2017-06-13 20:48:43 +00:00
|
|
|
return max([0].concat(durations));
|
|
|
|
}
|
2017-05-30 18:23:35 +00:00
|
|
|
|
2017-06-13 20:48:43 +00:00
|
|
|
/**
|
|
|
|
* Transfer control to the needed player (or not)
|
|
|
|
*/
|
|
|
|
private transferControl(): number {
|
2017-05-30 18:23:35 +00:00
|
|
|
let player = this.getPlayerNeeded();
|
|
|
|
if (player) {
|
|
|
|
if (this.battle.playing_ship && !this.battle.playing_ship.alive) {
|
|
|
|
this.view.setInteractionEnabled(false);
|
|
|
|
this.battle.advanceToNextShip();
|
2017-06-13 20:48:43 +00:00
|
|
|
return 200;
|
2017-05-30 18:23:35 +00:00
|
|
|
} else if (player === this.view.player) {
|
|
|
|
this.view.setInteractionEnabled(true);
|
2017-06-13 20:48:43 +00:00
|
|
|
return 0;
|
2017-05-30 18:23:35 +00:00
|
|
|
} else {
|
|
|
|
this.view.playAI();
|
2017-06-13 20:48:43 +00:00
|
|
|
return 0;
|
2017-05-30 18:23:35 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.view.setInteractionEnabled(false);
|
2017-06-13 20:48:43 +00:00
|
|
|
return 0;
|
2017-05-30 18:23:35 +00:00
|
|
|
}
|
2014-12-31 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
2015-02-03 00:00:00 +00:00
|
|
|
// Playing ship changed
|
2017-06-13 20:48:43 +00:00
|
|
|
private processShipChangeEvent(event: ShipChangeEvent): number {
|
2017-03-15 00:24:56 +00:00
|
|
|
this.view.arena.setShipPlaying(event.new_ship);
|
|
|
|
this.view.ship_list.setPlaying(event.new_ship);
|
2017-06-07 17:08:53 +00:00
|
|
|
if (event.ship !== event.new_ship) {
|
2017-07-19 23:22:18 +00:00
|
|
|
this.view.audio.playOnce("battle-ship-change");
|
2017-06-07 17:08:53 +00:00
|
|
|
}
|
2017-06-13 20:48:43 +00:00
|
|
|
return 0;
|
2015-02-03 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Damage to ship
|
2017-06-13 20:48:43 +00:00
|
|
|
private processDamageEvent(event: DamageEvent): number {
|
2017-01-15 22:05:00 +00:00
|
|
|
var item = this.view.ship_list.findItem(event.ship);
|
|
|
|
if (item) {
|
|
|
|
item.setDamageHit();
|
|
|
|
}
|
2017-06-13 20:48:43 +00:00
|
|
|
return 0;
|
2015-02-03 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 00:00:00 +00:00
|
|
|
// A ship died
|
2017-06-13 20:48:43 +00:00
|
|
|
private processDeathEvent(event: DeathEvent): number {
|
2015-02-23 00:00:00 +00:00
|
|
|
if (this.view.ship_hovered === event.ship) {
|
|
|
|
this.view.setShipHovered(null);
|
|
|
|
}
|
2017-01-12 00:37:05 +00:00
|
|
|
this.view.arena.markAsDead(event.ship);
|
|
|
|
this.view.ship_list.markAsDead(event.ship);
|
2017-02-15 22:34:27 +00:00
|
|
|
|
2017-06-13 20:48:43 +00:00
|
|
|
return event.initial ? 0 : 1000;
|
2015-02-18 00:00:00 +00:00
|
|
|
}
|
2015-02-20 00:00:00 +00:00
|
|
|
|
|
|
|
// Weapon used
|
2017-06-13 20:48:43 +00:00
|
|
|
private processFireEvent(event: FireEvent): number {
|
2017-06-22 22:37:38 +00:00
|
|
|
var effect = new WeaponEffect(this.view.arena, event.ship, event.target, event.weapon);
|
2017-02-14 00:30:50 +00:00
|
|
|
let duration = effect.start();
|
|
|
|
|
2017-06-13 20:48:43 +00:00
|
|
|
return duration;
|
2015-02-20 00:00:00 +00:00
|
|
|
}
|
2015-04-21 22:17:00 +00:00
|
|
|
|
|
|
|
// Battle ended (victory or defeat)
|
2017-06-13 20:48:43 +00:00
|
|
|
private processEndBattleEvent(event: EndBattleEvent): number {
|
2017-03-12 23:32:41 +00:00
|
|
|
this.view.endBattle();
|
2017-06-13 20:48:43 +00:00
|
|
|
return 0;
|
2015-04-21 22:17:00 +00:00
|
|
|
}
|
2015-04-22 20:03:59 +00:00
|
|
|
|
2017-02-08 18:54:02 +00:00
|
|
|
// New drone deployed
|
2017-06-13 20:48:43 +00:00
|
|
|
private processDroneDeployedEvent(event: DroneDeployedEvent): number {
|
2017-02-15 22:34:27 +00:00
|
|
|
let duration = this.view.arena.addDrone(event.drone, !event.initial);
|
2017-05-17 16:21:14 +00:00
|
|
|
|
|
|
|
if (duration) {
|
|
|
|
this.view.gameui.audio.playOnce("battle-drone-deploy");
|
|
|
|
}
|
|
|
|
|
2017-06-13 20:48:43 +00:00
|
|
|
return duration;
|
2017-02-08 18:54:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Drone destroyed
|
2017-06-13 20:48:43 +00:00
|
|
|
private processDroneDestroyedEvent(event: DroneDestroyedEvent): number {
|
2017-02-08 18:54:02 +00:00
|
|
|
this.view.arena.removeDrone(event.drone);
|
2017-05-17 16:21:14 +00:00
|
|
|
if (!event.initial) {
|
|
|
|
this.view.gameui.audio.playOnce("battle-drone-destroy");
|
2017-06-13 20:48:43 +00:00
|
|
|
return 1000;
|
|
|
|
} else {
|
|
|
|
return 0;
|
2017-05-17 16:21:14 +00:00
|
|
|
}
|
2017-02-08 18:54:02 +00:00
|
|
|
}
|
2017-02-15 21:15:31 +00:00
|
|
|
|
|
|
|
// Drone applied
|
2017-06-13 20:48:43 +00:00
|
|
|
private processDroneAppliedEvent(event: DroneAppliedEvent): number {
|
2017-02-15 21:15:31 +00:00
|
|
|
let drone = this.view.arena.findDrone(event.drone);
|
|
|
|
if (drone) {
|
|
|
|
let duration = drone.setApplied();
|
2017-05-17 16:21:14 +00:00
|
|
|
|
|
|
|
if (duration) {
|
|
|
|
this.view.gameui.audio.playOnce("battle-drone-activate");
|
|
|
|
}
|
|
|
|
|
2017-06-13 20:48:43 +00:00
|
|
|
return duration;
|
|
|
|
} else {
|
|
|
|
return 0;
|
2017-02-15 21:15:31 +00:00
|
|
|
}
|
|
|
|
}
|
2014-12-31 00:00:00 +00:00
|
|
|
}
|
2015-01-07 00:00:00 +00:00
|
|
|
}
|