1
0
Fork 0

battle: Fixed timing issues in log processing

This commit is contained in:
Michaël Lemaire 2017-06-13 22:48:43 +02:00
parent f62f2236f8
commit 07d0eb7a9d
6 changed files with 92 additions and 87 deletions

1
TODO
View file

@ -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

View file

@ -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);
}); });
}); });

View file

@ -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);
}); });
}); });

View file

@ -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);

View file

@ -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

View file

@ -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;
} }
} }
} }