diff --git a/TODO b/TODO index 1ac1837..05ed18c 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,13 @@ -* Floor movement power usage, and show it on the displayed line * Add auto-move to attack * Re-style cancel button and allow to cancel by clicking the action again +* Fix energy depleter effect not fading after one turn * Add equipment info (or summary) in ship tooltip * Handle effects overflowing ship tooltip when too numerous * Proper arena scaling (not graphical, only space coordinates) * Mobile: think UI layout so that fingers do not block the view (right and left handed) +* Mobile: display tooltips larger and on the side of screen where the finger is not * Mobile: targetting in two times, using a draggable target indicator +* AI: apply safety distances to move actions * Add a defeat screen (game over for now) * Add a victory screen, with loot display * Add retreat from battle diff --git a/out/assets/images/battle/actionpointsempty.png b/out/assets/images/battle/actionpointsempty.png index b2d1909..bdac7aa 100644 Binary files a/out/assets/images/battle/actionpointsempty.png and b/out/assets/images/battle/actionpointsempty.png differ diff --git a/out/assets/images/battle/actionpointsfull.png b/out/assets/images/battle/actionpointsfull.png index 268b36f..9c4442b 100644 Binary files a/out/assets/images/battle/actionpointsfull.png and b/out/assets/images/battle/actionpointsfull.png differ diff --git a/out/assets/images/battle/actionpointsnone.png b/out/assets/images/battle/actionpointsnone.png index 9df62fc..b93a437 100644 Binary files a/out/assets/images/battle/actionpointsnone.png and b/out/assets/images/battle/actionpointsnone.png differ diff --git a/out/assets/images/battle/actionpointspart.png b/out/assets/images/battle/actionpointspart.png index 548687d..7bd47d6 100644 Binary files a/out/assets/images/battle/actionpointspart.png and b/out/assets/images/battle/actionpointspart.png differ diff --git a/out/assets/images/battle/arena/ap-indicator.png b/out/assets/images/battle/arena/ap-indicator.png new file mode 100644 index 0000000..8a350fa Binary files /dev/null and b/out/assets/images/battle/arena/ap-indicator.png differ diff --git a/src/game/actions/MoveAction.spec.ts b/src/game/actions/MoveAction.spec.ts index e869cf6..a232493 100644 --- a/src/game/actions/MoveAction.spec.ts +++ b/src/game/actions/MoveAction.spec.ts @@ -13,6 +13,8 @@ module SpaceTac.Game { engine.ap_usage = 2; var action = new MoveAction(engine); + expect(action.getDistanceByActionPoint(ship)).toBe(0.5); + var result = action.checkTarget(battle, ship, Target.newFromLocation(0, 2)); expect(result).toEqual(Target.newFromLocation(0, 2)); diff --git a/src/game/actions/MoveAction.ts b/src/game/actions/MoveAction.ts index 2992f6c..5c11b1a 100644 --- a/src/game/actions/MoveAction.ts +++ b/src/game/actions/MoveAction.ts @@ -29,16 +29,23 @@ module SpaceTac.Game { } var distance = Target.newFromShip(ship).getDistanceTo(target); - return this.equipment.ap_usage * distance / this.equipment.distance; + return Math.ceil(this.equipment.ap_usage * distance / this.equipment.distance); } getRangeRadius(ship: Ship): number { return ship.ap_current.current * this.equipment.distance / this.equipment.ap_usage; } + /** + * Get the distance that may be traveled with 1 action point + */ + getDistanceByActionPoint(ship: Ship): number { + return this.equipment.distance / this.equipment.ap_usage; + } + checkLocationTarget(battle: Battle, ship: Ship, target: Target): Target { // Apply maximal distance - var max_distance = this.equipment.distance * ship.ap_current.current / this.equipment.ap_usage; + var max_distance = this.getRangeRadius(ship); target = target.constraintInRange(ship.arena_x, ship.arena_y, max_distance); // Apply collision prevention diff --git a/src/game/equipments/ConventionalEngine.ts b/src/game/equipments/ConventionalEngine.ts index 5b62ca3..509444a 100644 --- a/src/game/equipments/ConventionalEngine.ts +++ b/src/game/equipments/ConventionalEngine.ts @@ -7,8 +7,8 @@ module SpaceTac.Game.Equipments { super(SlotType.Engine, "Conventional Engine"); this.min_level = new IntegerRange(1, 1); - this.distance = new Range(300, 300); - this.ap_usage = new IntegerRange(3); + this.distance = new Range(100, 100); + this.ap_usage = new IntegerRange(1); this.addPermanentAttributeMaxEffect(AttributeCode.Initiative, 1); } diff --git a/src/view/Preload.ts b/src/view/Preload.ts index 76b5deb..3533207 100644 --- a/src/view/Preload.ts +++ b/src/view/Preload.ts @@ -30,6 +30,7 @@ module SpaceTac.View { this.loadImage("battle/shiplist-shield-full.png"); this.loadImage("battle/background.jpg"); this.loadImage("battle/arena/background.png"); + this.loadImage("battle/arena/ap-indicator.png"); this.loadImage("battle/actionbar.png"); this.loadImage("battle/actionbar-cancel.png"); this.loadImage("battle/action-inactive.png"); diff --git a/src/view/battle/ActionIcon.ts b/src/view/battle/ActionIcon.ts index 64f4ded..b7f2fcc 100644 --- a/src/view/battle/ActionIcon.ts +++ b/src/view/battle/ActionIcon.ts @@ -52,7 +52,7 @@ module SpaceTac.View { this.addChild(this.layer_icon); let show_info = () => { - if (this.bar.ship)  { + if (this.bar.ship) { this.bar.tooltip.setAction(this); this.battleview.arena.range_hint.setSecondary(this.ship, this.action); } @@ -99,6 +99,9 @@ module SpaceTac.View { this.targetting.setSource(this.battleview.arena.findShipSprite(this.ship)); this.targetting.targetSelected.add(this.processSelection, this); this.targetting.targetHovered.add(this.processHover, this); + if (this.action instanceof Game.MoveAction) { + this.targetting.setApIndicatorsInterval(this.action.getDistanceByActionPoint(this.ship)); + } } else { // No target needed, apply action immediately this.processSelection(null); diff --git a/src/view/battle/ActionTooltip.ts b/src/view/battle/ActionTooltip.ts index 558dafe..1646696 100644 --- a/src/view/battle/ActionTooltip.ts +++ b/src/view/battle/ActionTooltip.ts @@ -45,7 +45,11 @@ module SpaceTac.View { this.position.set(action.x, action.y + action.height + 44); this.main_title.setText(action.action.equipment ? action.action.equipment.name : action.action.name); this.sub_title.setText(action.action.equipment ? action.action.name : ""); - this.cost.setText(action.action.equipment ? `Cost: ${action.action.equipment.ap_usage} power` : ""); + let cost = action.action.equipment ? `Cost: ${action.action.equipment.ap_usage} power` : ""; + if (action.action instanceof Game.MoveAction) { + cost += ` per ${action.action.equipment.distance}km`; + } + this.cost.setText(cost); this.description.setText(action.action.equipment ? action.action.equipment.getActionDescription() : ""); Animation.fadeIn(this.game, this, 200, 0.9); diff --git a/src/view/battle/Targetting.spec.ts b/src/view/battle/Targetting.spec.ts index 64a2349..dd301c5 100644 --- a/src/view/battle/Targetting.spec.ts +++ b/src/view/battle/Targetting.spec.ts @@ -20,5 +20,49 @@ module SpaceTac.View.Specs { expect(hovered).toEqual([Game.Target.newFromLocation(1, 2)]); expect(selected).toEqual([Game.Target.newFromLocation(1, 2)]); }); + + inbattleview_it("displays action point indicators", (battleview) => { + let source = new Phaser.Group(battleview.game, battleview.arena); + source.position.set(0, 0); + + let init_children = battleview.arena.children.length; + + let targetting = new Targetting(battleview); + + targetting.setSource(source); + targetting.setTargetSpace(200, 100); + targetting.update(); + targetting.updateApIndicators(); + + expect(targetting.ap_indicators.length).toBe(0); + expect(battleview.arena.children.length).toBe(init_children + 3); + + targetting.setApIndicatorsInterval(Math.sqrt(5) * 20); + + expect(targetting.ap_indicators.length).toBe(5); + expect(battleview.arena.children.length).toBe(init_children + 3 + 5); + expect(targetting.ap_indicators[0].position.x).toBe(0); + expect(targetting.ap_indicators[0].position.y).toBe(0); + expect(targetting.ap_indicators[1].position.x).toBeCloseTo(40); + expect(targetting.ap_indicators[1].position.y).toBeCloseTo(20); + expect(targetting.ap_indicators[2].position.x).toBeCloseTo(80); + expect(targetting.ap_indicators[2].position.y).toBeCloseTo(40); + expect(targetting.ap_indicators[3].position.x).toBeCloseTo(120); + expect(targetting.ap_indicators[3].position.y).toBeCloseTo(60); + expect(targetting.ap_indicators[4].position.x).toBeCloseTo(160); + expect(targetting.ap_indicators[4].position.y).toBeCloseTo(80); + + targetting.setApIndicatorsInterval(1000); + expect(targetting.ap_indicators.length).toBe(1); + expect(battleview.arena.children.length).toBe(init_children + 3 + 1); + + targetting.setApIndicatorsInterval(1); + expect(targetting.ap_indicators.length).toBe(224); + expect(battleview.arena.children.length).toBe(init_children + 3 + 224); + + targetting.destroy(); + + expect(battleview.arena.children.length).toBe(init_children); + }); }); } diff --git a/src/view/battle/Targetting.ts b/src/view/battle/Targetting.ts index 6059bd4..ad5bd60 100644 --- a/src/view/battle/Targetting.ts +++ b/src/view/battle/Targetting.ts @@ -20,6 +20,10 @@ module SpaceTac.View { // Signal to receive targetting events targetSelected: Phaser.Signal; + // AP usage display + ap_interval: number = 0; + ap_indicators: Phaser.Image[] = []; + // Access to the parent battle view private battleview: BattleView; @@ -63,6 +67,13 @@ module SpaceTac.View { if (this.blast) { this.blast.destroy(); } + this.ap_indicators.forEach(indicator => indicator.destroy()); + } + + // Set AP indicators to display at fixed interval along the line + setApIndicatorsInterval(interval: number) { + this.ap_interval = interval; + this.updateApIndicators(); } // Update visual effects for current targetting @@ -98,6 +109,40 @@ module SpaceTac.View { } else { this.blast.visible = false; } + + this.updateApIndicators(); + } + } + + // Update the AP indicators display + updateApIndicators() { + // Get indicator count + let count = 0; + let distance = 0; + if (this.line_corrected.visible && this.ap_interval > 0) { + distance = this.target_corrected.getDistanceTo(Game.Target.newFromLocation(this.source.x, this.source.y)) - 0.00001; + count = Math.ceil(distance / this.ap_interval); + } + + // Adjust object count to match + while (this.ap_indicators.length < count) { + let indicator = new Phaser.Image(this.battleview.game, 0, 0, "battle-arena-ap-indicator"); + indicator.anchor.set(0.5, 0.5); + this.battleview.arena.addChild(indicator); + this.ap_indicators.push(indicator); + } + while (this.ap_indicators.length > count) { + this.ap_indicators[this.ap_indicators.length - 1].destroy(); + this.ap_indicators.pop(); + } + + // Spread indicators + if (count > 0 && distance > 0) { + let dx = this.ap_interval * (this.target_corrected.x - this.source.x) / distance; + let dy = this.ap_interval * (this.target_corrected.y - this.source.y) / distance; + this.ap_indicators.forEach((indicator, index) => { + indicator.position.set(this.source.x + dx * index, this.source.y + dy * index); + }); } }