diff --git a/TODO b/TODO index 60d8e59..978706d 100644 --- a/TODO +++ b/TODO @@ -6,11 +6,9 @@ * Organize arena objects and information in layers * Allow to cancel last moves * Add more visual effects to weapons, hits and explosions -* Add keyboard shortcuts for actions * Effect should be random in a range (eg. "damage target 50-75") * Add an overload/cooling system * Add auto-move to attack -* Escape key should cancel selected action * Merge identical sticky effects * Handle effects overflowing ship tooltip when too numerous * Proper arena scaling (not graphical, only space coordinates) diff --git a/src/ui/battle/ActionBar.ts b/src/ui/battle/ActionBar.ts index c3fcb19..ba42520 100644 --- a/src/ui/battle/ActionBar.ts +++ b/src/ui/battle/ActionBar.ts @@ -5,6 +5,7 @@ module TS.SpaceTac.UI { battleview: BattleView; // List of action icons + group: Phaser.Group; actions: ActionIcon[]; // Progress bar displaying action points @@ -14,9 +15,15 @@ module TS.SpaceTac.UI { // Tooltip to display hovered action info tooltip: ActionTooltip; + // Indicator of interaction disabled + icon_waiting: Phaser.Image; + // Current ship, whose actions are displayed ship: Ship; + // Interactivity + interactive = false; + // Create an empty action bar constructor(battleview: BattleView) { super(battleview.game); @@ -38,9 +45,56 @@ module TS.SpaceTac.UI { this.actionpointstemp.setBarImage("battle-actionpointsfull"); this.addChild(this.actionpointstemp); + // Group for actions + this.group = new Phaser.Group(this.game); + this.addChild(this.group); + + // Waiting icon + this.icon_waiting = new Phaser.Image(this.game, this.width / 2, 50, "battle-waiting", 0); + this.icon_waiting.anchor.set(0.5, 0.5); + this.icon_waiting.scale.set(0.5, 0.5); + this.game.tweens.create(this.icon_waiting).to({ "angle": 360 }, 3000).loop().start(); + this.addChild(this.icon_waiting); + // Tooltip this.tooltip = new ActionTooltip(this); this.addChild(this.tooltip); + + // Key bindings + battleview.inputs.bind(Phaser.Keyboard.ESC, "Cancel action", () => this.actionEnded()); + battleview.inputs.bind(Phaser.Keyboard.SPACEBAR, "End turn", () => this.keyActionPressed(-1)); + battleview.inputs.bind(Phaser.Keyboard.ONE, "Action 1", () => this.keyActionPressed(0)); + battleview.inputs.bind(Phaser.Keyboard.TWO, "Action 2", () => this.keyActionPressed(1)); + battleview.inputs.bind(Phaser.Keyboard.THREE, "Action 3", () => this.keyActionPressed(2)); + battleview.inputs.bind(Phaser.Keyboard.FOUR, "Action 4", () => this.keyActionPressed(3)); + battleview.inputs.bind(Phaser.Keyboard.FIVE, "Action 5", () => this.keyActionPressed(4)); + battleview.inputs.bind(Phaser.Keyboard.SIX, "Action 6", () => this.keyActionPressed(5)); + battleview.inputs.bind(Phaser.Keyboard.SEVEN, "Action 7", () => this.keyActionPressed(6)); + battleview.inputs.bind(Phaser.Keyboard.EIGHT, "Action 8", () => this.keyActionPressed(7)); + battleview.inputs.bind(Phaser.Keyboard.NINE, "Action 9", () => this.keyActionPressed(8)); + battleview.inputs.bind(Phaser.Keyboard.ZERO, "Action 10", () => this.keyActionPressed(9)); + } + + /** + * Set the interactivity state + */ + setInteractive(interactive: boolean) { + this.interactive = interactive; + + Animation.setVisibility(this.game, this.icon_waiting, !this.interactive, 100); + } + + /** + * Called when an action shortcut key is pressed + */ + keyActionPressed(position: number) { + if (this.interactive) { + if (position < 0) { + this.actions[this.actions.length - 1].processClick(); + } else if (position < this.actions.length) { + this.actions[position].processClick(); + } + } } // Clear the action icons diff --git a/src/ui/battle/ActionIcon.ts b/src/ui/battle/ActionIcon.ts index 3645427..e8c824a 100644 --- a/src/ui/battle/ActionIcon.ts +++ b/src/ui/battle/ActionIcon.ts @@ -43,7 +43,7 @@ module TS.SpaceTac.UI { this.ship = ship; this.action = action; - bar.addChild(this); + bar.group.addChild(this); // Active layer this.active = false; @@ -85,6 +85,9 @@ module TS.SpaceTac.UI { // Process a click event on the action icon processClick(): void { + if (!this.bar.interactive) { + return; + } if (!this.action.canBeUsed(this.battleview.battle, this.ship)) { return; } diff --git a/src/ui/battle/ActionTooltip.spec.ts b/src/ui/battle/ActionTooltip.spec.ts new file mode 100644 index 0000000..646ce6d --- /dev/null +++ b/src/ui/battle/ActionTooltip.spec.ts @@ -0,0 +1,35 @@ +/// + +module TS.SpaceTac.UI.Specs { + describe("ActionTooltip", () => { + inbattleview_it("displays action information", (battleview: BattleView) => { + let bar = battleview.action_bar; + let tooltip = bar.tooltip; + + bar.clearAll(); + let a1 = bar.addAction(battleview.battle.playing_ship, new MoveAction(new Equipment())); + a1.action.equipment.name = "Engine"; + a1.action.name = "Move"; + let a2 = bar.addAction(battleview.battle.playing_ship, new FireWeaponAction(new Equipment())); + a2.action.equipment.name = "Weapon"; + a2.action.name = "Fire"; + let a3 = bar.addAction(battleview.battle.playing_ship, new EndTurnAction()); + a3.action.name = "End turn"; + + tooltip.setAction(a1); + expect(tooltip.main_title.text).toEqual("Engine"); + expect(tooltip.sub_title.text).toEqual("Move"); + expect(tooltip.shortcut.text).toEqual("[ 1 ]"); + + tooltip.setAction(a2); + expect(tooltip.main_title.text).toEqual("Weapon"); + expect(tooltip.sub_title.text).toEqual("Fire"); + expect(tooltip.shortcut.text).toEqual("[ 2 ]"); + + tooltip.setAction(a3); + expect(tooltip.main_title.text).toEqual("End turn"); + expect(tooltip.sub_title.text).toEqual(""); + expect(tooltip.shortcut.text).toEqual("[ space ]"); + }); + }); +} diff --git a/src/ui/battle/ActionTooltip.ts b/src/ui/battle/ActionTooltip.ts index 30d62e7..8b3d149 100644 --- a/src/ui/battle/ActionTooltip.ts +++ b/src/ui/battle/ActionTooltip.ts @@ -1,14 +1,17 @@ module TS.SpaceTac.UI { // Tooltip to display action information export class ActionTooltip extends Phaser.Sprite { + bar: ActionBar; icon: Phaser.Image | null; main_title: Phaser.Text; sub_title: Phaser.Text; cost: Phaser.Text; description: Phaser.Text; + shortcut: Phaser.Text; constructor(parent: ActionBar) { super(parent.game, 0, 0, "battle-action-tooltip"); + this.bar = parent; this.visible = false; this.icon = null; @@ -29,6 +32,10 @@ module TS.SpaceTac.UI { this.description.wordWrap = true; this.description.wordWrapWidth = 476; this.addChild(this.description); + + this.shortcut = new Phaser.Text(this.game, this.width - 5, this.height - 5, "", { font: "12pt Arial", fill: "#aaaaaa" }); + this.shortcut.anchor.set(1, 1); + this.addChild(this.shortcut); } // Set current action to display, null to hide @@ -52,6 +59,17 @@ module TS.SpaceTac.UI { this.cost.setText(cost); this.description.setText(action.action.equipment ? action.action.equipment.getActionDescription() : ""); + let position = this.bar.actions.indexOf(action); + if (action.action instanceof EndTurnAction) { + this.shortcut.setText("[ space ]"); + } else if (position == 9) { + this.shortcut.setText("[ 0 ]"); + } else if (position >= 0 && position < 9) { + this.shortcut.setText(`[ ${position + 1} ]`); + } else { + this.shortcut.setText(""); + } + Animation.fadeIn(this.game, this, 200, 0.9); } else { Animation.fadeOut(this.game, this, 200); diff --git a/src/ui/battle/BattleView.ts b/src/ui/battle/BattleView.ts index 78fa6d4..21eb828 100644 --- a/src/ui/battle/BattleView.ts +++ b/src/ui/battle/BattleView.ts @@ -40,9 +40,6 @@ module TS.SpaceTac.UI { // True if player interaction is allowed interacting: boolean; - // Indicator of interaction disabled - icon_waiting: Phaser.Image; - // Init the view, binding it to a specific battle init(player: Player, battle: Battle) { super.init(); @@ -79,12 +76,6 @@ module TS.SpaceTac.UI { this.ship_list = new ShipList(this); this.ship_tooltip = new ShipTooltip(this); - this.icon_waiting = new Phaser.Image(this.game, this.getWidth() / 2, 50, "battle-waiting", 0); - this.icon_waiting.anchor.set(0.5, 0.5); - this.icon_waiting.scale.set(0.5, 0.5); - game.add.existing(this.icon_waiting); - game.tweens.create(this.icon_waiting).to({ "angle": 360 }, 3000).loop().start(); - // Start processing the battle log this.log_processor = new LogProcessor(this); @@ -95,7 +86,6 @@ module TS.SpaceTac.UI { this.gameui.audio.startMusic("full-on"); // Key mapping - this.inputs.bind(Phaser.Keyboard.SPACEBAR, "End turn", this.onSpaceKeyPressed); this.inputs.bindCheat(Phaser.Keyboard.W, "Win current battle", () => { this.battle.endBattle(this.player.fleet); }); @@ -138,13 +128,6 @@ module TS.SpaceTac.UI { text_anim.start(); } - // Listener for space key events - onSpaceKeyPressed(): void { - if (this.battle.playing_ship && this.battle.playing_ship.getPlayer() === this.player) { - this.battle.advanceToNextShip(); - } - } - // Method called when cursor starts hovering over a ship (or its icon) cursorOnShip(ship: Ship): void { this.setShipHovered(ship); @@ -192,9 +175,8 @@ module TS.SpaceTac.UI { // Disable interaction when it is the AI turn, or when the current ship can't play setInteractionEnabled(enabled: boolean): void { this.exitTargettingMode(); + this.action_bar.setInteractive(enabled); this.interacting = enabled; - - Animation.setVisibility(this.game, this.icon_waiting, !this.interacting, 100); } // Enter targetting mode diff --git a/src/ui/common/InputManager.ts b/src/ui/common/InputManager.ts index 76729a4..6e307ca 100644 --- a/src/ui/common/InputManager.ts +++ b/src/ui/common/InputManager.ts @@ -20,6 +20,8 @@ module TS.SpaceTac.UI { this.cheats_enabled = true; this.cheat = false; + this.input.reset(true); + // Default mappings this.bind(Phaser.Keyboard.S, "Quick save", () => { this.game.saveGame(); @@ -34,6 +36,7 @@ module TS.SpaceTac.UI { this.bind(Phaser.Keyboard.NUMPAD_ADD, null, () => { if (this.cheats_enabled) { this.cheat = !this.cheat; + this.game.displayMessage(this.cheat ? "Cheats enabled" : "Cheats disabled"); } }); }