diff --git a/out/assets/images/common/particles.png b/out/assets/images/common/particles.png index 7d7d4c9..238d99f 100644 Binary files a/out/assets/images/common/particles.png and b/out/assets/images/common/particles.png differ diff --git a/out/assets/images/common/particles.xcf b/out/assets/images/common/particles.xcf index e760d6a..2b1237f 100644 Binary files a/out/assets/images/common/particles.xcf and b/out/assets/images/common/particles.xcf differ diff --git a/out/assets/images/menu/star.png b/out/assets/images/menu/star.png deleted file mode 100644 index d6ce600..0000000 Binary files a/out/assets/images/menu/star.png and /dev/null differ diff --git a/src/ui/Preload.ts b/src/ui/Preload.ts index d932e95..99a8c77 100644 --- a/src/ui/Preload.ts +++ b/src/ui/Preload.ts @@ -15,7 +15,6 @@ module TS.SpaceTac.UI { this.loadImage("menu/button.png"); this.loadImage("menu/button-hover.png"); this.loadImage("menu/button-fullscreen.png"); - this.loadImage("menu/star.png"); this.loadImage("menu/load-bg.png"); this.loadSheet("common/particles.png", 32); this.loadImage("common/transparent.png"); diff --git a/src/ui/common/ParticleBuilder.spec.ts b/src/ui/common/ParticleBuilder.spec.ts new file mode 100644 index 0000000..89b009d --- /dev/null +++ b/src/ui/common/ParticleBuilder.spec.ts @@ -0,0 +1,33 @@ +module TS.SpaceTac.UI.Specs { + describe("ParticleBuilder", () => { + let testgame = setupEmptyView(); + + it("builds composed particles", function () { + let builder = new ParticleBuilder(testgame.baseview); + let particle = builder.build([ + new ParticleConfig(ParticleShape.ROUND, ParticleColor.BLUE, 2, 1, 45, 10, -20), + new ParticleConfig(ParticleShape.DISK_HALO, ParticleColor.WHITE, 0.5, 1, 0, 5, 0) + ]); + + expect(particle instanceof Phaser.Image).toBe(true); + expect(particle.data.frame).toEqual(4); + expect(particle.data.key).toEqual("common-particles"); + expect(particle.scale.x).toEqual(2); + expect(particle.scale.y).toEqual(2); + expect(particle.x).toEqual(10); + expect(particle.y).toEqual(-20); + expect(particle.angle).toEqual(45); + + expect(particle.children.length).toEqual(1); + let subparticle = particle.getChildAt(0); + expect(subparticle instanceof Phaser.Image).toBe(true); + expect(subparticle.data.frame).toEqual(16); + expect(subparticle.data.key).toEqual("common-particles"); + expect(subparticle.scale.x).toEqual(0.25); + expect(subparticle.scale.y).toEqual(0.25); + expect(subparticle.x).toEqual(2.5); + expect(subparticle.y).toEqual(0); + expect(subparticle.angle).toEqual(-45); + }); + }); +} diff --git a/src/ui/common/ParticleBuilder.ts b/src/ui/common/ParticleBuilder.ts new file mode 100644 index 0000000..3bbf159 --- /dev/null +++ b/src/ui/common/ParticleBuilder.ts @@ -0,0 +1,100 @@ +module TS.SpaceTac.UI { + /** + * Particle shapes + */ + export enum ParticleShape { + ROUND = 0, + DISK_HALO = 1, + TRAIL = 2, + FLARE = 3 + } + + /** + * Particle colors + */ + export enum ParticleColor { + WHITE = 0, + BLACK = 1, + GREY = 2, + RED = 3, + BLUE = 4, + YELLOW = 5, + GREEN = 6, + CYAN = 7, + MAGENTA = 8, + BROWN = 9, + VIOLET = 10, + MARINE = 11, + ORANGE = 12, + YELLOWISH = 13, + GREENISH = 14, + BLUEISH = 15, + } + + /** + * Config for a single sub-particle + */ + export class ParticleConfig { + shape: ParticleShape + color: ParticleColor + scale: number + alpha: number + angle: number + offsetx: number + offsety: number + + constructor(shape = ParticleShape.ROUND, color = ParticleColor.WHITE, scale = 1, alpha = 1, angle = 0, offsetx = 0, offsety = 0) { + this.shape = shape; + this.color = color; + this.scale = scale; + this.alpha = alpha; + this.angle = angle; + this.offsetx = offsetx; + this.offsety = offsety; + } + + /** + * Get a particle image for this config + */ + getImage(game: Phaser.Game, scaling = 1, angle = 0): Phaser.Image { + let frame = this.shape * 16 + this.color; + let result = game.add.image(0, 0, "common-particles", frame); + result.data.frame = frame; + result.data.key = "common-particles"; + result.anchor.set(0.5); + result.angle = angle + this.angle; + result.alpha = this.alpha; + result.scale.set(this.scale * scaling); + result.position.set(this.offsetx * scaling, this.offsety * scaling); + return result; + } + } + + /** + * Builder of particles, composed of one or several sub particles + */ + export class ParticleBuilder { + view: BaseView + + constructor(view: BaseView) { + this.view = view; + } + + /** + * Build a composed particle + */ + build(configs: ParticleConfig[]): Phaser.Image { + if (configs.length == 0) { + return this.view.game.add.image(0, 0, "common-transparent"); + } else { + let base = configs[0]; + let result = base.getImage(this.view.game, 1); + configs.slice(1).forEach(config => { + let sub = config.getImage(this.view.game, 1 / base.scale, -base.angle); + result.addChild(sub); + }); + return result; + } + } + } +} \ No newline at end of file diff --git a/src/ui/intro/IntroSteps.ts b/src/ui/intro/IntroSteps.ts index 0ba63bd..d517bef 100644 --- a/src/ui/intro/IntroSteps.ts +++ b/src/ui/intro/IntroSteps.ts @@ -63,7 +63,10 @@ module TS.SpaceTac.UI { this.message("No official communication has been issued since, and numerous rogue fleets have taken position in key sectors of the galaxy, forbidding passage or harassing merchants."), this.message("The Master Merchant Guild, a powerful group that spans several galaxies, is worried about the profit loss those events incurred, and after many debates, decided to send several investigation teams to Terranax."), this.message("Their task is to discreetly uncover the origin of the invasion, and to bring back intel that may be used by the Guild to plan an appropriate response."), - this.message("Your team has been sent through the Expeller jump system based in the Eros-MC galaxy, and just left quantum space in orbit of a Terranaxan star..."), + this.simultaneous( [ + this.exitftl(), + this.message("Your team has been sent through the Expeller jump system based in the Eros-MC galaxy, and just left quantum space in orbit of a Terranaxan star..."), + ]), ]; } @@ -107,6 +110,32 @@ module TS.SpaceTac.UI { } } + /** + * Display a fleet emerging from FTL + */ + protected exitftl(): Function { + return () => { + let layer = this.getLayer(1); + let builder = new ParticleBuilder(this.view); + let fleet = builder.build([ + new ParticleConfig(ParticleShape.TRAIL, ParticleColor.BLUEISH, 0.8, 1, 200), + new ParticleConfig(ParticleShape.FLARE, ParticleColor.CYAN, 10, 0.2, -45) + ]); + fleet.position.set(this.view.getMidWidth(), this.view.getMidHeight()); + this.view.game.add.tween(fleet).from({ x: fleet.x + 1500, y: fleet.y - 750 }, 3000, Phaser.Easing.Circular.Out, true); + this.view.game.add.tween(fleet).to({ alpha: 0, width: 40, height: 40 }, 500, Phaser.Easing.Cubic.Out, true, 2000); + let flash = this.view.game.add.image(this.view.getMidWidth() + 60, this.view.getMidHeight() - 30, "common-particles", 15); + flash.anchor.set(0.5); + flash.scale.set(0.1); + flash.alpha = 0; + let subflash = this.view.game.add.image(0, 0, "common-particles", 0); + subflash.anchor.set(0.5); + subflash.scale.set(0.5); + flash.addChild(subflash); + this.view.game.add.tween(flash).to({ alpha: 0.7, width: 60, height: 60 }, 300, Phaser.Easing.Quadratic.Out, true, 2000, undefined, true); + } + } + /** * Build a step that performs several other steps at the same time */ @@ -119,11 +148,13 @@ module TS.SpaceTac.UI { /** * Build a step to display a message. */ - protected message(message: string, layer = 1, clear = true): Function { + protected message(message: string, layer = 2, clear = true): Function { return () => { let display = new ProgressiveMessage(this.view, 800, 150, message, true); display.setPositionInsideParent(0.5, 0.9); display.moveToLayer(this.getLayer(layer, clear)); + display.setVisible(false); + display.setVisible(true, 500); } } diff --git a/src/ui/menu/MainMenu.ts b/src/ui/menu/MainMenu.ts index 496202b..07972f5 100644 --- a/src/ui/menu/MainMenu.ts +++ b/src/ui/menu/MainMenu.ts @@ -23,10 +23,11 @@ module TS.SpaceTac.UI { for (let i = 0; i < 300; i++) { let fade = Math.random() * 0.5 + 0.5; let x = Math.random() * 0.998 + 0.001; - let star = this.add.image(1920 * x, Math.random() * 1080, "menu-star", 0, this.layer_stars); + let star = this.add.image(1920 * x, Math.random() * 1080, "common-particles", 32, this.layer_stars); star.anchor.set(0.5, 0.5); + star.angle = 225; star.alpha = 0.7 * fade; - star.scale.set(0.1 * fade, 0.1 * fade); + star.scale.set(0.8 * fade, 0.8 * fade); this.tweens.create(star).to({ x: -30 }, 30000 * x / fade).to({ x: 1950 }, 0.00001).to({ x: 1920 * x }, 30000 * (1 - x) / fade).loop().start(); }