diff --git a/TODO b/TODO index 58a5d06..ebd37ac 100644 --- a/TODO +++ b/TODO @@ -18,8 +18,7 @@ * Menu: allow to delete cloud saves * Arena: hide dead drones from tactical view * Arena: add power indicator in ship hover information -* Arena: temporarily show ship information when it changes -* Arena: display important changes (damages, effects...) instead of attribute changes +* Arena: display effects description instead of attribute changes * Arena: Organize objects and information in layers * Arena: Add auto-move to attack * Actions: Separate overheat and cooldown display diff --git a/src/core/ShipValue.ts b/src/core/ShipValue.ts index 612a433..a01f8b5 100644 --- a/src/core/ShipValue.ts +++ b/src/core/ShipValue.ts @@ -25,6 +25,13 @@ module TS.SpaceTac { return this.current; } + /** + * Get the maximal value + */ + getMaximal(): number | null { + return this.maximal; + } + /** * Set the upper bound the value must not cross */ diff --git a/src/ui/battle/Arena.ts b/src/ui/battle/Arena.ts index 83269d9..3f7dec6 100644 --- a/src/ui/battle/Arena.ts +++ b/src/ui/battle/Arena.ts @@ -126,6 +126,7 @@ module TS.SpaceTac.UI { var arena_ship = this.findShipSprite(ship); if (arena_ship) { arena_ship.setHovered(true); + this.bringToTop(arena_ship); } this.hovered = arena_ship; } else { diff --git a/src/ui/battle/ArenaShip.ts b/src/ui/battle/ArenaShip.ts index 0fe2ec0..729c90f 100644 --- a/src/ui/battle/ArenaShip.ts +++ b/src/ui/battle/ArenaShip.ts @@ -23,6 +23,7 @@ module TS.SpaceTac.UI { info: Phaser.Group info_hull: ValueBar info_shield: ValueBar + info_toggle: Toggle // Frame to indicate the owner of the ship, and if it is playing frame: Phaser.Image @@ -74,6 +75,7 @@ module TS.SpaceTac.UI { this.info_shield.setValue(this.ship.getValue("shield"), this.ship.getAttribute("shield_capacity")); this.info.add(this.info_shield); this.info.visible = false; + this.info_toggle = this.battleview.animations.newVisibilityToggle(this.info, 200); this.add(this.info); // Effects display @@ -93,11 +95,28 @@ module TS.SpaceTac.UI { this.position.set(ship.arena_x, ship.arena_y); // Log processing - this.battleview.log_processor.registerForShip(ship, event => { - if (event instanceof EffectAddedEvent || event instanceof EffectRemovedEvent || event instanceof EffectDurationChangedEvent) { - this.updateStickyEffects(); + this.battleview.log_processor.registerForShip(ship, event => this.processLogEvent(event)); + } + + /** + * Process a log event for this ship + */ + private processLogEvent(event: BaseLogEvent) { + if (event instanceof EffectAddedEvent || event instanceof EffectRemovedEvent || event instanceof EffectDurationChangedEvent) { + this.updateStickyEffects(); + } else if (event instanceof ValueChangeEvent) { + if (event.value.name == "hull") { + this.info_toggle.start(1500, true); + this.info_hull.setValue(event.value.get(), event.value.getMaximal() || 0); + } else if (event.value.name == "shield") { + this.info_toggle.start(1500, true); + this.info_shield.setValue(event.value.get(), event.value.getMaximal() || 0); + } else { + this.displayValueChanged(event); } - }); + } else if (event instanceof DamageEvent) { + this.displayEffect(`${event.hull + event.shield} damage`, false); + } } /** @@ -106,7 +125,11 @@ module TS.SpaceTac.UI { * This will show the information HUD accordingly */ setHovered(hovered: boolean) { - this.battleview.animations.setVisible(this.info, hovered, 200); + if (hovered) { + this.info_toggle.start(); + } else { + this.info_toggle.stop(); + } } // Set the playing state on this ship @@ -177,12 +200,6 @@ module TS.SpaceTac.UI { let diff = event.diff; let name = event.value.name; this.displayEffect(`${name} ${diff < 0 ? "-" : "+"}${Math.abs(diff)}`, diff >= 0); - - if (name == "hull") { - this.info_hull.setValue(event.value.get(), this.ship.getAttribute("hull_capacity")); - } else if (name == "shield") { - this.info_shield.setValue(event.value.get(), this.ship.getAttribute("shield_capacity")); - } } /** diff --git a/src/ui/battle/LogProcessor.ts b/src/ui/battle/LogProcessor.ts index 26e08e0..c54aedb 100644 --- a/src/ui/battle/LogProcessor.ts +++ b/src/ui/battle/LogProcessor.ts @@ -100,8 +100,6 @@ module TS.SpaceTac.UI { this.processShipChangeEvent(event); } else if (event instanceof MoveEvent) { this.processMoveEvent(event); - } else if (event instanceof ValueChangeEvent) { - this.processValueChangedEvent(event); } else if (event instanceof DeathEvent) { this.processDeathEvent(event); } else if (event instanceof FireEvent) { @@ -170,16 +168,6 @@ module TS.SpaceTac.UI { } } - // Ship value changed - private processValueChangedEvent(event: ValueChangeEvent): void { - var sprite = this.view.arena.findShipSprite(event.ship); - if (sprite) { - sprite.displayValueChanged(event); - } - - // TODO Update tooltip - } - // A ship died private processDeathEvent(event: DeathEvent): void { if (this.view.ship_hovered === event.ship) { diff --git a/src/ui/common/Animations.ts b/src/ui/common/Animations.ts index 5b40582..256874e 100644 --- a/src/ui/common/Animations.ts +++ b/src/ui/common/Animations.ts @@ -102,6 +102,13 @@ module TS.SpaceTac.UI { } } + /** + * Get a toggle on visibility + */ + newVisibilityToggle(obj: IAnimationFadeable, duration = 1000): Toggle { + return new Toggle(() => this.setVisible(obj, true, duration), () => this.setVisible(obj, false, duration)); + } + /** * Interpolate a rotation value * diff --git a/src/ui/common/Toggle.spec.ts b/src/ui/common/Toggle.spec.ts new file mode 100644 index 0000000..d581164 --- /dev/null +++ b/src/ui/common/Toggle.spec.ts @@ -0,0 +1,107 @@ +/// + +module TS.SpaceTac.UI.Specs { + describe("Toggle", function () { + let on_calls = 0; + let off_calls = 0; + + beforeEach(function () { + on_calls = 0; + off_calls = 0; + jasmine.clock().install(); + }); + + afterEach(function () { + jasmine.clock().uninstall(); + }); + + function newToggle(): Toggle { + return new Toggle(() => on_calls++, () => off_calls++); + } + + function check(on: number, off: number) { + expect(on_calls).toBe(on); + expect(off_calls).toBe(off); + on_calls = 0; + off_calls = 0; + } + + it("handles simple toggling", function () { + let toggle = newToggle(); + check(0, 0); + + toggle.start(); + check(1, 0); + + jasmine.clock().tick(10000000); + check(0, 0); + + toggle.stop(); + check(0, 1); + + jasmine.clock().tick(10000000); + check(0, 0); + + toggle.stop(); + check(0, 0); + + toggle.start(); + check(1, 0); + }); + + it("applies hard priority", function () { + let toggle = newToggle(); + check(0, 0); + + // hard + toggle.start(0, true); + check(1, 0); + + toggle.stop(false); + check(0, 0); + + toggle.stop(true); + check(0, 1); + + // soft + toggle.start(0, false); + check(1, 0); + + toggle.stop(true); + check(0, 1); + + // soft lifted to hard + toggle.start(0, false); + check(1, 0); + + toggle.start(0, true); + check(0, 0); + + toggle.stop(false); + check(0, 0); + + toggle.stop(true); + check(0, 1); + }); + + it("automatically stop after a duration", function () { + let toggle = newToggle(); + check(0, 0); + + toggle.start(10); + check(1, 0); + + jasmine.clock().tick(9); + check(0, 0); + + jasmine.clock().tick(2); + check(0, 1); + + toggle.stop(); + check(0, 0); + + toggle.start(); + check(1, 0); + }); + }); +} \ No newline at end of file diff --git a/src/ui/common/Toggle.ts b/src/ui/common/Toggle.ts new file mode 100644 index 0000000..b94ec09 --- /dev/null +++ b/src/ui/common/Toggle.ts @@ -0,0 +1,50 @@ +module TS.SpaceTac { + /** + * A toggle between two states (on and off), with timing features. + */ + export class Toggle { + private on: Function + private off: Function + private status = false + private hard = false + private timer = Timer.global; + + constructor(on: Function, off: Function) { + this.on = on; + this.off = off; + } + + /** + * Start the toggle (set the status *on*) + * + * If *duration* is set, stop() will automatically be called after these milliseconds. + * + * If *hard* is true, it can only be stopped by a hard stop. + */ + start(duration = 0, hard = false) { + if (hard) { + this.hard = true; + } + if (!this.status) { + this.status = true; + this.on(); + } + if (duration) { + this.timer.schedule(duration, () => this.stop(hard)); + } + } + + /** + * Stop the toggle (set the status *off*) + */ + stop(hard = false) { + if (this.status) { + if (hard || !this.hard) { + this.status = false; + this.hard = false; + this.off(); + } + } + } + } +}