map: Added sound and effect for jumps between systems
This commit is contained in:
parent
21865feafa
commit
9fcea58fc9
2
TODO.md
2
TODO.md
|
@ -23,13 +23,13 @@ Menu/settings/saves
|
||||||
Map/story
|
Map/story
|
||||||
---------
|
---------
|
||||||
|
|
||||||
* Add sound effects and more visual effects (jumps...)
|
|
||||||
* Add factions and reputation
|
* Add factions and reputation
|
||||||
* Allow to cancel secondary missions
|
* Allow to cancel secondary missions
|
||||||
* Forbid to end up with more than 5 ships in the fleet because of escorts
|
* Forbid to end up with more than 5 ships in the fleet because of escorts
|
||||||
* Fix problems when several dialogs are active at the same time
|
* Fix problems when several dialogs are active at the same time
|
||||||
* Add a zoom level, to see the location only
|
* Add a zoom level, to see the location only
|
||||||
* Restore the progressive text effect
|
* Restore the progressive text effect
|
||||||
|
* Improve performance when refreshing (and thus during jumps)
|
||||||
|
|
||||||
Character sheet
|
Character sheet
|
||||||
---------------
|
---------------
|
||||||
|
|
BIN
data/stage3/image/map/jump-effect.png
Normal file
BIN
data/stage3/image/map/jump-effect.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
BIN
data/stage3/sound/map/warp-in.wav
Normal file
BIN
data/stage3/sound/map/warp-in.wav
Normal file
Binary file not shown.
BIN
data/stage3/sound/map/warp-out.wav
Normal file
BIN
data/stage3/sound/map/warp-out.wav
Normal file
Binary file not shown.
|
@ -20,6 +20,42 @@
|
||||||
enable-background="new">
|
enable-background="new">
|
||||||
<defs
|
<defs
|
||||||
id="defs2">
|
id="defs2">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient1898">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#bfd0d1;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop1894" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#495757;stop-opacity:0.98507464"
|
||||||
|
offset="1"
|
||||||
|
id="stop1896" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient1774">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#97a3a4;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop1770" />
|
||||||
|
<stop
|
||||||
|
id="stop1784"
|
||||||
|
offset="0.05601373"
|
||||||
|
style="stop-color:#c9d0d1;stop-opacity:0.99607843" />
|
||||||
|
<stop
|
||||||
|
id="stop1782"
|
||||||
|
offset="0.49102494"
|
||||||
|
style="stop-color:#384a4c;stop-opacity:0.99215686" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#bfd0d1;stop-opacity:0.98823529"
|
||||||
|
offset="0.94595039"
|
||||||
|
id="stop1786" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#97a3a4;stop-opacity:0.9880597"
|
||||||
|
offset="1"
|
||||||
|
id="stop1772" />
|
||||||
|
</linearGradient>
|
||||||
<linearGradient
|
<linearGradient
|
||||||
inkscape:collect="always"
|
inkscape:collect="always"
|
||||||
id="linearGradient1765">
|
id="linearGradient1765">
|
||||||
|
@ -1121,6 +1157,76 @@
|
||||||
fx="342.45343"
|
fx="342.45343"
|
||||||
fy="142.875"
|
fy="142.875"
|
||||||
r="9.7939377" />
|
r="9.7939377" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient1774"
|
||||||
|
id="linearGradient1776"
|
||||||
|
x1="362.30035"
|
||||||
|
y1="284.25162"
|
||||||
|
x2="362.8378"
|
||||||
|
y2="347.94138"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(0,8.1204444)" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient1774"
|
||||||
|
id="linearGradient1866"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.1973483,0,0,1.0696619,-71.568108,-13.910532)"
|
||||||
|
x1="362.30035"
|
||||||
|
y1="284.25162"
|
||||||
|
x2="362.8378"
|
||||||
|
y2="347.94138" />
|
||||||
|
<filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
style="color-interpolation-filters:sRGB"
|
||||||
|
id="filter1876"
|
||||||
|
x="-0.078789305"
|
||||||
|
width="1.1575786"
|
||||||
|
y="-0.048446666"
|
||||||
|
height="1.0968933">
|
||||||
|
<feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="1.3719322"
|
||||||
|
id="feGaussianBlur1878" />
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
style="color-interpolation-filters:sRGB"
|
||||||
|
id="filter1888"
|
||||||
|
x="-0.023691724"
|
||||||
|
width="1.0473834"
|
||||||
|
y="-0.013014261"
|
||||||
|
height="1.0260285">
|
||||||
|
<feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.34454164"
|
||||||
|
id="feGaussianBlur1890" />
|
||||||
|
</filter>
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient1898"
|
||||||
|
id="radialGradient1900"
|
||||||
|
cx="362.64804"
|
||||||
|
cy="324.37646"
|
||||||
|
fx="362.64804"
|
||||||
|
fy="324.37646"
|
||||||
|
r="89.845749"
|
||||||
|
gradientTransform="matrix(1,0,0,0.13157894,0,281.69535)"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
style="color-interpolation-filters:sRGB"
|
||||||
|
id="filter1926"
|
||||||
|
x="-0.099126316"
|
||||||
|
width="1.1982526"
|
||||||
|
y="-0.75336001"
|
||||||
|
height="2.50672">
|
||||||
|
<feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="7.4217317"
|
||||||
|
id="feGaussianBlur1928" />
|
||||||
|
</filter>
|
||||||
</defs>
|
</defs>
|
||||||
<sodipodi:namedview
|
<sodipodi:namedview
|
||||||
id="base"
|
id="base"
|
||||||
|
@ -1129,11 +1235,11 @@
|
||||||
borderopacity="1.0"
|
borderopacity="1.0"
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="3.3234019"
|
inkscape:zoom="2.35"
|
||||||
inkscape:cx="855.45368"
|
inkscape:cx="1289.633"
|
||||||
inkscape:cy="455.29087"
|
inkscape:cy="-91.7479"
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
inkscape:current-layer="layer5"
|
inkscape:current-layer="g1933"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
units="px"
|
units="px"
|
||||||
inkscape:snap-bbox="true"
|
inkscape:snap-bbox="true"
|
||||||
|
@ -1644,6 +1750,32 @@
|
||||||
inkscape:export-filename="/home/michael/workspace/spacetac/data/stage3/image/map/location-planet-hover.png"
|
inkscape:export-filename="/home/michael/workspace/spacetac/data/stage3/image/map/location-planet-hover.png"
|
||||||
inkscape:export-xdpi="96"
|
inkscape:export-xdpi="96"
|
||||||
inkscape:export-ydpi="96" />
|
inkscape:export-ydpi="96" />
|
||||||
|
<g
|
||||||
|
id="g1933"
|
||||||
|
transform="matrix(0.4321442,0,0,0.4321442,205.93179,184.19907)"
|
||||||
|
inkscape:export-filename="/home/michael/workspace/spacetac/data/stage3/image/map/jump-effect.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96">
|
||||||
|
<path
|
||||||
|
style="opacity:0.756;fill:url(#linearGradient1866);fill-opacity:1;stroke:none;stroke-width:0.29943049;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.22985074;filter:url(#filter1876)"
|
||||||
|
d="m 341.75272,292.9497 c 13.45982,-3.15113 27.31495,-3.65375 41.79041,0 -26.10679,19.87768 -23.47344,40.49956 0,61.66109 -13.65748,5.35883 -27.62513,4.62167 -41.79041,0 23.43318,-20.5537 24.73093,-41.10739 0,-61.66109 z"
|
||||||
|
id="path1864"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="ccccc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="rect1695"
|
||||||
|
d="m 345.19681,294.99636 c 11.24137,-2.94592 22.81288,-3.4158 34.90248,0 -21.80385,18.58313 -19.60453,37.86201 0,57.64539 -11.40644,5.00984 -23.07193,4.32069 -34.90248,0 19.57091,-19.21513 20.65476,-38.43026 0,-57.64539 z"
|
||||||
|
style="opacity:1;fill:url(#linearGradient1776);fill-opacity:1;stroke:none;stroke-width:0.26458335;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.22985074;filter:url(#filter1888)" />
|
||||||
|
<ellipse
|
||||||
|
ry="11.821809"
|
||||||
|
rx="89.845749"
|
||||||
|
cy="324.37646"
|
||||||
|
cx="362.64804"
|
||||||
|
id="path1892"
|
||||||
|
style="opacity:0.756;fill:url(#radialGradient1900);fill-opacity:1;stroke:none;stroke-width:0.26458335;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.22985074;filter:url(#filter1926)" />
|
||||||
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<g
|
<g
|
||||||
inkscape:groupmode="layer"
|
inkscape:groupmode="layer"
|
||||||
|
|
Before Width: | Height: | Size: 413 KiB After Width: | Height: | Size: 418 KiB |
|
@ -75,14 +75,19 @@ module TK.SpaceTac.UI {
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
// Layers
|
// Layers
|
||||||
this.layers = this.add.container(0, 0);
|
this.layers = new UIContainer(this);
|
||||||
|
this.add.existing(this.layers);
|
||||||
this.layers.setName("View layers");
|
this.layers.setName("View layers");
|
||||||
this.dialogs_layer = this.add.container(0, 0);
|
this.dialogs_layer = new UIContainer(this);
|
||||||
this.dialogs_layer.setName("Dialogs layer");
|
this.dialogs_layer.setName("Dialogs layer");
|
||||||
this.tooltip_layer = this.add.container(0, 0);
|
this.add.existing(this.dialogs_layer);
|
||||||
|
this.tooltip_layer = new UIContainer(this);
|
||||||
this.tooltip_layer.setName("Tooltip layer");
|
this.tooltip_layer.setName("Tooltip layer");
|
||||||
|
this.add.existing(this.tooltip_layer);
|
||||||
this.tooltip = new Tooltip(this);
|
this.tooltip = new Tooltip(this);
|
||||||
this.messages_layer = this.add.container(0, 0);
|
this.messages_layer = new UIContainer(this);
|
||||||
|
this.messages_layer.setName("Messages layer");
|
||||||
|
this.add.existing(this.messages_layer);
|
||||||
this.messages = new Messages(this);
|
this.messages = new Messages(this);
|
||||||
this.dialogs_opened = [];
|
this.dialogs_opened = [];
|
||||||
|
|
||||||
|
|
|
@ -2,22 +2,37 @@
|
||||||
|
|
||||||
module TK.SpaceTac.UI.Specs {
|
module TK.SpaceTac.UI.Specs {
|
||||||
/**
|
/**
|
||||||
* Class to hold references to test objects (used as singleton in "describe" blocks)
|
* Class to hold references to test objects (used as singleton in "testing" blocks)
|
||||||
*
|
*
|
||||||
* Attributes should only be accessed from inside corresponding "it" blocks (they are initialized by the setup).
|
* Attributes should only be accessed from inside corresponding "test.case" blocks (they are initialized by the setup).
|
||||||
*/
|
*/
|
||||||
export class TestGame<T extends Phaser.Scene> {
|
export class TestGame<T extends Phaser.Scene> {
|
||||||
ui!: MainUI;
|
check!: TestContext
|
||||||
view!: T;
|
ui!: MainUI
|
||||||
multistorage!: Multi.FakeRemoteStorage;
|
view!: T
|
||||||
clock!: FakeClock;
|
multistorage!: Multi.FakeRemoteStorage
|
||||||
|
clock: FakeClock
|
||||||
|
time = 0
|
||||||
|
|
||||||
|
constructor(test: TestSuite) {
|
||||||
|
this.clock = test.clock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advance the time in the view and fake testing clock
|
||||||
|
*/
|
||||||
|
clockForward(milliseconds: number) {
|
||||||
|
this.time += milliseconds;
|
||||||
|
this.clock.forward(milliseconds);
|
||||||
|
this.ui.headlessStep(this.time, milliseconds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup a headless test UI, with a single view started.
|
* Setup a headless test UI, with a single view started.
|
||||||
*/
|
*/
|
||||||
export function setupSingleView<T extends Phaser.Scene & { create: Function }>(test: TestSuite, buildView: () => [T, object]) {
|
export function setupSingleView<T extends Phaser.Scene & { create: Function }>(test: TestSuite, buildView: () => [T, object]) {
|
||||||
let testgame = new TestGame<T>();
|
let testgame = new TestGame<T>(test);
|
||||||
|
|
||||||
test.asetup(() => new Promise((resolve, reject) => {
|
test.asetup(() => new Promise((resolve, reject) => {
|
||||||
let check = new TestContext(); // TODO Should be taken from test suite
|
let check = new TestContext(); // TODO Should be taken from test suite
|
||||||
|
@ -25,6 +40,7 @@ module TK.SpaceTac.UI.Specs {
|
||||||
check.patch(console, "warn", null);
|
check.patch(console, "warn", null);
|
||||||
|
|
||||||
testgame.ui = new MainUI(true);
|
testgame.ui = new MainUI(true);
|
||||||
|
testgame.check = check;
|
||||||
|
|
||||||
let [scene, scenedata] = buildView();
|
let [scene, scenedata] = buildView();
|
||||||
|
|
||||||
|
@ -133,6 +149,17 @@ module TK.SpaceTac.UI.Specs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check a simulation of a tweened property
|
||||||
|
*/
|
||||||
|
export function checkTween<T, P extends keyof T>(game: TestGame<any>, obj: T, property: P, expected: number[]): void {
|
||||||
|
let tweendata = game.view.animations.simulate(obj, property, expected.length);
|
||||||
|
game.check.equals(tweendata.length, expected.length, "number of points");
|
||||||
|
expected.forEach((value, idx) => {
|
||||||
|
game.check.nears(tweendata[idx], value, undefined, `point ${idx}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simulate a click on a button
|
* Simulate a click on a button
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,20 +1,12 @@
|
||||||
module TK.SpaceTac.UI.Specs {
|
module TK.SpaceTac.UI.Specs {
|
||||||
testing("WeaponEffect", test => {
|
testing("WeaponEffect", test => {
|
||||||
let testgame = setupBattleview(test);
|
let testgame = setupBattleview(test);
|
||||||
let clock = test.clock();
|
|
||||||
let t = 0;
|
|
||||||
|
|
||||||
function checkEmitters(step: string, expected: number) {
|
function checkEmitters(step: string, expected: number) {
|
||||||
test.check.same(testgame.view.arena.layer_weapon_effects.length, expected, `${step} - layer children`);
|
test.check.same(testgame.view.arena.layer_weapon_effects.length, expected, `${step} - layer children`);
|
||||||
//test.check.same(keys(testgame.view.particles.emitters).length, expected, `${step} - registered emitters`);
|
//test.check.same(keys(testgame.view.particles.emitters).length, expected, `${step} - registered emitters`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fastForward(milliseconds: number) {
|
|
||||||
t += milliseconds;
|
|
||||||
clock.forward(milliseconds);
|
|
||||||
testgame.ui.headlessStep(t, milliseconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
test.case("displays shield hit effect", check => {
|
test.case("displays shield hit effect", check => {
|
||||||
let battleview = testgame.view;
|
let battleview = testgame.view;
|
||||||
battleview.timer = new Timer();
|
battleview.timer = new Timer();
|
||||||
|
@ -25,7 +17,7 @@ module TK.SpaceTac.UI.Specs {
|
||||||
let layer = battleview.arena.layer_weapon_effects;
|
let layer = battleview.arena.layer_weapon_effects;
|
||||||
check.equals(layer.length, 1);
|
check.equals(layer.length, 1);
|
||||||
|
|
||||||
clock.forward(600);
|
testgame.clockForward(600);
|
||||||
check.equals(layer.length, 2);
|
check.equals(layer.length, 2);
|
||||||
|
|
||||||
let child = layer.list[0];
|
let child = layer.list[0];
|
||||||
|
@ -91,12 +83,12 @@ module TK.SpaceTac.UI.Specs {
|
||||||
|
|
||||||
effect.gunEffect();
|
effect.gunEffect();
|
||||||
checkEmitters("gun effect started", 1);
|
checkEmitters("gun effect started", 1);
|
||||||
fastForward(6000);
|
testgame.clockForward(6000);
|
||||||
checkEmitters("gun effect ended", 0);
|
checkEmitters("gun effect ended", 0);
|
||||||
|
|
||||||
effect.hullImpactEffect({ x: 0, y: 0 }, { x: 50, y: 50 }, 1000, 2000);
|
effect.hullImpactEffect({ x: 0, y: 0 }, { x: 50, y: 50 }, 1000, 2000);
|
||||||
checkEmitters("hull effect started", 1);
|
checkEmitters("hull effect started", 1);
|
||||||
fastForward(8500);
|
testgame.clockForward(8500);
|
||||||
checkEmitters("hull effect ended", 0);
|
checkEmitters("hull effect ended", 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -65,5 +65,21 @@ module TK.SpaceTac.UI.Specs {
|
||||||
check.nears(points[2], 0);
|
check.nears(points[2], 0);
|
||||||
check.nears(points[3], Math.PI * 0.25);
|
check.nears(points[3], Math.PI * 0.25);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.case("stops previous animations before starting a new one", check => {
|
||||||
|
let obj = { x: 0, y: 0 };
|
||||||
|
testgame.view.animations.addAnimation(obj, { x: 1 }, 1000);
|
||||||
|
testgame.clockForward(1);
|
||||||
|
testgame.clockForward(1);
|
||||||
|
check.equals(testgame.view.tweens.getAllTweens().length, 1);
|
||||||
|
testgame.view.animations.addAnimation(obj, { y: 1 }, 1000);
|
||||||
|
testgame.clockForward(1);
|
||||||
|
testgame.clockForward(1);
|
||||||
|
check.equals(testgame.view.tweens.getAllTweens().length, 2);
|
||||||
|
testgame.view.animations.addAnimation(obj, { x: 2 }, 1000);
|
||||||
|
testgame.clockForward(1);
|
||||||
|
testgame.clockForward(1);
|
||||||
|
check.equals(testgame.view.tweens.getAllTweens().length, 2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,9 @@ module TK.SpaceTac.UI {
|
||||||
* This is a wrapper around phaser's tweens.
|
* This is a wrapper around phaser's tweens.
|
||||||
*/
|
*/
|
||||||
export class Animations {
|
export class Animations {
|
||||||
private tweens: Phaser.Tweens.TweenManager
|
|
||||||
private immediate = false
|
private immediate = false
|
||||||
|
|
||||||
constructor(tweens: Phaser.Tweens.TweenManager) {
|
constructor(private tweens: Phaser.Tweens.TweenManager) {
|
||||||
this.tweens = tweens;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,11 +37,14 @@ module TK.SpaceTac.UI {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kill previous tweens from an object
|
* Kill previous tweens currently running on an object's properties
|
||||||
*/
|
*/
|
||||||
killPrevious(obj: object): void {
|
killPrevious<T extends object>(obj: T, properties: Extract<keyof T, string>[]): void {
|
||||||
// TODO Only updated properties
|
this.tweens.getTweensOf(obj).forEach(tween => {
|
||||||
this.tweens.killTweensOf(obj);
|
if (tween.data && any(tween.data, data => bool(data.key) && contains(properties, data.key))) {
|
||||||
|
tween.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,7 +71,7 @@ module TK.SpaceTac.UI {
|
||||||
* Display an object, with opacity transition
|
* Display an object, with opacity transition
|
||||||
*/
|
*/
|
||||||
show(obj: IAnimationFadeable, duration = 1000, alpha = 1): void {
|
show(obj: IAnimationFadeable, duration = 1000, alpha = 1): void {
|
||||||
this.killPrevious(obj);
|
this.killPrevious(obj, ['alpha']);
|
||||||
|
|
||||||
if (!obj.visible) {
|
if (!obj.visible) {
|
||||||
obj.alpha = 0;
|
obj.alpha = 0;
|
||||||
|
@ -105,7 +106,7 @@ module TK.SpaceTac.UI {
|
||||||
* Hide an object, with opacity transition
|
* Hide an object, with opacity transition
|
||||||
*/
|
*/
|
||||||
hide(obj: IAnimationFadeable, duration = 1000, alpha = 0): void {
|
hide(obj: IAnimationFadeable, duration = 1000, alpha = 0): void {
|
||||||
this.killPrevious(obj);
|
this.killPrevious(obj, ['alpha']);
|
||||||
|
|
||||||
if (obj.changeStateFrame) {
|
if (obj.changeStateFrame) {
|
||||||
obj.changeStateFrame("Out");
|
obj.changeStateFrame("Out");
|
||||||
|
@ -155,8 +156,8 @@ module TK.SpaceTac.UI {
|
||||||
* Add an asynchronous animation to an object.
|
* Add an asynchronous animation to an object.
|
||||||
*/
|
*/
|
||||||
addAnimation<T extends object>(obj: T, properties: Partial<T>, duration: number, ease = "Linear", delay = 0, loop = 1, yoyo = false): Promise<void> {
|
addAnimation<T extends object>(obj: T, properties: Partial<T>, duration: number, ease = "Linear", delay = 0, loop = 1, yoyo = false): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise(resolve => {
|
||||||
this.killPrevious(obj);
|
this.killPrevious(obj, keys(properties));
|
||||||
|
|
||||||
this.tweens.add(merge<object>({
|
this.tweens.add(merge<object>({
|
||||||
targets: obj,
|
targets: obj,
|
||||||
|
@ -232,12 +233,12 @@ module TK.SpaceTac.UI {
|
||||||
* Returns the animation duration.
|
* Returns the animation duration.
|
||||||
*/
|
*/
|
||||||
moveInSpace(obj: Phaser.GameObjects.Components.Transform, x: number, y: number, angle: number, rotated_obj = obj): number {
|
moveInSpace(obj: Phaser.GameObjects.Components.Transform, x: number, y: number, angle: number, rotated_obj = obj): number {
|
||||||
|
this.killPrevious(obj, ["x", "y"]);
|
||||||
|
|
||||||
if (x == obj.x && y == obj.y) {
|
if (x == obj.x && y == obj.y) {
|
||||||
this.killPrevious(obj);
|
|
||||||
return this.rotationTween(rotated_obj, angle, 0.5);
|
return this.rotationTween(rotated_obj, angle, 0.5);
|
||||||
} else {
|
} else {
|
||||||
this.killPrevious(obj);
|
this.killPrevious(rotated_obj, ["rotation"]);
|
||||||
this.killPrevious(rotated_obj);
|
|
||||||
let distance = Target.newFromLocation(obj.x, obj.y).getDistanceTo(Target.newFromLocation(x, y));
|
let distance = Target.newFromLocation(obj.x, obj.y).getDistanceTo(Target.newFromLocation(x, y));
|
||||||
let duration = Math.sqrt(distance / 1000) * 3000;
|
let duration = Math.sqrt(distance / 1000) * 3000;
|
||||||
let curve_force = distance * 0.4;
|
let curve_force = distance * 0.4;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
module TK.SpaceTac.UI.Specs {
|
module TK.SpaceTac.UI.Specs {
|
||||||
testing("InputManager", test => {
|
testing("InputManager", test => {
|
||||||
let testgame = setupEmptyView(test);
|
let testgame = setupEmptyView(test);
|
||||||
let clock = test.clock();
|
|
||||||
|
|
||||||
test.case("handles hover and click on desktops and mobile targets", check => {
|
test.case("handles hover and click on desktops and mobile targets", check => {
|
||||||
let inputs = testgame.view.inputs;
|
let inputs = testgame.view.inputs;
|
||||||
|
@ -46,7 +45,7 @@ module TK.SpaceTac.UI.Specs {
|
||||||
[button, mocks] = newButton();
|
[button, mocks] = newButton();
|
||||||
check.in("Leaves on destroy", check => {
|
check.in("Leaves on destroy", check => {
|
||||||
press(button);
|
press(button);
|
||||||
clock.forward(150);
|
testgame.clockForward(150);
|
||||||
check.called(mocks.enter, 1);
|
check.called(mocks.enter, 1);
|
||||||
check.called(mocks.leave, 0);
|
check.called(mocks.leave, 0);
|
||||||
check.called(mocks.click, 0);
|
check.called(mocks.click, 0);
|
||||||
|
@ -65,7 +64,7 @@ module TK.SpaceTac.UI.Specs {
|
||||||
let [button1, funcs1] = newButton();
|
let [button1, funcs1] = newButton();
|
||||||
let [button2, funcs2] = newButton();
|
let [button2, funcs2] = newButton();
|
||||||
enter(button1);
|
enter(button1);
|
||||||
clock.forward(150);
|
testgame.clockForward(150);
|
||||||
check.called(funcs1.enter, 1);
|
check.called(funcs1.enter, 1);
|
||||||
check.called(funcs1.leave, 0);
|
check.called(funcs1.leave, 0);
|
||||||
check.called(funcs1.click, 0);
|
check.called(funcs1.click, 0);
|
||||||
|
@ -76,7 +75,7 @@ module TK.SpaceTac.UI.Specs {
|
||||||
check.called(funcs2.enter, 0);
|
check.called(funcs2.enter, 0);
|
||||||
check.called(funcs2.leave, 0);
|
check.called(funcs2.leave, 0);
|
||||||
check.called(funcs2.click, 0);
|
check.called(funcs2.click, 0);
|
||||||
clock.forward(150);
|
testgame.clockForward(150);
|
||||||
check.called(funcs1.enter, 0);
|
check.called(funcs1.enter, 0);
|
||||||
check.called(funcs1.leave, 0);
|
check.called(funcs1.leave, 0);
|
||||||
check.called(funcs1.click, 0);
|
check.called(funcs1.click, 0);
|
||||||
|
@ -88,7 +87,7 @@ module TK.SpaceTac.UI.Specs {
|
||||||
[button, mocks] = newButton();
|
[button, mocks] = newButton();
|
||||||
check.in("Hold to hover on mobile", check => {
|
check.in("Hold to hover on mobile", check => {
|
||||||
button.emit("pointerdown", pointer);
|
button.emit("pointerdown", pointer);
|
||||||
clock.forward(150);
|
testgame.clockForward(150);
|
||||||
check.called(mocks.enter, 1);
|
check.called(mocks.enter, 1);
|
||||||
check.called(mocks.leave, 0);
|
check.called(mocks.leave, 0);
|
||||||
check.called(mocks.click, 0);
|
check.called(mocks.click, 0);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
module TK.SpaceTac.UI.Specs {
|
module TK.SpaceTac.UI.Specs {
|
||||||
testing("Tooltip", test => {
|
testing("Tooltip", test => {
|
||||||
let testgame = setupEmptyView(test);
|
let testgame = setupEmptyView(test);
|
||||||
let clock = test.clock();
|
|
||||||
|
|
||||||
test.case("shows near the hovered button", check => {
|
test.case("shows near the hovered button", check => {
|
||||||
let button = new UIBuilder(testgame.view).button("fake");
|
let button = new UIBuilder(testgame.view).button("fake");
|
||||||
|
@ -18,7 +17,7 @@ module TK.SpaceTac.UI.Specs {
|
||||||
button.emit("pointerover", { pointer: pointer });
|
button.emit("pointerover", { pointer: pointer });
|
||||||
check.equals(container.visible, false);
|
check.equals(container.visible, false);
|
||||||
|
|
||||||
clock.forward(1000);
|
testgame.clockForward(1000);
|
||||||
container.update();
|
container.update();
|
||||||
check.equals(container.visible, true);
|
check.equals(container.visible, true);
|
||||||
check.equals(container.x, 113);
|
check.equals(container.x, 113);
|
||||||
|
|
|
@ -2,7 +2,7 @@ module TK.SpaceTac.UI {
|
||||||
/**
|
/**
|
||||||
* UI component to show a loader animation while waiting for something
|
* UI component to show a loader animation while waiting for something
|
||||||
*/
|
*/
|
||||||
export class UIAwaiter extends Phaser.GameObjects.Container {
|
export class UIAwaiter extends UIContainer {
|
||||||
constructor(view: BaseView, x: number, y: number, visible: boolean) {
|
constructor(view: BaseView, x: number, y: number, visible: boolean) {
|
||||||
super(view, x, y);
|
super(view, x, y);
|
||||||
this.setName("awaiter");
|
this.setName("awaiter");
|
||||||
|
|
|
@ -3,6 +3,13 @@ module TK.SpaceTac.UI {
|
||||||
* UI component able to contain other UI components
|
* UI component able to contain other UI components
|
||||||
*/
|
*/
|
||||||
export class UIContainer extends Phaser.GameObjects.Container {
|
export class UIContainer extends Phaser.GameObjects.Container {
|
||||||
|
/**
|
||||||
|
* Get a container to build UI components inside the container
|
||||||
|
*/
|
||||||
|
getBuilder(): UIBuilder {
|
||||||
|
return new UIBuilder(<BaseView>this.scene, this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fixed version that does not force (0, 0) to be in bounds
|
* Fixed version that does not force (0, 0) to be in bounds
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,20 +1,43 @@
|
||||||
module TK.SpaceTac.UI.Specs {
|
module TK.SpaceTac.UI.Specs {
|
||||||
testing("FleetDisplay", test => {
|
testing("FleetDisplay", test => {
|
||||||
let testgame = setupMapview(test);
|
let testgame = setupEmptyView(test);
|
||||||
|
|
||||||
test.case("orbits the fleet around its current location", check => {
|
test.case("orbits the fleet around its current location", check => {
|
||||||
let mapview = testgame.view;
|
let session = new GameSession();
|
||||||
let fleet = mapview.player_fleet;
|
session.startNewGame(true, false);
|
||||||
|
let fleet = new FleetDisplay(testgame.view, session.player.fleet, session.universe, undefined, false);
|
||||||
|
|
||||||
fleet.loopOrbit();
|
fleet.loopOrbit();
|
||||||
check.equals(fleet.rotation, 0);
|
check.equals(fleet.rotation, 0);
|
||||||
|
|
||||||
let tweendata = mapview.animations.simulate(fleet, "rotation", 4);
|
checkTween(testgame, fleet, "rotation", [0, -Math.PI * 2 / 3, Math.PI * 2 / 3, 0]);
|
||||||
check.equals(tweendata.length, 4);
|
});
|
||||||
check.nears(tweendata[0], 0);
|
|
||||||
check.nears(tweendata[1], -Math.PI * 2 / 3);
|
test.case("animates jumps between locations", check => {
|
||||||
check.nears(tweendata[2], Math.PI * 2 / 3);
|
let session = new GameSession();
|
||||||
check.nears(tweendata[3], 0);
|
session.startNewGame(true, false);
|
||||||
|
let fleet_disp = new FleetDisplay(testgame.view, session.player.fleet, session.universe, undefined, false);
|
||||||
|
|
||||||
|
let on_leave = check.mockfunc("on_leave", (duration: number): any => null);
|
||||||
|
let on_finished = check.mockfunc();
|
||||||
|
|
||||||
|
let current = nn(session.universe.getLocation(session.player.fleet.location));
|
||||||
|
let dest = nn(first(current.star.locations, loc => loc !== current));
|
||||||
|
dest.universe_x = current.universe_x - 0.1;
|
||||||
|
dest.universe_y = current.universe_y;
|
||||||
|
dest.clearEncounter();
|
||||||
|
fleet_disp.moveToLocation(dest, 1, on_leave.func, on_finished.func);
|
||||||
|
check.called(on_leave, 0);
|
||||||
|
check.called(on_finished, 0);
|
||||||
|
checkTween(testgame, fleet_disp, "rotation", [
|
||||||
|
0,
|
||||||
|
-0.0436332312998573,
|
||||||
|
-0.3490658503988655,
|
||||||
|
-1.178097245096172,
|
||||||
|
-2.7925268031909276,
|
||||||
|
0.8290313946973056,
|
||||||
|
-3.141592653589793
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,25 +13,23 @@ module TK.SpaceTac.UI {
|
||||||
* Group to display a fleet
|
* Group to display a fleet
|
||||||
*/
|
*/
|
||||||
export class FleetDisplay extends UIContainer {
|
export class FleetDisplay extends UIContainer {
|
||||||
private map: UniverseMapView
|
|
||||||
private fleet: Fleet
|
|
||||||
private ship_count = 0
|
private ship_count = 0
|
||||||
|
private is_moving = false
|
||||||
|
|
||||||
constructor(parent: UniverseMapView, fleet: Fleet) {
|
constructor(private map: BaseView, private fleet: Fleet, private universe: Universe, private location_marker?: CurrentLocationMarker, orbit = true) {
|
||||||
super(parent);
|
super(map);
|
||||||
|
|
||||||
this.map = parent;
|
|
||||||
this.fleet = fleet;
|
|
||||||
|
|
||||||
this.updateShipSprites();
|
this.updateShipSprites();
|
||||||
|
|
||||||
let location = this.map.universe.getLocation(fleet.location);
|
let location = this.universe.getLocation(fleet.location);
|
||||||
if (location) {
|
if (location) {
|
||||||
this.setPosition(location.star.x + location.x, location.star.y + location.y);
|
this.setPosition(location.star.x + location.x, location.star.y + location.y);
|
||||||
}
|
}
|
||||||
this.setScale(SCALING, SCALING);
|
this.setScale(SCALING, SCALING);
|
||||||
|
|
||||||
this.loopOrbit();
|
if (orbit) {
|
||||||
|
this.loopOrbit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,14 +52,14 @@ module TK.SpaceTac.UI {
|
||||||
}
|
}
|
||||||
|
|
||||||
get location(): StarLocation {
|
get location(): StarLocation {
|
||||||
return this.map.universe.getLocation(this.fleet.location) || new StarLocation();
|
return this.universe.getLocation(this.fleet.location) || new StarLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Animate to a given position in orbit of its current star location
|
* Animate to a given position in orbit of its current star location
|
||||||
*/
|
*/
|
||||||
goToOrbitPoint(angle: number, speed = 1, fullturns = 0, then: Function | null = null, ease = false) {
|
async goToOrbitPoint(angle: number, speed = 1, fullturns = 0, ease = false): Promise<void> {
|
||||||
this.map.animations.killPrevious(this);
|
this.map.animations.killPrevious<UIContainer>(this, ["angle"]);
|
||||||
this.rotation %= PI2;
|
this.rotation %= PI2;
|
||||||
|
|
||||||
let target = -angle;
|
let target = -angle;
|
||||||
|
@ -70,33 +68,32 @@ module TK.SpaceTac.UI {
|
||||||
}
|
}
|
||||||
target -= PI2 * fullturns;
|
target -= PI2 * fullturns;
|
||||||
let distance = Math.abs(target - this.rotation) / PI2;
|
let distance = Math.abs(target - this.rotation) / PI2;
|
||||||
let tween = this.map.animations.addAnimation<UIContainer>(this, { rotation: target }, 30000 * distance / speed, ease ? "Cubic.easeIn" : "Linear");
|
await this.map.animations.addAnimation<UIContainer>(this, { rotation: target }, 30000 * distance / speed, ease ? "Cubic.easeIn" : "Linear");
|
||||||
if (then) {
|
|
||||||
tween.then(() => then());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make the fleet loop in orbit
|
* Make the fleet loop in orbit
|
||||||
*/
|
*/
|
||||||
loopOrbit() {
|
loopOrbit() {
|
||||||
this.goToOrbitPoint(this.rotation + PI2, 1, 0, () => {
|
if (!this.is_moving) {
|
||||||
this.loopOrbit();
|
this.goToOrbitPoint(this.rotation + PI2, 1, 0).then(() => this.loopOrbit());
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make the fleet move to another location in the same system
|
* Make the fleet move to another location in the same system
|
||||||
*/
|
*/
|
||||||
moveToLocation(location: StarLocation, speed = 1, on_leave: ((duration: number) => any) | null = null, on_finished: Function | null = null) {
|
moveToLocation(location: StarLocation, speed = 1, on_leave: ((duration: number) => any) | null = null, on_finished: Function | null = null) {
|
||||||
let fleet_location = this.map.universe.getLocation(this.fleet.location);
|
let fleet_location = this.universe.getLocation(this.fleet.location);
|
||||||
if (fleet_location && this.fleet.move(location)) {
|
if (fleet_location && this.fleet.move(location)) {
|
||||||
let dx = location.universe_x - fleet_location.universe_x;
|
let dx = location.universe_x - fleet_location.universe_x;
|
||||||
let dy = location.universe_y - fleet_location.universe_y;
|
let dy = location.universe_y - fleet_location.universe_y;
|
||||||
let distance = Math.sqrt(dx * dx + dy * dy);
|
let distance = Math.sqrt(dx * dx + dy * dy);
|
||||||
let angle = Math.atan2(dx, dy);
|
let angle = Math.atan2(-dy, dx);
|
||||||
this.map.current_location.setFleetMoving(true);
|
this.setMoving(true);
|
||||||
this.goToOrbitPoint(angle - Math.PI / 2, 40, 1, () => {
|
console.error(fleet_location, location, angle);
|
||||||
|
this.goToOrbitPoint(angle, 40, 1, true).then(() => {
|
||||||
|
this.setRotation(-angle);
|
||||||
let duration = 10000 * distance / speed;
|
let duration = 10000 * distance / speed;
|
||||||
if (on_leave) {
|
if (on_leave) {
|
||||||
on_leave(duration);
|
on_leave(duration);
|
||||||
|
@ -106,7 +103,7 @@ module TK.SpaceTac.UI {
|
||||||
if (this.fleet.battle) {
|
if (this.fleet.battle) {
|
||||||
this.map.backToRouter();
|
this.map.backToRouter();
|
||||||
} else {
|
} else {
|
||||||
this.map.current_location.setFleetMoving(false);
|
this.setMoving(false);
|
||||||
this.loopOrbit();
|
this.loopOrbit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +111,33 @@ module TK.SpaceTac.UI {
|
||||||
on_finished();
|
on_finished();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, true);
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a jump flash effect
|
||||||
|
*/
|
||||||
|
async showJumpEffect(lag = 0, duration = 0): Promise<void> {
|
||||||
|
this.map.audio.playOnce(lag ? "map-warp-out" : "map-warp-in");
|
||||||
|
let effect = this.getBuilder().image("map-jump-effect", 0, 150, true);
|
||||||
|
effect.setScale(0.01);
|
||||||
|
effect.setZ(-1);
|
||||||
|
if (lag && duration) {
|
||||||
|
this.map.animations.addAnimation(effect, { x: -lag / SCALING }, duration * 0.5, "Cubic.easeOut");
|
||||||
|
}
|
||||||
|
await this.map.animations.addAnimation(effect, { scaleX: 3, scaleY: 3 }, 100);
|
||||||
|
await this.map.animations.addAnimation(effect, { scaleX: 2, scaleY: 2, alpha: 0 }, 200);
|
||||||
|
effect.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark the fleet as moving
|
||||||
|
*/
|
||||||
|
private setMoving(moving: boolean): void {
|
||||||
|
this.is_moving = moving;
|
||||||
|
if (this.location_marker) {
|
||||||
|
this.location_marker.setFleetMoving(moving);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ module TK.SpaceTac.UI {
|
||||||
this.starsystems = this.universe.stars.map(star => new StarSystemDisplay(this, star));
|
this.starsystems = this.universe.stars.map(star => new StarSystemDisplay(this, star));
|
||||||
this.starsystems.forEach(starsystem => this.layer_universe.add(starsystem));
|
this.starsystems.forEach(starsystem => this.layer_universe.add(starsystem));
|
||||||
|
|
||||||
this.player_fleet = new FleetDisplay(this, this.player.fleet);
|
this.player_fleet = new FleetDisplay(this, this.player.fleet, this.universe, this.current_location);
|
||||||
this.layer_universe.add(this.player_fleet);
|
this.layer_universe.add(this.player_fleet);
|
||||||
|
|
||||||
this.current_location = new CurrentLocationMarker(this, this.player_fleet);
|
this.current_location = new CurrentLocationMarker(this, this.player_fleet);
|
||||||
|
@ -290,7 +290,9 @@ module TK.SpaceTac.UI {
|
||||||
let dest_location = location.jump_dest;
|
let dest_location = location.jump_dest;
|
||||||
let dest_star = dest_location.star;
|
let dest_star = dest_location.star;
|
||||||
this.player_fleet.moveToLocation(dest_location, 3, duration => {
|
this.player_fleet.moveToLocation(dest_location, 3, duration => {
|
||||||
this.timer.schedule(duration / 3, () => this.updateInfo(dest_star, false));
|
this.player_fleet.showJumpEffect(location.getDistanceTo(dest_location), duration);
|
||||||
|
this.timer.schedule(duration * 0.3, () => this.updateInfo(dest_star, false));
|
||||||
|
this.timer.schedule(duration * 0.7, () => this.player_fleet.showJumpEffect());
|
||||||
this.setCamera(dest_star.x, dest_star.y, dest_star.radius * 2, duration, "Cubic.easeOut");
|
this.setCamera(dest_star.x, dest_star.y, dest_star.radius * 2, duration, "Cubic.easeOut");
|
||||||
}, () => {
|
}, () => {
|
||||||
this.setInteractionEnabled(true);
|
this.setInteractionEnabled(true);
|
||||||
|
|
Loading…
Reference in a new issue