battle: Fixed timing issues in log processing
This commit is contained in:
parent
f62f2236f8
commit
07d0eb7a9d
1
TODO
1
TODO
|
@ -26,7 +26,6 @@
|
||||||
* Fix capacity limit effect not refreshing associated value (for example, on "limit power capacity to 3", potential "power" value change is not broadcast)
|
* Fix capacity limit effect not refreshing associated value (for example, on "limit power capacity to 3", potential "power" value change is not broadcast)
|
||||||
* Actions: show power usage/recovery in power bar, on action hover
|
* Actions: show power usage/recovery in power bar, on action hover
|
||||||
* Actions: fix targetting not resetting when using keyboard shortcuts
|
* Actions: fix targetting not resetting when using keyboard shortcuts
|
||||||
* Suspend AI operation when the game is paused (window not focused)
|
|
||||||
* Add actions with cost dependent of distance (like current move actions)
|
* Add actions with cost dependent of distance (like current move actions)
|
||||||
* Find incentives to move from starting position
|
* Find incentives to move from starting position
|
||||||
* Outcome: disable the loot button if there is no loot
|
* Outcome: disable the loot button if there is no loot
|
||||||
|
|
|
@ -127,6 +127,7 @@ module TS.SpaceTac.UI.Specs {
|
||||||
|
|
||||||
// used points
|
// used points
|
||||||
ship.setValue("power", 6);
|
ship.setValue("power", 6);
|
||||||
|
testgame.battleview.log_processor.jumpToEnd();
|
||||||
check(6, 0, 2);
|
check(6, 0, 2);
|
||||||
|
|
||||||
// using points
|
// using points
|
||||||
|
@ -135,6 +136,7 @@ module TS.SpaceTac.UI.Specs {
|
||||||
|
|
||||||
// decrease
|
// decrease
|
||||||
ship.setAttribute("power_capacity", 3);
|
ship.setAttribute("power_capacity", 3);
|
||||||
|
testgame.battleview.log_processor.jumpToEnd();
|
||||||
check(3);
|
check(3);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -30,12 +30,15 @@ module TS.SpaceTac.UI.Specs {
|
||||||
expect(sprite.sticky_effects.children.length).toBe(0);
|
expect(sprite.sticky_effects.children.length).toBe(0);
|
||||||
|
|
||||||
ship.addStickyEffect(new StickyEffect(new BaseEffect("test")));
|
ship.addStickyEffect(new StickyEffect(new BaseEffect("test")));
|
||||||
|
testgame.battleview.log_processor.jumpToEnd();
|
||||||
expect(sprite.sticky_effects.children.length).toBe(1);
|
expect(sprite.sticky_effects.children.length).toBe(1);
|
||||||
|
|
||||||
ship.addStickyEffect(new StickyEffect(new BaseEffect("test")));
|
ship.addStickyEffect(new StickyEffect(new BaseEffect("test")));
|
||||||
|
testgame.battleview.log_processor.jumpToEnd();
|
||||||
expect(sprite.sticky_effects.children.length).toBe(2);
|
expect(sprite.sticky_effects.children.length).toBe(2);
|
||||||
|
|
||||||
ship.cleanStickyEffects();
|
ship.cleanStickyEffects();
|
||||||
|
testgame.battleview.log_processor.jumpToEnd();
|
||||||
expect(sprite.sticky_effects.children.length).toBe(0);
|
expect(sprite.sticky_effects.children.length).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,6 +7,7 @@ module TS.SpaceTac.UI.Specs {
|
||||||
it("forwards events in targetting mode", function () {
|
it("forwards events in targetting mode", function () {
|
||||||
let battleview = testgame.battleview;
|
let battleview = testgame.battleview;
|
||||||
expect(battleview.targetting).toBeNull();
|
expect(battleview.targetting).toBeNull();
|
||||||
|
battleview.setInteractionEnabled(true);
|
||||||
|
|
||||||
battleview.cursorInSpace(5, 5);
|
battleview.cursorInSpace(5, 5);
|
||||||
|
|
||||||
|
|
|
@ -231,10 +231,12 @@ module TS.SpaceTac.UI {
|
||||||
enabled = false;
|
enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enabled != this.interacting) {
|
||||||
this.action_bar.setInteractive(enabled);
|
this.action_bar.setInteractive(enabled);
|
||||||
this.exitTargettingMode();
|
this.exitTargettingMode();
|
||||||
this.interacting = enabled;
|
this.interacting = enabled;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Enter targetting mode
|
// Enter targetting mode
|
||||||
// While in this mode, the Targetting object will receive hover and click events, and handle them
|
// While in this mode, the Targetting object will receive hover and click events, and handle them
|
||||||
|
|
|
@ -17,20 +17,14 @@ module TS.SpaceTac.UI {
|
||||||
// Link to the battle log
|
// Link to the battle log
|
||||||
private log: BattleLog
|
private log: BattleLog
|
||||||
|
|
||||||
// Subscription identifier
|
|
||||||
private subscription: any = null
|
|
||||||
|
|
||||||
// Delay before processing next events
|
|
||||||
private delayed = false
|
|
||||||
|
|
||||||
// Processing queue, when delay is active
|
|
||||||
private queue: BaseBattleEvent[] = []
|
|
||||||
|
|
||||||
// Forward events to other subscribers
|
// Forward events to other subscribers
|
||||||
private forwarding: LogSubscriber[] = []
|
private forwarding: ((event: BaseBattleEvent) => number)[] = []
|
||||||
|
|
||||||
// Current position in the battle log
|
// Current position in the battle log
|
||||||
private cursor = -1;
|
private cursor = -1
|
||||||
|
|
||||||
|
// Indicator that the log is being played continuously
|
||||||
|
private playing = false
|
||||||
|
|
||||||
constructor(view: BattleView) {
|
constructor(view: BattleView) {
|
||||||
this.view = view;
|
this.view = view;
|
||||||
|
@ -55,9 +49,29 @@ module TS.SpaceTac.UI {
|
||||||
* Start log processing
|
* Start log processing
|
||||||
*/
|
*/
|
||||||
start() {
|
start() {
|
||||||
this.subscription = this.log.subscribe(event => this.processBattleEvent(event));
|
|
||||||
this.cursor = this.log.events.length - 1;
|
this.cursor = this.log.events.length - 1;
|
||||||
this.battle.getBootstrapEvents().forEach(event => this.processBattleEvent(event));
|
this.battle.getBootstrapEvents().forEach(event => this.processBattleEvent(event));
|
||||||
|
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,17 +80,28 @@ module TS.SpaceTac.UI {
|
||||||
stepBackward() {
|
stepBackward() {
|
||||||
if (!this.atStart()) {
|
if (!this.atStart()) {
|
||||||
this.cursor -= 1;
|
this.cursor -= 1;
|
||||||
|
this.playing = false;
|
||||||
this.processBattleEvent(this.log.events[this.cursor + 1].getReverse());
|
this.processBattleEvent(this.log.events[this.cursor + 1].getReverse());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a step forward in time
|
* Make a step forward in time
|
||||||
|
*
|
||||||
|
* Returns the duration of the step processing
|
||||||
*/
|
*/
|
||||||
stepForward() {
|
stepForward(): number {
|
||||||
if (!this.atEnd()) {
|
if (!this.atEnd()) {
|
||||||
this.cursor += 1;
|
this.cursor += 1;
|
||||||
this.processBattleEvent(this.log.events[this.cursor]);
|
let result = this.processBattleEvent(this.log.events[this.cursor]);
|
||||||
|
|
||||||
|
if (this.atEnd()) {
|
||||||
|
this.playing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,12 +161,7 @@ module TS.SpaceTac.UI {
|
||||||
* The callback may return the duration it needs to display the change.
|
* The callback may return the duration it needs to display the change.
|
||||||
*/
|
*/
|
||||||
register(callback: (event: BaseBattleEvent) => number) {
|
register(callback: (event: BaseBattleEvent) => number) {
|
||||||
this.forwarding.push(event => {
|
this.forwarding.push(callback);
|
||||||
let duration = callback(event);
|
|
||||||
if (duration) {
|
|
||||||
this.delayNextEvents(duration);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,159 +177,135 @@ module TS.SpaceTac.UI {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Introduce a delay in event processing
|
|
||||||
*/
|
|
||||||
delayNextEvents(duration: number) {
|
|
||||||
if (duration > 0 && !this.view.gameui.headless) {
|
|
||||||
this.delayed = true;
|
|
||||||
this.view.timer.schedule(duration, () => this.processQueued());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process the events queued due to a delay
|
|
||||||
*/
|
|
||||||
processQueued() {
|
|
||||||
let events = acopy(this.queue);
|
|
||||||
this.queue = [];
|
|
||||||
this.delayed = false;
|
|
||||||
|
|
||||||
events.forEach(event => this.processBattleEvent(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a single event
|
* Process a single event
|
||||||
*/
|
*/
|
||||||
processBattleEvent(event: BaseBattleEvent) {
|
processBattleEvent(event: BaseBattleEvent): number {
|
||||||
if (this.delayed) {
|
|
||||||
this.queue.push(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Battle event", event);
|
console.log("Battle event", event);
|
||||||
|
|
||||||
this.forwarding.forEach(subscriber => subscriber(event));
|
let durations = this.forwarding.map(subscriber => subscriber(event));
|
||||||
|
|
||||||
if (event instanceof ShipChangeEvent) {
|
if (event instanceof ShipChangeEvent) {
|
||||||
this.processShipChangeEvent(event);
|
durations.push(this.processShipChangeEvent(event));
|
||||||
} else if (event instanceof DeathEvent) {
|
} else if (event instanceof DeathEvent) {
|
||||||
this.processDeathEvent(event);
|
durations.push(this.processDeathEvent(event));
|
||||||
} else if (event instanceof FireEvent) {
|
} else if (event instanceof FireEvent) {
|
||||||
this.processFireEvent(event);
|
durations.push(this.processFireEvent(event));
|
||||||
} else if (event instanceof DamageEvent) {
|
} else if (event instanceof DamageEvent) {
|
||||||
this.processDamageEvent(event);
|
durations.push(this.processDamageEvent(event));
|
||||||
} else if (event instanceof EndBattleEvent) {
|
} else if (event instanceof EndBattleEvent) {
|
||||||
this.processEndBattleEvent(event);
|
durations.push(this.processEndBattleEvent(event));
|
||||||
} else if (event instanceof DroneDeployedEvent) {
|
} else if (event instanceof DroneDeployedEvent) {
|
||||||
this.processDroneDeployedEvent(event);
|
durations.push(this.processDroneDeployedEvent(event));
|
||||||
} else if (event instanceof DroneDestroyedEvent) {
|
} else if (event instanceof DroneDestroyedEvent) {
|
||||||
this.processDroneDestroyedEvent(event);
|
durations.push(this.processDroneDestroyedEvent(event));
|
||||||
} else if (event instanceof DroneAppliedEvent) {
|
} else if (event instanceof DroneAppliedEvent) {
|
||||||
this.processDroneAppliedEvent(event);
|
durations.push(this.processDroneAppliedEvent(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME temporary fix for cursor not being forwarded
|
return max([0].concat(durations));
|
||||||
let cursor = this.log.events.indexOf(event);
|
|
||||||
if (cursor >= 0) {
|
|
||||||
this.cursor = cursor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer control to the needed player
|
/**
|
||||||
|
* Transfer control to the needed player (or not)
|
||||||
|
*/
|
||||||
|
private transferControl(): number {
|
||||||
let player = this.getPlayerNeeded();
|
let player = this.getPlayerNeeded();
|
||||||
if (player) {
|
if (player) {
|
||||||
if (this.battle.playing_ship && !this.battle.playing_ship.alive) {
|
if (this.battle.playing_ship && !this.battle.playing_ship.alive) {
|
||||||
this.view.setInteractionEnabled(false);
|
this.view.setInteractionEnabled(false);
|
||||||
this.battle.advanceToNextShip();
|
this.battle.advanceToNextShip();
|
||||||
this.delayNextEvents(200);
|
return 200;
|
||||||
} else if (player === this.view.player) {
|
} else if (player === this.view.player) {
|
||||||
this.view.setInteractionEnabled(true);
|
this.view.setInteractionEnabled(true);
|
||||||
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
this.view.playAI();
|
this.view.playAI();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.view.setInteractionEnabled(false);
|
this.view.setInteractionEnabled(false);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy the log processor
|
// Destroy the log processor
|
||||||
destroy() {
|
destroy() {
|
||||||
if (this.subscription) {
|
// TODO interrupt current processing or delay
|
||||||
this.log.unsubscribe(this.subscription);
|
|
||||||
this.subscription = null;
|
|
||||||
this.queue = [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Playing ship changed
|
// Playing ship changed
|
||||||
private processShipChangeEvent(event: ShipChangeEvent): void {
|
private processShipChangeEvent(event: ShipChangeEvent): number {
|
||||||
this.view.arena.setShipPlaying(event.new_ship);
|
this.view.arena.setShipPlaying(event.new_ship);
|
||||||
this.view.ship_list.setPlaying(event.new_ship);
|
this.view.ship_list.setPlaying(event.new_ship);
|
||||||
if (event.ship !== event.new_ship) {
|
if (event.ship !== event.new_ship) {
|
||||||
this.view.gameui.audio.playOnce("battle-ship-change");
|
this.view.gameui.audio.playOnce("battle-ship-change");
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Damage to ship
|
// Damage to ship
|
||||||
private processDamageEvent(event: DamageEvent): void {
|
private processDamageEvent(event: DamageEvent): number {
|
||||||
var item = this.view.ship_list.findItem(event.ship);
|
var item = this.view.ship_list.findItem(event.ship);
|
||||||
if (item) {
|
if (item) {
|
||||||
item.setDamageHit();
|
item.setDamageHit();
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ship died
|
// A ship died
|
||||||
private processDeathEvent(event: DeathEvent): void {
|
private processDeathEvent(event: DeathEvent): number {
|
||||||
if (this.view.ship_hovered === event.ship) {
|
if (this.view.ship_hovered === event.ship) {
|
||||||
this.view.setShipHovered(null);
|
this.view.setShipHovered(null);
|
||||||
}
|
}
|
||||||
this.view.arena.markAsDead(event.ship);
|
this.view.arena.markAsDead(event.ship);
|
||||||
this.view.ship_list.markAsDead(event.ship);
|
this.view.ship_list.markAsDead(event.ship);
|
||||||
|
|
||||||
if (!event.initial) {
|
return event.initial ? 0 : 1000;
|
||||||
this.delayNextEvents(1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weapon used
|
// Weapon used
|
||||||
private processFireEvent(event: FireEvent): void {
|
private processFireEvent(event: FireEvent): number {
|
||||||
var source = Target.newFromShip(event.ship);
|
var source = Target.newFromShip(event.ship);
|
||||||
var destination = event.target;
|
var destination = event.target;
|
||||||
|
|
||||||
var effect = new WeaponEffect(this.view.arena, source, destination, event.weapon);
|
var effect = new WeaponEffect(this.view.arena, source, destination, event.weapon);
|
||||||
let duration = effect.start();
|
let duration = effect.start();
|
||||||
|
|
||||||
this.delayNextEvents(duration);
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Battle ended (victory or defeat)
|
// Battle ended (victory or defeat)
|
||||||
private processEndBattleEvent(event: EndBattleEvent): void {
|
private processEndBattleEvent(event: EndBattleEvent): number {
|
||||||
this.view.endBattle();
|
this.view.endBattle();
|
||||||
this.destroy();
|
this.destroy();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// New drone deployed
|
// New drone deployed
|
||||||
private processDroneDeployedEvent(event: DroneDeployedEvent): void {
|
private processDroneDeployedEvent(event: DroneDeployedEvent): number {
|
||||||
let duration = this.view.arena.addDrone(event.drone, !event.initial);
|
let duration = this.view.arena.addDrone(event.drone, !event.initial);
|
||||||
|
|
||||||
if (duration) {
|
if (duration) {
|
||||||
this.view.gameui.audio.playOnce("battle-drone-deploy");
|
this.view.gameui.audio.playOnce("battle-drone-deploy");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.delayNextEvents(duration);
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drone destroyed
|
// Drone destroyed
|
||||||
private processDroneDestroyedEvent(event: DroneDestroyedEvent): void {
|
private processDroneDestroyedEvent(event: DroneDestroyedEvent): number {
|
||||||
this.view.arena.removeDrone(event.drone);
|
this.view.arena.removeDrone(event.drone);
|
||||||
if (!event.initial) {
|
if (!event.initial) {
|
||||||
this.view.gameui.audio.playOnce("battle-drone-destroy");
|
this.view.gameui.audio.playOnce("battle-drone-destroy");
|
||||||
this.delayNextEvents(1000);
|
return 1000;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drone applied
|
// Drone applied
|
||||||
private processDroneAppliedEvent(event: DroneAppliedEvent): void {
|
private processDroneAppliedEvent(event: DroneAppliedEvent): number {
|
||||||
let drone = this.view.arena.findDrone(event.drone);
|
let drone = this.view.arena.findDrone(event.drone);
|
||||||
if (drone) {
|
if (drone) {
|
||||||
let duration = drone.setApplied();
|
let duration = drone.setApplied();
|
||||||
|
@ -318,7 +314,9 @@ module TS.SpaceTac.UI {
|
||||||
this.view.gameui.audio.playOnce("battle-drone-activate");
|
this.view.gameui.audio.playOnce("battle-drone-activate");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.delayNextEvents(duration);
|
return duration;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue