diff --git a/TODO b/TODO
index 636391f..1635399 100644
--- a/TODO
+++ b/TODO
@@ -21,7 +21,6 @@
* Menu: allow to delete cloud saves
* Arena: display effects description instead of attribute changes
* Arena: display radius for area effects (both on action hover, and while action is active)
-* Arena: add auto-move to attack
* Arena: fix effects originating from real ship location instead of current sprite (when AI fires then moves)
* Arena: add engine trail
* Fix capacity limit effect not refreshing associated value (for example, on "limit power capacity to 3", potential "power" value change is not broadcast)
diff --git a/graphics/ui/battle.svg b/graphics/ui/battle.svg
index 4c89536..c872b4b 100644
--- a/graphics/ui/battle.svg
+++ b/graphics/ui/battle.svg
@@ -235,18 +235,6 @@
offset="1"
id="stop10127" />
-
-
-
-
@@ -710,16 +698,6 @@
x2="1512.2041"
y2="877.88531"
gradientUnits="userSpaceOnUse" />
-
-
@@ -1548,6 +1516,12 @@
cx="1551.4003"
cy="742.08289"
r="31.144533" />
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ inkscape:export-ydpi="90" />
-
+
+
{
- return ichainit(imap(istep(0, irepeat(nr ? 1 / (nr - 1) : 0, nr - 1)), r => {
+ let rcount = nr ? 1 / (nr - 1) : 0;
+ return ichainit(imap(istep(0, irepeat(rcount, nr - 1)), r => {
let angles = Math.max(1, Math.ceil(na * r));
return imap(istep(0, irepeat(2 * Math.PI / angles, angles - 1)), a => {
return new Target(x + r * radius * Math.cos(a), y + r * radius * Math.sin(a))
@@ -125,8 +128,10 @@ module TS.SpaceTac {
// Move or approach needed ?
let move_target: Target | null = null;
+ result.move_location = Target.newFromShip(this.ship);
if (action instanceof MoveAction) {
- let corrected_target = action.applyExclusion(this.ship, target);
+ let corrected_target = action.applyReachableRange(this.ship, target, move_margin);
+ corrected_target = action.applyExclusion(this.ship, corrected_target, move_margin);
if (corrected_target) {
result.need_move = target.getDistanceTo(this.ship.location) > 0;
move_target = corrected_target;
@@ -174,7 +179,9 @@ module TS.SpaceTac {
result.fire_location = target;
result.parts.push({ action: action, target: target, ap: result.total_fire_ap, possible: (!result.need_move || result.can_end_move) && result.can_fire });
}
+
result.success = true;
+ result.complete = (!result.need_move || result.can_end_move) && (!result.need_fire || result.can_fire);
return result;
}
diff --git a/src/core/actions/MoveAction.spec.ts b/src/core/actions/MoveAction.spec.ts
index 2987c8a..c1b7610 100644
--- a/src/core/actions/MoveAction.spec.ts
+++ b/src/core/actions/MoveAction.spec.ts
@@ -17,7 +17,7 @@ module TS.SpaceTac {
expect(result).toEqual(Target.newFromLocation(0, 2));
result = action.checkTarget(ship, Target.newFromLocation(0, 8));
- expect(result).toEqual(Target.newFromLocation(0, 3));
+ expect(result).toEqual(Target.newFromLocation(0, 2.9));
ship.values.power.set(0);
result = action.checkTarget(ship, Target.newFromLocation(0, 8));
diff --git a/src/core/actions/MoveAction.ts b/src/core/actions/MoveAction.ts
index 3c8fb0a..4b7dd73 100644
--- a/src/core/actions/MoveAction.ts
+++ b/src/core/actions/MoveAction.ts
@@ -57,7 +57,7 @@ module TS.SpaceTac {
/**
* Apply exclusion areas (neer arena borders, or other ships)
*/
- applyExclusion(ship: Ship, target: Target): Target {
+ applyExclusion(ship: Ship, target: Target, margin = 0.1): Target {
let battle = ship.getBattle();
if (battle) {
// Keep out of arena borders
@@ -76,14 +76,18 @@ module TS.SpaceTac {
return target;
}
+ /**
+ * Apply reachable range, with remaining power
+ */
+ applyReachableRange(ship: Ship, target: Target, margin = 0.1): Target {
+ let max_distance = this.getRangeRadius(ship);
+ max_distance = Math.max(0, max_distance - margin);
+ return target.constraintInRange(ship.arena_x, ship.arena_y, max_distance);
+ }
+
checkLocationTarget(ship: Ship, target: Target): Target {
- // Apply maximal distance
- var max_distance = this.getRangeRadius(ship);
- target = target.constraintInRange(ship.arena_x, ship.arena_y, max_distance);
-
- // Apply exclusion areas
+ target = this.applyReachableRange(ship, target);
target = this.applyExclusion(ship, target);
-
return target;
}
diff --git a/src/core/ai/Maneuver.spec.ts b/src/core/ai/Maneuver.spec.ts
index 189b958..c871ac5 100644
--- a/src/core/ai/Maneuver.spec.ts
+++ b/src/core/ai/Maneuver.spec.ts
@@ -42,7 +42,8 @@ module TS.SpaceTac.Specs {
it("guesses area effects on final location", function () {
let battle = new Battle();
let ship = battle.fleets[0].addShip();
- TestTools.addEngine(ship, 500);
+ let engine = TestTools.addEngine(ship, 500);
+ TestTools.setShipAP(ship, 10);
let drone = new Drone(ship);
drone.effects = [new AttributeEffect("maneuvrability", 1)];
drone.x = 100;
@@ -50,11 +51,11 @@ module TS.SpaceTac.Specs {
drone.radius = 50;
battle.addDrone(drone);
- let maneuver = new Maneuver(ship, new MoveAction(new Equipment()), Target.newFromLocation(40, 30));
+ let maneuver = new Maneuver(ship, engine.action, Target.newFromLocation(40, 30));
expect(maneuver.getFinalLocation()).toEqual(jasmine.objectContaining({ x: 40, y: 30 }));
expect(maneuver.effects).toEqual([]);
- maneuver = new Maneuver(ship, new MoveAction(new Equipment()), Target.newFromLocation(100, 30));
+ maneuver = new Maneuver(ship, engine.action, Target.newFromLocation(100, 30));
expect(maneuver.getFinalLocation()).toEqual(jasmine.objectContaining({ x: 100, y: 30 }));
expect(maneuver.effects).toEqual([[ship, new AttributeEffect("maneuvrability", 1)]]);
});
diff --git a/src/ui/BaseView.ts b/src/ui/BaseView.ts
index 6c1eb9d..999839c 100644
--- a/src/ui/BaseView.ts
+++ b/src/ui/BaseView.ts
@@ -50,6 +50,7 @@ module TS.SpaceTac.UI {
create() {
// Phaser config
this.game.stage.backgroundColor = 0x000000;
+ this.game.stage.disableVisibilityChange = this.gameui.headless;
this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
this.input.maxPointers = 1;
diff --git a/src/ui/Preload.ts b/src/ui/Preload.ts
index 2fb69fa..adb91c5 100644
--- a/src/ui/Preload.ts
+++ b/src/ui/Preload.ts
@@ -35,10 +35,10 @@ module TS.SpaceTac.UI {
this.loadImage("battle/actionbar/action-endturn.png");
this.loadSheet("battle/actionbar/button-menu.png", 79, 132);
this.loadImage("battle/arena/background.png");
- this.loadImage("battle/arena/ap-indicator.png");
this.loadImage("battle/arena/blast.png");
this.loadSheet("battle/arena/gauges.png", 19, 93);
this.loadSheet("battle/arena/small-indicators.png", 10, 10);
+ this.loadSheet("battle/arena/indicators.png", 64, 64);
this.loadSheet("battle/arena/ship-frames.png", 70, 70);
this.loadImage("battle/shiplist/background.png");
this.loadImage("battle/shiplist/item-background.png");
diff --git a/src/ui/battle/ActionBar.spec.ts b/src/ui/battle/ActionBar.spec.ts
index 7cf90f2..9067a46 100644
--- a/src/ui/battle/ActionBar.spec.ts
+++ b/src/ui/battle/ActionBar.spec.ts
@@ -49,51 +49,45 @@ module TS.SpaceTac.UI.Specs {
expect(bar.action_icons.length).toBe(4);
- var checkFading = (fading: number[], available: number[]) => {
+ var checkFading = (fading: number[], available: number[], message: string) => {
fading.forEach((index: number) => {
var icon = bar.action_icons[index];
- expect(icon.fading || !icon.active).toBe(true);
+ expect(icon.fading || !icon.active).toBe(true, `${message} - ${index} should be fading`);
});
available.forEach((index: number) => {
var icon = bar.action_icons[index];
- expect(icon.fading).toBe(false);
+ expect(icon.fading).toBe(false, `${message} - ${index} should be available`);
});
};
- // Weapon 1 leaves all choices open
- bar.action_icons[1].processClick();
- checkFading([], [0, 1, 2, 3]);
+ bar.updateSelectedActionPower(3, 0, bar.action_icons[1].action);
+ checkFading([], [0, 1, 2, 3], "Weapon 1 leaves all choices open");
bar.actionEnded();
- // Weapon 2 can't be fired twice
- bar.action_icons[2].processClick();
- checkFading([2], [0, 1, 3]);
+ bar.updateSelectedActionPower(5, 0, bar.action_icons[2].action);
+ checkFading([2], [0, 1, 3], "Weapon 2 can't be fired twice");
bar.actionEnded();
- // Not enough AP for both weapons
ship.setValue("power", 7);
- bar.action_icons[2].processClick();
- checkFading([1, 2], [0, 3]);
+ bar.updateSelectedActionPower(5, 0, bar.action_icons[2].action);
+ checkFading([1, 2], [0, 3], "Not enough AP for both weapons");
bar.actionEnded();
- // Not enough AP to move
ship.setValue("power", 3);
- bar.action_icons[1].processClick();
- checkFading([0, 1, 2], [3]);
+ bar.updateSelectedActionPower(3, 0, bar.action_icons[1].action);
+ checkFading([0, 1, 2], [3], "Not enough AP to move");
bar.actionEnded();
// Dynamic AP usage for move actions
ship.setValue("power", 6);
- bar.action_icons[0].processClick();
- checkFading([], [0, 1, 2, 3]);
- bar.action_icons[0].processHover(Target.newFromLocation(2, 8));
- checkFading([2], [0, 1, 3]);
- bar.action_icons[0].processHover(Target.newFromLocation(3, 8));
- checkFading([1, 2], [0, 3]);
- bar.action_icons[0].processHover(Target.newFromLocation(4, 8));
- checkFading([0, 1, 2], [3]);
- bar.action_icons[0].processHover(Target.newFromLocation(5, 8));
- checkFading([0, 1, 2], [3]);
+ bar.updateSelectedActionPower(2, 0, bar.action_icons[0].action);
+ checkFading([2], [0, 1, 3], "2 move power used");
+ bar.updateSelectedActionPower(4, 0, bar.action_icons[0].action);
+ checkFading([1, 2], [0, 3], "4 move power used");
+ bar.updateSelectedActionPower(6, 0, bar.action_icons[0].action);
+ checkFading([0, 1, 2], [3], "6 move power used");
+ bar.updateSelectedActionPower(8, 0, bar.action_icons[0].action);
+ checkFading([0, 1, 2], [3], "8 move power used");
bar.actionEnded();
});
diff --git a/src/ui/battle/ActionBar.ts b/src/ui/battle/ActionBar.ts
index b4c72c4..9ba5a9b 100644
--- a/src/ui/battle/ActionBar.ts
+++ b/src/ui/battle/ActionBar.ts
@@ -150,7 +150,7 @@ module TS.SpaceTac.UI {
/**
* Update the power indicator
*/
- updatePower(selected_action = 0): void {
+ updatePower(move_power = 0, fire_power = 0): void {
let current_power = this.power.children.length;
let power_capacity = this.ship_power_capacity;
@@ -162,14 +162,16 @@ module TS.SpaceTac.UI {
}
let power_value = this.ship_power_value;
- let remaining_power = power_value - selected_action;
+ let remaining_power = power_value - move_power - fire_power;
this.power.children.forEach((obj, idx) => {
let img = obj;
let frame: number;
if (idx < remaining_power) {
frame = 0;
- } else if (idx < power_value) {
+ } else if (idx < remaining_power + move_power) {
frame = 2;
+ } else if (idx < power_value) {
+ frame = 3;
} else {
frame = 1;
}
@@ -179,23 +181,34 @@ module TS.SpaceTac.UI {
}
/**
- * Set current action power usage.
+ * Temporarily set current action power usage.
*
* When an action is selected, this will fade the icons not available after the action would be done.
* This will also highlight power usage in the power bar.
*
- * *power_usage* is the consumption of currently selected action.
+ * *move_power* and *fire_power* is the consumption of currently selected action/target.
*/
- updateSelectedActionPower(power_usage: number, action: BaseAction): void {
- var remaining_ap = this.ship ? (this.ship.values.power.get() - power_usage) : 0;
+ updateSelectedActionPower(move_power: number, fire_power: number, action: BaseAction): void {
+ var remaining_ap = this.ship ? (this.ship.getValue("power") - move_power - fire_power) : 0;
if (remaining_ap < 0) {
remaining_ap = 0;
}
- this.action_icons.forEach((icon: ActionIcon) => {
+ this.action_icons.forEach(icon => {
icon.updateFadingStatus(remaining_ap, action);
});
- this.updatePower(power_usage);
+ this.updatePower(move_power, fire_power);
+ }
+
+ /**
+ * Temporarily set power status for a given move-fire simulation
+ */
+ updateFromSimulation(action: BaseAction, simulation: MoveFireResult) {
+ if (simulation.complete) {
+ this.updateSelectedActionPower(simulation.total_move_ap, simulation.total_fire_ap, action);
+ } else {
+ this.updateSelectedActionPower(0, 0, action);
+ }
}
/**
diff --git a/src/ui/battle/ActionIcon.ts b/src/ui/battle/ActionIcon.ts
index a00b1cf..362ce9c 100644
--- a/src/ui/battle/ActionIcon.ts
+++ b/src/ui/battle/ActionIcon.ts
@@ -40,7 +40,7 @@ module TS.SpaceTac.UI {
// Create an icon for a single ship action
constructor(bar: ActionBar, x: number, y: number, ship: Ship, action: BaseAction, position: number) {
- super(bar.game, x, y, "battle-actionbar-icon");
+ super(bar.game, x, y, "battle-actionbar-icon", () => this.processClick());
this.bar = bar;
this.battleview = bar.battleview;
@@ -83,19 +83,6 @@ module TS.SpaceTac.UI {
ActionTooltip.fill(filler, this.ship, this.action, position);
return true;
});
- UITools.setHoverClick(this,
- () => {
- if (!this.bar.hasActionSelected()) {
- this.battleview.arena.range_hint.update(this.ship, this.action);
- }
- },
- () => {
- if (!this.bar.hasActionSelected()) {
- this.battleview.arena.range_hint.clear();
- }
- },
- () => this.processClick()
- );
// Initialize
this.updateActiveStatus(true);
@@ -120,13 +107,10 @@ module TS.SpaceTac.UI {
this.bar.actionStarted();
// Update range hint
- if (this.battleview.arena.range_hint) {
+ if (this.battleview.arena.range_hint && this.action instanceof MoveAction) {
this.battleview.arena.range_hint.update(this.ship, this.action);
}
- // Update fading statuses
- this.bar.updateSelectedActionPower(this.action.getActionPointsUsage(this.ship, null), this.action);
-
// Set the selected state
this.setSelected(true);
@@ -134,15 +118,7 @@ module TS.SpaceTac.UI {
let sprite = this.battleview.arena.findShipSprite(this.ship);
if (sprite) {
// Switch to targetting mode (will apply action when a target is selected)
- this.targetting = this.battleview.enterTargettingMode();
- if (this.targetting) {
- this.targetting.setSource(sprite);
- this.targetting.targetSelected.add(this.processSelection, this);
- this.targetting.targetHovered.add(this.processHover, this);
- if (this.action instanceof MoveAction) {
- this.targetting.setApIndicatorsInterval(this.action.getDistanceByActionPoint(this.ship));
- }
- }
+ this.targetting = this.battleview.enterTargettingMode(this.action);
}
} else {
// No target needed, apply action immediately
@@ -150,16 +126,6 @@ module TS.SpaceTac.UI {
}
}
- // Called when a target is hovered
- // This will check the target against current action and adjust it if needed
- processHover(target: Target): void {
- let correct_target = this.action.checkTarget(this.ship, target);
- if (this.targetting) {
- this.targetting.setTarget(correct_target, false, this.action.getBlastRadius(this.ship));
- }
- this.bar.updateSelectedActionPower(this.action.getActionPointsUsage(this.ship, correct_target), this.action);
- }
-
// Called when a target is selected
processSelection(target: Target | null): void {
if (this.action.apply(this.ship, target)) {
diff --git a/src/ui/battle/Arena.ts b/src/ui/battle/Arena.ts
index 9fcbd07..1cbdaf9 100644
--- a/src/ui/battle/Arena.ts
+++ b/src/ui/battle/Arena.ts
@@ -67,6 +67,9 @@ module TS.SpaceTac.UI {
background.onInputUp.add(() => {
battleview.cursorClicked();
});
+ background.onInputOut.add(() => {
+ battleview.targetting.setTarget(null);
+ });
// Watch mouse move to capture hovering over background
this.input_callback = this.game.input.addMoveCallback((pointer: Phaser.Pointer) => {
@@ -234,13 +237,6 @@ module TS.SpaceTac.UI {
}
}
- /**
- * Highlight ships that would be the target of current action
- */
- highlightTargets(ships: Ship[]): void {
- this.ship_sprites.forEach(sprite => sprite.setTargetted(contains(ships, sprite.ship)));
- }
-
/**
* Switch the tactical mode (shows information on all ships, and fades background)
*/
diff --git a/src/ui/battle/ArenaShip.ts b/src/ui/battle/ArenaShip.ts
index 10f0770..62632af 100644
--- a/src/ui/battle/ArenaShip.ts
+++ b/src/ui/battle/ArenaShip.ts
@@ -17,9 +17,6 @@ module TS.SpaceTac.UI {
// Statis effect
stasis: Phaser.Image
- // Target effect
- target: Phaser.Image
-
// HSP display
hull: ValueBar
toggle_hull: Toggle
@@ -53,7 +50,7 @@ module TS.SpaceTac.UI {
this.sprite = new Phaser.Button(this.game, 0, 0, "ship-" + ship.model.code + "-sprite");
this.sprite.rotation = ship.arena_angle;
this.sprite.anchor.set(0.5, 0.5);
- this.sprite.scale.set(64 / this.sprite.width);
+ this.sprite.scale.set(0.25);
this.add(this.sprite);
// Add stasis effect
@@ -62,12 +59,6 @@ module TS.SpaceTac.UI {
this.stasis.visible = false;
this.add(this.stasis);
- // Add target effect
- this.target = new Phaser.Image(this.game, 0, 0, "battle-arena-ship-frames", 5);
- this.target.anchor.set(0.5, 0.5);
- this.target.visible = false;
- this.add(this.target);
-
// Add playing effect
this.frame = new Phaser.Image(this.game, 0, 0, "battle-arena-ship-frames", this.enemy ? 0 : 1);
this.frame.anchor.set(0.5, 0.5);
@@ -202,15 +193,6 @@ module TS.SpaceTac.UI {
this.frame.frame = (playing ? 3 : 0) + (this.enemy ? 0 : 1);
}
- /**
- * Set the ship as target of current action
- *
- * This will toggle the visibility of target indicator
- */
- setTargetted(targetted: boolean): void {
- this.target.visible = targetted;
- }
-
/**
* Activate the dead effect (stasis)
*/
diff --git a/src/ui/battle/BattleView.spec.ts b/src/ui/battle/BattleView.spec.ts
index 2960e94..0b2feba 100644
--- a/src/ui/battle/BattleView.spec.ts
+++ b/src/ui/battle/BattleView.spec.ts
@@ -6,34 +6,26 @@ module TS.SpaceTac.UI.Specs {
it("forwards events in targetting mode", function () {
let battleview = testgame.battleview;
- expect(battleview.targetting).toBeNull();
+ expect(battleview.targetting.active).toBe(false);
battleview.setInteractionEnabled(true);
+ spyOn(battleview.targetting, "validate").and.stub();
+
battleview.cursorInSpace(5, 5);
- expect(battleview.targetting).toBeNull();
+ expect(battleview.targetting.active).toBe(false);
// Enter targetting mode
- var result = nn(battleview.enterTargettingMode());
+ let weapon = TestTools.addWeapon(nn(battleview.battle.playing_ship), 10);
+ battleview.enterTargettingMode(weapon.action);
- expect(battleview.targetting).toBeTruthy();
- expect(result).toBe(nn(battleview.targetting));
-
- // Collect targetting events
- var hovered: (Target | null)[] = [];
- var clicked: Target[] = [];
- result.targetHovered.add((target: Target) => {
- hovered.push(target);
- });
- result.targetSelected.add((target: Target) => {
- clicked.push(target);
- });
+ expect(battleview.targetting.active).toBe(true);
// Forward selection in space
battleview.cursorInSpace(8, 4);
expect(battleview.ship_hovered).toBeNull();
- expect(nn(battleview.targetting).target_corrected).toEqual(Target.newFromLocation(8, 4));
+ expect(battleview.targetting.target).toEqual(Target.newFromLocation(8, 4));
// Process a click on space
battleview.cursorClicked();
@@ -42,19 +34,19 @@ module TS.SpaceTac.UI.Specs {
battleview.cursorOnShip(battleview.battle.play_order[0]);
expect(battleview.ship_hovered).toEqual(battleview.battle.play_order[0]);
- expect(nn(battleview.targetting).target_corrected).toEqual(Target.newFromShip(battleview.battle.play_order[0]));
+ expect(battleview.targetting.target).toEqual(Target.newFromShip(battleview.battle.play_order[0]));
// Don't leave a ship we're not hovering
battleview.cursorOffShip(battleview.battle.play_order[1]);
expect(battleview.ship_hovered).toEqual(battleview.battle.play_order[0]);
- expect(nn(battleview.targetting).target_corrected).toEqual(Target.newFromShip(battleview.battle.play_order[0]));
+ expect(battleview.targetting.target).toEqual(Target.newFromShip(battleview.battle.play_order[0]));
// Don't move in space while on ship
battleview.cursorInSpace(1, 3);
expect(battleview.ship_hovered).toEqual(battleview.battle.play_order[0]);
- expect(nn(battleview.targetting).target_corrected).toEqual(Target.newFromShip(battleview.battle.play_order[0]));
+ expect(battleview.targetting.target).toEqual(Target.newFromShip(battleview.battle.play_order[0]));
// Process a click on ship
battleview.cursorClicked();
@@ -63,12 +55,12 @@ module TS.SpaceTac.UI.Specs {
battleview.cursorOffShip(battleview.battle.play_order[0]);
expect(battleview.ship_hovered).toBeNull();
- expect(nn(battleview.targetting).target_corrected).toBeNull();
+ expect(battleview.targetting.target).toBeNull();
// Quit targetting
battleview.exitTargettingMode();
- expect(battleview.targetting).toBeNull();
+ expect(battleview.targetting.active).toBe(false);
// Events process normally
battleview.cursorInSpace(8, 4);
@@ -78,17 +70,6 @@ module TS.SpaceTac.UI.Specs {
// Quit twice don't do anything
battleview.exitTargettingMode();
-
- // Check collected targetting events
- expect(hovered).toEqual([
- Target.newFromLocation(8, 4),
- Target.newFromShip(battleview.battle.play_order[0]),
- null
- ]);
- expect(clicked).toEqual([
- Target.newFromLocation(8, 4),
- Target.newFromShip(battleview.battle.play_order[0]),
- ]);
});
});
}
diff --git a/src/ui/battle/BattleView.ts b/src/ui/battle/BattleView.ts
index aec6768..ea299c9 100644
--- a/src/ui/battle/BattleView.ts
+++ b/src/ui/battle/BattleView.ts
@@ -3,56 +3,55 @@
module TS.SpaceTac.UI {
// Interactive view of a Battle
export class BattleView extends BaseView {
-
// Displayed battle
- battle: Battle;
+ battle: Battle
// Interacting player
- player: Player;
+ player: Player
// Layers
- layer_background: Phaser.Group;
- layer_arena: Phaser.Group;
- layer_borders: Phaser.Group;
- layer_overlay: Phaser.Group;
- layer_dialogs: Phaser.Group;
- layer_sheets: Phaser.Group;
+ layer_background: Phaser.Group
+ layer_arena: Phaser.Group
+ layer_borders: Phaser.Group
+ layer_overlay: Phaser.Group
+ layer_dialogs: Phaser.Group
+ layer_sheets: Phaser.Group
// Battleground container
- arena: Arena;
+ arena: Arena
// Background image
- background: Phaser.Image | null;
+ background: Phaser.Image | null
// Targetting mode (null if we're not in this mode)
- targetting: Targetting | null;
+ targetting: Targetting
// Ship list
- ship_list: ShipList;
+ ship_list: ShipList
// Action bar
- action_bar: ActionBar;
+ action_bar: ActionBar
// Currently hovered ship
- ship_hovered: Ship | null;
+ ship_hovered: Ship | null
// Ship tooltip
- ship_tooltip: ShipTooltip;
+ ship_tooltip: ShipTooltip
// Outcome dialog layer
- outcome_layer: Phaser.Group;
+ outcome_layer: Phaser.Group
// Character sheet
- character_sheet: CharacterSheet;
+ character_sheet: CharacterSheet
// Subscription to the battle log
- log_processor: LogProcessor;
+ log_processor: LogProcessor
// True if player interaction is allowed
- interacting: boolean;
+ interacting: boolean
// Tactical mode toggle
- toggle_tactical_mode: Toggle;
+ toggle_tactical_mode: Toggle
// Init the view, binding it to a specific battle
init(player: Player, battle: Battle) {
@@ -60,7 +59,6 @@ module TS.SpaceTac.UI {
this.player = player;
this.battle = battle;
- this.targetting = null;
this.ship_hovered = null;
this.background = null;
@@ -104,6 +102,10 @@ module TS.SpaceTac.UI {
this.character_sheet = new CharacterSheet(this, -this.getWidth());
this.layer_sheets.add(this.character_sheet);
+ // Targetting info
+ this.targetting = new Targetting(this, this.action_bar);
+ this.targetting.moveToLayer(this.arena.layer_targetting);
+
// "Battle" animation
this.displayFightMessage();
@@ -150,8 +152,6 @@ module TS.SpaceTac.UI {
// Leaving the view, we unbind the battle
shutdown() {
- this.exitTargettingMode();
-
this.log_processor.destroy();
super.shutdown();
@@ -172,7 +172,7 @@ module TS.SpaceTac.UI {
// Method called when cursor starts hovering over a ship (or its icon)
cursorOnShip(ship: Ship): void {
- if (!this.targetting || ship.alive) {
+ if (!this.targetting.active || ship.alive) {
this.setShipHovered(ship);
}
}
@@ -187,15 +187,15 @@ module TS.SpaceTac.UI {
// Method called when cursor moves in space
cursorInSpace(x: number, y: number): void {
if (!this.ship_hovered) {
- if (this.targetting) {
- this.targetting.setTargetSpace(x, y);
+ if (this.targetting.active) {
+ this.targetting.setTarget(Target.newFromLocation(x, y));
}
}
}
// Method called when cursor has been clicked (in space or on a ship)
cursorClicked(): void {
- if (this.targetting) {
+ if (this.targetting.active) {
this.targetting.validate();
} else if (this.ship_hovered && this.ship_hovered.getPlayer() == this.player && this.interacting) {
this.character_sheet.show(this.ship_hovered);
@@ -215,11 +215,11 @@ module TS.SpaceTac.UI {
this.ship_tooltip.hide();
}
- if (this.targetting) {
+ if (this.targetting.active) {
if (ship) {
- this.targetting.setTargetShip(ship);
+ this.targetting.setTarget(Target.newFromShip(ship));
} else {
- this.targetting.unsetTarget();
+ this.targetting.setTarget(null);
}
}
}
@@ -240,25 +240,18 @@ module TS.SpaceTac.UI {
// Enter targetting mode
// While in this mode, the Targetting object will receive hover and click events, and handle them
- enterTargettingMode(): Targetting | null {
+ enterTargettingMode(action: BaseAction): Targetting | null {
if (!this.interacting) {
return null;
}
- if (this.targetting) {
- this.exitTargettingMode();
- }
-
- this.targetting = new Targetting(this);
+ this.targetting.setAction(action);
return this.targetting;
}
// Exit targetting mode
exitTargettingMode(): void {
- if (this.targetting) {
- this.targetting.destroy();
- }
- this.targetting = null;
+ this.targetting.setAction(null);
}
/**
diff --git a/src/ui/battle/RangeHint.ts b/src/ui/battle/RangeHint.ts
index 8c16365..01576ec 100644
--- a/src/ui/battle/RangeHint.ts
+++ b/src/ui/battle/RangeHint.ts
@@ -43,7 +43,7 @@ module TS.SpaceTac.UI {
/**
* Update displayed information
*/
- update(ship: Ship, action: BaseAction): void {
+ update(ship: Ship, action: BaseAction, location: ArenaLocation = ship.location): void {
let yescolor = 0x000000;
let nocolor = 0x242022;
this.info.clear();
@@ -54,7 +54,7 @@ module TS.SpaceTac.UI {
this.info.drawRect(0, 0, this.width, this.height);
this.info.beginFill(yescolor);
- this.info.drawCircle(ship.arena_x, ship.arena_y, radius * 2);
+ this.info.drawCircle(location.x, location.y, radius * 2);
if (action instanceof MoveAction) {
let safety = action.safety_distance / 2;
diff --git a/src/ui/battle/Targetting.spec.ts b/src/ui/battle/Targetting.spec.ts
index 2fcdccf..2dbf998 100644
--- a/src/ui/battle/Targetting.spec.ts
+++ b/src/ui/battle/Targetting.spec.ts
@@ -2,68 +2,111 @@ module TS.SpaceTac.UI.Specs {
describe("Targetting", function () {
let testgame = setupBattleview();
- it("broadcasts hovering and selection events", function () {
- var targetting = new Targetting(null);
+ it("draws simulation parts", function () {
+ let targetting = new Targetting(testgame.battleview, testgame.battleview.action_bar);
- var hovered: Target[] = [];
- var selected: Target[] = [];
- targetting.targetHovered.add((target: Target) => {
- hovered.push(target);
- });
- targetting.targetSelected.add((target: Target) => {
- selected.push(target);
- });
+ let ship = nn(testgame.battleview.battle.playing_ship);
+ ship.setArenaPosition(10, 20);
+ let weapon = TestTools.addWeapon(ship);
+ let engine = TestTools.addEngine(ship, 12);
+ targetting.setAction(weapon.action);
- targetting.setTargetSpace(1, 2);
- expect(hovered).toEqual([Target.newFromLocation(1, 2)]);
- expect(selected).toEqual([]);
+ let drawvector = spyOn(targetting, "drawVector").and.stub();
- targetting.validate();
- expect(hovered).toEqual([Target.newFromLocation(1, 2)]);
- expect(selected).toEqual([Target.newFromLocation(1, 2)]);
+ let part = {
+ action: weapon.action,
+ target: new Target(50, 30),
+ ap: 5,
+ possible: true
+ };
+ targetting.drawPart(part, true, null);
+ expect(drawvector).toHaveBeenCalledTimes(1);
+ expect(drawvector).toHaveBeenCalledWith(0xdc6441, 10, 20, 50, 30, 0);
+
+ targetting.drawPart(part, false, null);
+ expect(drawvector).toHaveBeenCalledTimes(2);
+ expect(drawvector).toHaveBeenCalledWith(0x8e8e8e, 10, 20, 50, 30, 0);
+
+ targetting.setAction(engine.action);
+ part.action = engine.action;
+ targetting.drawPart(part, true, null);
+ expect(drawvector).toHaveBeenCalledTimes(3);
+ expect(drawvector).toHaveBeenCalledWith(0xe09c47, 10, 20, 50, 30, 12);
});
- it("displays action point indicators", function () {
- let battleview = testgame.battleview;
- let source = new Phaser.Group(battleview.game, battleview.arena);
- source.position.set(0, 0);
+ it("updates impact indicators on ships inside the blast radius", function () {
+ let targetting = new Targetting(testgame.battleview, testgame.battleview.action_bar);
+ let ship = nn(testgame.battleview.battle.playing_ship);
- let targetting = new Targetting(battleview);
+ let collect = spyOn(testgame.battleview.battle, "collectShipsInCircle").and.returnValues(
+ [new Ship(), new Ship(), new Ship()],
+ [new Ship(), new Ship()],
+ []);
+ targetting.updateImpactIndicators(ship, new Target(20, 10), 50);
- targetting.setSource(source);
- targetting.setTargetSpace(200, 100);
+ expect(collect).toHaveBeenCalledTimes(1);
+ expect(collect).toHaveBeenCalledWith(new Target(20, 10), 50, true);
+ expect(targetting.fire_impact.children.length).toBe(3);
+ expect(targetting.fire_impact.visible).toBe(true);
+
+ targetting.updateImpactIndicators(ship, new Target(20, 11), 50);
+
+ expect(collect).toHaveBeenCalledTimes(2);
+ expect(collect).toHaveBeenCalledWith(new Target(20, 11), 50, true);
+ expect(targetting.fire_impact.children.length).toBe(2);
+ expect(targetting.fire_impact.visible).toBe(true);
+
+ let target = Target.newFromShip(new Ship());
+ targetting.updateImpactIndicators(ship, target, 0);
+
+ expect(collect).toHaveBeenCalledTimes(2);
+ expect(targetting.fire_impact.children.length).toBe(1);
+ expect(targetting.fire_impact.visible).toBe(true);
+
+ targetting.updateImpactIndicators(ship, new Target(20, 12), 50);
+
+ expect(collect).toHaveBeenCalledTimes(3);
+ expect(collect).toHaveBeenCalledWith(new Target(20, 12), 50, true);
+ expect(targetting.fire_impact.visible).toBe(false);
+ });
+
+ it("updates graphics from simulation", function () {
+ let targetting = new Targetting(testgame.battleview, testgame.battleview.action_bar);
+ let ship = nn(testgame.battleview.battle.playing_ship);
+
+ let engine = TestTools.addEngine(ship, 8000);
+ let weapon = TestTools.addWeapon(ship, 30, 5, 100, 50);
+ targetting.setAction(weapon.action);
+ targetting.setTarget(Target.newFromLocation(156, 65));
+
+ spyOn(targetting, "simulate").and.callFake(() => {
+ let result = new MoveFireResult();
+ result.success = true;
+ result.complete = true;
+ result.need_move = true;
+ result.move_location = Target.newFromLocation(80, 20);
+ result.can_move = true;
+ result.can_end_move = true;
+ result.need_fire = true;
+ result.can_fire = true;
+ result.parts = [
+ { action: engine.action, target: Target.newFromLocation(80, 20), ap: 1, possible: true },
+ { action: weapon.action, target: Target.newFromLocation(156, 65), ap: 5, possible: true }
+ ]
+ targetting.simulation = result;
+ });
targetting.update();
- targetting.updateApIndicators();
- expect(targetting.ap_indicators.length).toBe(0);
- expect(battleview.arena.layer_targetting.children.length).toBe(3);
-
- targetting.setApIndicatorsInterval(Math.sqrt(5) * 20);
-
- expect(targetting.ap_indicators.length).toBe(5);
- expect(battleview.arena.layer_targetting.children.length).toBe(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.layer_targetting.children.length).toBe(3 + 1);
-
- targetting.setApIndicatorsInterval(1);
- expect(targetting.ap_indicators.length).toBe(224);
- expect(battleview.arena.layer_targetting.children.length).toBe(3 + 224);
-
- targetting.destroy();
-
- expect(battleview.arena.layer_targetting.children.length).toBe(0);
+ expect(targetting.container.visible).toBe(true);
+ expect(targetting.drawn_info.visible).toBe(true);
+ expect(targetting.fire_arrow.visible).toBe(true);
+ expect(targetting.fire_arrow.position).toEqual(jasmine.objectContaining({ x: 156, y: 65 }));
+ expect(targetting.fire_arrow.rotation).toBeCloseTo(0.534594, 5);
+ expect(targetting.fire_blast.visible).toBe(true);
+ expect(targetting.fire_blast.position).toEqual(jasmine.objectContaining({ x: 156, y: 65 }));
+ expect(targetting.move_ghost.visible).toBe(true);
+ expect(targetting.move_ghost.position).toEqual(jasmine.objectContaining({ x: 80, y: 20 }));
+ expect(targetting.move_ghost.rotation).toBeCloseTo(0.534594, 5);
});
});
}
diff --git a/src/ui/battle/Targetting.ts b/src/ui/battle/Targetting.ts
index 92b522d..a9cd838 100644
--- a/src/ui/battle/Targetting.ts
+++ b/src/ui/battle/Targetting.ts
@@ -1,199 +1,265 @@
module TS.SpaceTac.UI {
- // Targetting system
- // Allows to pick a target for an action
+ /**
+ * Targetting system on the arena
+ *
+ * This system handles choosing a target for currently selected action, and displays a visual aid.
+ */
export class Targetting {
- // Initial target (as pointed by the user)
- target_initial: Target | null;
- line_initial: Phaser.Graphics;
+ // Container group
+ container: Phaser.Group
- // Corrected target (applying action rules)
- target_corrected: Target | null;
- line_corrected: Phaser.Graphics;
+ // Current action
+ ship: Ship | null = null
+ action: BaseAction | null = null
+ target: Target | null = null
+ simulation = new MoveFireResult()
- // Circle for effect radius
- blast_radius: number;
- blast: Phaser.Image;
+ // Movement projector
+ drawn_info: Phaser.Graphics
+ move_ghost: Phaser.Image
- // Signal to receive hovering events
- targetHovered: Phaser.Signal;
+ // Fire projector
+ fire_arrow: Phaser.Image
+ fire_blast: Phaser.Image
+ fire_impact: Phaser.Group
- // Signal to receive targetting events
- targetSelected: Phaser.Signal;
+ // Collaborators to update
+ actionbar: ActionBar
- // AP usage display
- ap_interval: number = 0;
- ap_indicators: Phaser.Image[] = [];
+ // Access to the parent view
+ view: BaseView
- // Access to the parent battle view
- private battleview: BattleView | null;
+ constructor(view: BaseView, actionbar: ActionBar) {
+ this.view = view;
+ this.actionbar = actionbar;
- // Source of the targetting
- private source: PIXI.DisplayObject | null;
-
- // Create a default targetting mode
- constructor(battleview: BattleView | null) {
- this.battleview = battleview;
- this.targetHovered = new Phaser.Signal();
- this.targetSelected = new Phaser.Signal();
+ this.container = view.add.group();
// Visual effects
- if (battleview) {
- this.blast = new Phaser.Image(battleview.game, 0, 0, "battle-arena-blast");
- this.blast.anchor.set(0.5, 0.5);
- this.blast.visible = false;
- battleview.arena.layer_targetting.add(this.blast);
- this.line_initial = new Phaser.Graphics(battleview.game, 0, 0);
- this.line_initial.visible = false;
- battleview.arena.layer_targetting.add(this.line_initial);
- this.line_corrected = new Phaser.Graphics(battleview.game, 0, 0);
- this.line_corrected.visible = false;
- battleview.arena.layer_targetting.add(this.line_corrected);
- }
+ this.drawn_info = new Phaser.Graphics(view.game, 0, 0);
+ this.drawn_info.visible = false;
+ this.move_ghost = new Phaser.Image(view.game, 0, 0, "common-transparent");
+ this.move_ghost.anchor.set(0.5, 0.5);
+ this.move_ghost.alpha = 0.8;
+ this.move_ghost.visible = false;
+ this.fire_arrow = new Phaser.Image(view.game, 0, 0, "battle-arena-indicators", 0);
+ this.fire_arrow.anchor.set(1, 0.5);
+ this.fire_arrow.visible = false;
+ this.fire_impact = new Phaser.Group(view.game);
+ this.fire_impact.visible = false;
+ this.fire_blast = new Phaser.Image(view.game, 0, 0, "battle-arena-blast");
+ this.fire_blast.anchor.set(0.5, 0.5);
+ this.fire_blast.visible = false;
- this.source = null;
- this.target_initial = null;
- this.target_corrected = null;
+ this.container.add(this.fire_impact);
+ this.container.add(this.fire_blast);
+ this.container.add(this.drawn_info);
+ this.container.add(this.fire_arrow);
+ this.container.add(this.move_ghost);
}
- // Destructor
- destroy(): void {
- this.targetHovered.dispose();
- this.targetSelected.dispose();
- if (this.line_initial) {
- this.line_initial.destroy();
- }
- if (this.line_corrected) {
- this.line_corrected.destroy();
- }
- if (this.blast) {
- this.blast.destroy();
- }
- this.ap_indicators.forEach(indicator => indicator.destroy());
- if (this.battleview) {
- this.battleview.arena.highlightTargets([]);
- }
+ /**
+ * Move to a given view layer
+ */
+ moveToLayer(layer: Phaser.Group): void {
+ layer.add(this.container);
}
- // Set AP indicators to display at fixed interval along the line
- setApIndicatorsInterval(interval: number) {
- this.ap_interval = interval;
- this.updateApIndicators();
+ /**
+ * Indicator that the targetting is currently active
+ */
+ get active(): boolean {
+ return (this.ship && this.action) ? true : false;
}
- // Update visual effects for current targetting
- update(): void {
- if (this.battleview) {
- if (this.source && this.target_initial) {
- this.line_initial.clear();
- this.line_initial.lineStyle(3, 0x666666);
- this.line_initial.moveTo(this.source.x, this.source.y);
- this.line_initial.lineTo(this.target_initial.x, this.target_initial.y);
- this.line_initial.visible = true;
- } else {
- this.line_initial.visible = false;
+ /**
+ * Draw a vector, with line and gradation
+ */
+ drawVector(color: number, x1: number, y1: number, x2: number, y2: number, gradation = 0) {
+ let line = this.drawn_info;
+ line.lineStyle(6, color);
+ line.moveTo(x1, y1);
+ line.lineTo(x2, y2);
+ line.visible = true;
+
+ if (gradation) {
+ let dx = x2 - x1;
+ let dy = y2 - y1;
+ let dist = Math.sqrt(dx * dx + dy * dy);
+ let angle = Math.atan2(dy, dx);
+ dx = Math.cos(angle);
+ dy = Math.sin(angle);
+ for (let d = gradation; d <= dist; d += gradation) {
+ line.moveTo(x1 + dx * d + dy * 10, y1 + dy * d - dx * 10);
+ line.lineTo(x1 + dx * d - dy * 10, y1 + dy * d + dx * 10);
}
-
- if (this.source && this.target_corrected) {
- this.line_corrected.clear();
- this.line_corrected.lineStyle(6, this.ap_interval ? 0xe09c47 : 0xDC6441);
- this.line_corrected.moveTo(this.source.x, this.source.y);
- this.line_corrected.lineTo(this.target_corrected.x, this.target_corrected.y);
- this.line_corrected.visible = true;
- } else {
- this.line_corrected.visible = false;
- }
-
- if (this.target_corrected && this.blast_radius) {
- this.blast.position.set(this.target_corrected.x, this.target_corrected.y);
- this.blast.scale.set(this.blast_radius * 2 / 365);
- this.blast.visible = true;
-
- let targets = this.battleview.battle.collectShipsInCircle(this.target_corrected, this.blast_radius, true);
- this.battleview.arena.highlightTargets(targets);
- } else {
- this.blast.visible = false;
-
- this.battleview.arena.highlightTargets(this.target_corrected && this.target_corrected.ship ? [this.target_corrected.ship] : []);
- }
-
- this.updateApIndicators();
}
}
- // Update the AP indicators display
- updateApIndicators() {
- if (!this.battleview || !this.source) {
+ /**
+ * Draw a part of the simulation
+ */
+ drawPart(part: MoveFirePart, enabled = true, previous: MoveFirePart | null = null): void {
+ if (!this.ship) {
return;
}
- // Get indicator count
- let count = 0;
- let distance = 0;
- if (this.line_corrected.visible && this.ap_interval > 0 && this.target_corrected) {
- distance = this.target_corrected.getDistanceTo(Target.newFromLocation(this.source.x, this.source.y)) - 0.00001;
- count = Math.ceil(distance / this.ap_interval);
+ let move = part.action instanceof MoveAction;
+ let color = (enabled && part.possible) ? (move ? 0xe09c47 : 0xdc6441) : 0x8e8e8e;
+ let src = previous ? previous.target : this.ship.location;
+ let gradation = part.action instanceof MoveAction ? part.action.distance_per_power : 0;
+ this.drawVector(color, src.x, src.y, part.target.x, part.target.y, gradation);
+ }
+
+ /**
+ * Update impact indicators
+ */
+ updateImpactIndicators(ship: Ship, target: Target, radius: number): void {
+ let ships: Ship[];
+ if (radius) {
+ let battle = ship.getBattle();
+ if (battle) {
+ ships = battle.collectShipsInCircle(target, radius, true);
+ } else {
+ ships = [];
+ }
+ } else {
+ ships = target.ship ? [target.ship] : [];
}
- // 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.layer_targetting.add(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 && this.target_corrected) {
- let source = this.source;
- let dx = this.ap_interval * (this.target_corrected.x - source.x) / distance;
- let dy = this.ap_interval * (this.target_corrected.y - source.y) / distance;
- this.ap_indicators.forEach((indicator, index) => {
- indicator.position.set(source.x + dx * index, source.y + dy * index);
+ if (ships.length) {
+ this.fire_impact.removeAll(true);
+ ships.forEach(iship => {
+ let frame = this.view.add.image(iship.arena_x, iship.arena_y, "battle-arena-ship-frames", 5, this.fire_impact);
+ frame.anchor.set(0.5);
});
+ this.fire_impact.visible = true;
+ } else {
+ this.fire_impact.visible = false;
}
}
- // Set the source sprite for the targetting (for visual effects)
- setSource(sprite: PIXI.DisplayObject) {
- this.source = sprite;
+ /**
+ * Update visual effects to show the simulation of current action/target
+ */
+ update(): void {
+ this.simulate();
+ if (this.ship && this.action && this.target) {
+ let simulation = this.simulation;
+
+ this.drawn_info.clear();
+ this.fire_arrow.visible = false;
+ this.move_ghost.visible = false;
+
+ if (simulation.success) {
+ let previous: MoveFirePart | null = null;
+ simulation.parts.forEach(part => {
+ this.drawPart(part, simulation.complete, previous);
+ previous = part;
+ });
+ this.fire_arrow.frame = simulation.complete ? 0 : 1;
+
+ let from = simulation.need_fire ? simulation.move_location : this.ship.location;
+ let angle = Math.atan2(this.target.y - from.y, this.target.x - from.x);
+
+ if (simulation.need_move) {
+ this.move_ghost.visible = true;
+ this.move_ghost.position.set(simulation.move_location.x, simulation.move_location.y);
+ this.move_ghost.rotation = angle;
+ } else {
+ this.move_ghost.visible = false;
+ }
+
+ if (simulation.need_fire) {
+ let blast = this.action.getBlastRadius(this.ship);
+ if (blast) {
+ this.fire_blast.position.set(this.target.x, this.target.y);
+ this.fire_blast.scale.set(blast * 2 / 365);
+ this.fire_blast.alpha = simulation.can_fire ? 1 : 0.5;
+ this.fire_blast.visible = true;
+ } else {
+ this.fire_blast.visible = false;
+ }
+ this.updateImpactIndicators(this.ship, this.target, blast);
+
+ this.fire_arrow.position.set(this.target.x, this.target.y);
+ this.fire_arrow.rotation = angle;
+ this.fire_arrow.frame = simulation.complete ? 0 : 1;
+ this.fire_arrow.visible = true;
+ } else {
+ this.fire_blast.visible = false;
+ this.fire_impact.visible = false;
+ this.fire_arrow.visible = false;
+ }
+
+ this.container.visible = true;
+ } else {
+ // TODO Display error
+ this.container.visible = false;
+ }
+ } else {
+ this.container.visible = false;
+ }
}
- // Set a target from a target object
- setTarget(target: Target | null, dispatch: boolean = true, blast_radius: number = 0): void {
- this.target_corrected = target;
- this.blast_radius = blast_radius;
- if (dispatch) {
- this.target_initial = target ? copy(target) : null;
- this.targetHovered.dispatch(this.target_corrected);
+ /**
+ * Simulate current action
+ */
+ simulate(): void {
+ if (this.ship && this.action && this.target) {
+ let simulator = new MoveFireSimulator(this.ship);
+ this.simulation = simulator.simulateAction(this.action, this.target, 1);
+ } else {
+ this.simulation = new MoveFireResult();
}
+ }
+
+ /**
+ * Set the current targetting action, or null to stop targetting
+ */
+ setAction(action: BaseAction | null): void {
+ if (action && action.equipment && action.equipment.attached_to && action.equipment.attached_to.ship) {
+ this.ship = action.equipment.attached_to.ship;
+ this.action = action;
+
+ this.move_ghost.loadTexture(`ship-${this.ship.model.code}-sprite`);
+ this.move_ghost.scale.set(0.25);
+ } else {
+ this.ship = null;
+ this.action = null;
+ }
+ this.target = null;
this.update();
}
- // Set no target
- unsetTarget(dispatch: boolean = true): void {
- this.setTarget(null, dispatch);
- }
-
- // Set the current target ship (when hovered)
- setTargetShip(ship: Ship, dispatch: boolean = true): void {
- if (ship.alive) {
- this.setTarget(Target.newFromShip(ship), dispatch);
+ /**
+ * Set the target for current action
+ */
+ setTarget(target: Target | null): void {
+ this.target = target;
+ this.update();
+ if (this.action) {
+ this.actionbar.updateFromSimulation(this.action, this.simulation);
}
}
- // Set the current target in space (when hovered)
- setTargetSpace(x: number, y: number, dispatch: boolean = true): void {
- this.setTarget(Target.newFromLocation(x, y));
- }
-
- // Validate the current target (when clicked)
- // This will broadcast the targetSelected signal
+ /**
+ * Validate the current target.
+ *
+ * This will make the needed approach and apply the action.
+ */
validate(): void {
- this.targetSelected.dispatch(this.target_corrected);
+ this.simulate();
+
+ if (this.ship && this.simulation.complete) {
+ let ship = this.ship;
+ this.simulation.parts.forEach(part => {
+ if (part.possible) {
+ part.action.apply(ship, part.target);
+ }
+ });
+ this.actionbar.actionEnded();
+ }
}
}
}