arena: Added auto-approach to bring in range for action
1
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)
|
||||
|
|
|
@ -235,18 +235,6 @@
|
|||
offset="1"
|
||||
id="stop10127" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient9717">
|
||||
<stop
|
||||
style="stop-color:#d1d4b2;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop9713" />
|
||||
<stop
|
||||
style="stop-color:#d1d4b2;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop9715" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient9609">
|
||||
|
@ -710,16 +698,6 @@
|
|||
x2="1512.2041"
|
||||
y2="877.88531"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient9717"
|
||||
id="radialGradient9719"
|
||||
cx="825.83337"
|
||||
cy="849.4455"
|
||||
fx="825.83337"
|
||||
fy="849.4455"
|
||||
r="22.5"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10129"
|
||||
|
@ -843,16 +821,6 @@
|
|||
operator="atop"
|
||||
result="composite2" />
|
||||
</filter>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient9717"
|
||||
id="radialGradient5025"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="825.83337"
|
||||
cy="849.4455"
|
||||
fx="825.83337"
|
||||
fy="849.4455"
|
||||
r="22.5" />
|
||||
<filter
|
||||
id="filter5041"
|
||||
inkscape:label="Color impossible"
|
||||
|
@ -1166,11 +1134,11 @@
|
|||
borderopacity="1"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.5"
|
||||
inkscape:cx="1132.2376"
|
||||
inkscape:cy="630.86578"
|
||||
inkscape:zoom="4"
|
||||
inkscape:cx="358.32238"
|
||||
inkscape:cy="992.57962"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer25"
|
||||
inkscape:current-layer="layer8"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
showguides="false"
|
||||
|
@ -1181,7 +1149,7 @@
|
|||
inkscape:object-nodes="true"
|
||||
inkscape:snap-intersection-paths="false"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:snap-global="false"
|
||||
inkscape:showpageshadow="false"
|
||||
showborder="true"
|
||||
borderlayer="true" />
|
||||
|
@ -1548,6 +1516,12 @@
|
|||
cx="1551.4003"
|
||||
cy="742.08289"
|
||||
r="31.144533" />
|
||||
<path
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:url(#linearGradient9611);stroke-width:5.89960623;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
|
||||
d="M 732.44478,877.88531 H 1512.2041"
|
||||
id="path9595"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
|
@ -1557,121 +1531,16 @@
|
|||
width="100%"
|
||||
height="100%"
|
||||
style="display:inline;opacity:0.58499995;enable-background:new" />
|
||||
<path
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:url(#linearGradient9611);stroke-width:5.89960623;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
|
||||
d="M 732.44478,877.88531 H 1512.2041"
|
||||
id="path9595"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="use9628"
|
||||
d="m 1133.0389,877.88531 -23.5347,9.28279 3.6495,-9.28279 -3.6495,-9.28279 z"
|
||||
inkscape:transform-center-x="-2.67976"
|
||||
style="display:inline;opacity:1;fill:#362c20;fill-opacity:1;fill-rule:evenodd;stroke:#e09c47;stroke-width:2.43093753;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new" />
|
||||
<path
|
||||
style="display:inline;opacity:1;fill:#391b13;fill-opacity:1;fill-rule:evenodd;stroke:#dc6441;stroke-width:2.33156252;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
|
||||
inkscape:transform-center-x="-3.93566"
|
||||
d="m 1517.8308,877.88531 -34.5651,13.6335 5.3601,-13.6335 -5.3601,-13.6335 z"
|
||||
id="path10745"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<g
|
||||
style="display:inline;enable-background:new"
|
||||
id="g9681"
|
||||
transform="translate(-16.499158,28.439814)"
|
||||
inkscape:export-filename="/home/michael/workspace/perso/spacetac/out/assets/images/battle/arena/ap-indicator.png"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:export-filename="/tmp/export.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<circle
|
||||
r="22.5"
|
||||
cy="849.4455"
|
||||
cx="825.83337"
|
||||
id="path9677"
|
||||
style="opacity:1;fill:url(#radialGradient9719);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.33156252;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<use
|
||||
height="100%"
|
||||
width="100%"
|
||||
transform="translate(828.83521,989.66854)"
|
||||
id="use9656"
|
||||
xlink:href="#g4573"
|
||||
y="0"
|
||||
x="0" />
|
||||
</g>
|
||||
<use
|
||||
style="display:inline;enable-background:new"
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g9681"
|
||||
id="use9699"
|
||||
transform="translate(121.99958)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<use
|
||||
style="display:inline;enable-background:new"
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g9681"
|
||||
id="use9701"
|
||||
transform="translate(243.99916)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<use
|
||||
style="display:inline;enable-background:new"
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g9681"
|
||||
id="use9703"
|
||||
transform="translate(370.64181)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<use
|
||||
style="display:inline;enable-background:new"
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g9681"
|
||||
id="use9705"
|
||||
transform="translate(436.68086)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<use
|
||||
style="display:inline;enable-background:new"
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g9681"
|
||||
id="use9707"
|
||||
transform="translate(502.7198)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<use
|
||||
style="display:inline;enable-background:new"
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g9681"
|
||||
id="use9709"
|
||||
transform="translate(568.75877)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<g
|
||||
transform="translate(618.6426,28.439814)"
|
||||
id="g5021"
|
||||
style="display:inline;filter:url(#filter5041);enable-background:new">
|
||||
<circle
|
||||
style="opacity:1;fill:url(#radialGradient5025);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.33156252;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle5017"
|
||||
cx="825.83337"
|
||||
cy="849.4455"
|
||||
r="22.5" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g4573"
|
||||
id="use5019"
|
||||
transform="translate(828.83521,989.66854)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
</g>
|
||||
inkscape:export-ydpi="90" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
|
@ -2262,17 +2131,7 @@
|
|||
inkscape:export-ydpi="90"
|
||||
transform="matrix(0.98149613,0,0,1.5780874,-2.5439867,-65.79016)" />
|
||||
<path
|
||||
transform="matrix(0.98149613,0,0,1.5780874,52.760346,-65.79016)"
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/michael/workspace/perso/spacetac/out/assets/images/battle/power-using.png"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4925"
|
||||
d="m 197.49714,119.86752 h 51.83694 l 5.86374,-12.12184 h -51.83694 z"
|
||||
style="opacity:1;fill:#f8f0b5;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
transform="matrix(0.98149613,0,0,1.5780874,108.06471,-65.79016)"
|
||||
transform="matrix(0.98149613,0,0,1.5780874,160.43481,-65.79016)"
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/michael/workspace/perso/spacetac/out/assets/images/battle/power-used.png"
|
||||
|
@ -2281,6 +2140,26 @@
|
|||
id="path4927"
|
||||
d="m 197.49714,119.86752 h 51.83694 l 5.86374,-12.12184 h -51.83694 z"
|
||||
style="opacity:1;fill:#6b6443;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter5649)" />
|
||||
<path
|
||||
transform="matrix(0.98149613,0,0,1.5780874,51.782267,-65.79016)"
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/tmp/export.png"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5001"
|
||||
d="m 197.49714,119.86752 h 51.83694 l 5.86374,-12.12184 h -51.83694 z"
|
||||
style="opacity:1;fill:#e09c47;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter5649)" />
|
||||
<path
|
||||
transform="matrix(0.98149613,0,0,1.5780874,106.10853,-65.79016)"
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/tmp/export.png"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5003"
|
||||
d="m 197.49714,119.86752 h 51.83694 l 5.86374,-12.12184 h -51.83694 z"
|
||||
style="opacity:1;fill:#dc6441;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:16;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter5649)" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
|
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 127 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.5 KiB |
BIN
out/assets/images/battle/arena/indicators.png
Normal file
After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 12 KiB |
|
@ -25,6 +25,8 @@ module TS.SpaceTac {
|
|||
success = false
|
||||
// Ideal successive parts to make the full move+fire
|
||||
parts: MoveFirePart[] = []
|
||||
// Simulation complete (both move and fire are possible)
|
||||
complete = false
|
||||
|
||||
need_move = false
|
||||
can_move = false
|
||||
|
@ -74,7 +76,8 @@ module TS.SpaceTac {
|
|||
* Get an iterator for scanning a circle
|
||||
*/
|
||||
scanCircle(x: number, y: number, radius: number, nr = 6, na = 30): Iterator<Target> {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)]]);
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
||||
|
|
|
@ -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 = <Phaser.Image>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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)
|
||||
*/
|
||||
|
|
|
@ -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)
|
||||
*/
|
||||
|
|
|
@ -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]),
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|