1
0
Fork 0

New gatling gun effect, with shield hit

This commit is contained in:
Michaël Lemaire 2017-02-13 20:31:45 +01:00
parent e5cd71f4e1
commit 5b7b01d85e
13 changed files with 472 additions and 34 deletions

8
TODO
View file

@ -1,6 +1,9 @@
* Ensure that tweens and particle emitters get destroyed once animation is done
* Stop processing the log during a ship move animation, or a weapon animation
* Drones: add tooltip
* Drones: add hull points and take area damage
* Drones: change the sprite angle for deploy animation
* Drones: add animation for each activation
* Add a battle log display
* Organize arena objects and information in layers
* Prevent arena effects information (eg. "shield -36") to overflow out of the arena
@ -26,4 +29,7 @@
* Map : restore fog of war
* Map : add information on current star/location + information on hovered location
* Map : add stores and shipyards
* Map : remove jump links that crosses the radius of other systems
* Map : remove jump links that cross the radius of other systems
* Map : improve performance
* Menu : fix background stars aggregating at right side when the game is not focused
* Multiplayer

View file

@ -0,0 +1,330 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="128"
height="128"
viewBox="0 0 128 128"
version="1.1"
id="svg8"
inkscape:version="0.92.0 r15299"
sodipodi:docname="weaponeffects.svg"
inkscape:export-filename="/tmp/image.png"
inkscape:export-xdpi="96.000008"
inkscape:export-ydpi="96.000008">
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient4516">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4512" />
<stop
id="stop4520"
offset="0.56179416"
style="stop-color:#ffffff;stop-opacity:0.75686275;" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop4514" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient4508">
<stop
style="stop-color:#f04d22;stop-opacity:1"
offset="0"
id="stop4504" />
<stop
style="stop-color:#f1b52b;stop-opacity:0"
offset="1"
id="stop4506" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient5422">
<stop
style="stop-color:#bac3ce;stop-opacity:1;"
offset="0"
id="stop5418" />
<stop
id="stop5426"
offset="0.20119144"
style="stop-color:#bac3ce;stop-opacity:0.71372549;" />
<stop
style="stop-color:#bac3ce;stop-opacity:0;"
offset="1"
id="stop5420" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5422"
id="radialGradient5424"
cx="142.81401"
cy="112.77065"
fx="142.81401"
fy="112.77065"
r="28.063299"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.42830958,-0.00892459,0.02249464,1.0795673,32.728161,206.28878)" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4508"
id="radialGradient4510"
cx="91.696426"
cy="322.20474"
fx="91.696426"
fy="322.20474"
r="1.1607143"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.0318626,0,0,2.0318626,-94.618115,-332.47103)" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4516"
id="radialGradient4518"
cx="64"
cy="64"
fx="64"
fy="64"
r="7.4498734"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient5468">
<stop
style="stop-color:#fbe854;stop-opacity:1"
offset="0"
id="stop5470" />
<stop
id="stop5472"
offset="0.11726352"
style="stop-color:#b98850;stop-opacity:1" />
<stop
style="stop-color:#6d6d6d;stop-opacity:0;"
offset="1"
id="stop5474" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5468"
id="linearGradient5381"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-10)"
x1="169.07599"
y1="113.24746"
x2="108.23768"
y2="113.18433" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="5.6"
inkscape:cx="48.929207"
inkscape:cy="69.976519"
inkscape:document-units="px"
inkscape:current-layer="layer4"
showgrid="false"
units="px"
inkscape:measure-start="35.2291,59.599"
inkscape:measure-end="92.8078,59.4727"
scale-x="0.20001" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Ref ship"
transform="translate(0,1.950136e-5)"
style="display:inline">
<g
transform="matrix(0.17980079,0,0,0.17980079,124.51786,44.45057)"
style="enable-background:new"
id="g4184">
<rect
y="68.069527"
x="-437.6991"
height="60.811184"
width="16.970562"
id="rect4180"
style="opacity:1;fill:#666666;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
y="60.998459"
x="-262.33661"
height="73.539108"
width="19.091883"
id="rect4182"
style="opacity:1;fill:#666666;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<ellipse
ry="99.702057"
rx="53.740116"
cy="108.37461"
cx="-339.41125"
id="path4176"
style="opacity:1;fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:#4f4f4f;stroke-width:3.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<ellipse
ry="65.407379"
rx="130.81476"
cy="143.3764"
cx="-336.58282"
id="path4178"
style="opacity:1;fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:#4f4f4f;stroke-width:3.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
<g
inkscape:label="Shield impact"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-263.13331)">
<circle
style="opacity:1;fill:url(#radialGradient5424);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.61623001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path5416"
cx="64"
cy="327.1333"
inkscape:export-filename="/home/michael/workspace/perso/spacetac/out/assets/images/battle/weapon/shield-impact.png"
inkscape:export-xdpi="95.81321"
inkscape:export-ydpi="95.81321"
r="37.190208" />
<circle
style="opacity:1;fill:url(#radialGradient4510);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.57095861;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path4502"
cx="91.696426"
cy="322.20474"
r="2.358412"
inkscape:export-filename="/home/michael/workspace/perso/spacetac/out/assets/images/battle/weapon/hot.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Default"
style="display:none">
<circle
style="opacity:1;fill:url(#radialGradient4518);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.39426357;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path4510"
cx="64"
cy="64"
r="7.4498734"
inkscape:export-filename="/home/michael/workspace/perso/spacetac/out/assets/images/battle/weapon/default.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
</g>
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="Bullets">
<g
id="g5371"
transform="matrix(0.11505865,0.11505865,-0.11505865,0.11505865,63.327458,53.088239)"
inkscape:export-filename="/home/michael/workspace/perso/spacetac/out/assets/images/battle/weapon/bullets.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<use
x="0"
y="0"
xlink:href="#g4509"
id="use6554"
transform="translate(137.06162,-137.06162)"
width="100%"
height="100%"
style="display:inline" />
<g
id="g4509"
transform="matrix(2.4013417,-2.4013417,1.1128473,1.1128473,-504.01292,314.07977)"
style="display:inline">
<path
style="opacity:1;fill:url(#linearGradient5381);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10.39999962;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0"
d="m 98.214287,111.35714 h 61.071423 v 4.28571 H 98.214287 Z"
id="rect4507"
inkscape:connector-curvature="0" />
</g>
<use
x="0"
y="0"
xlink:href="#g4509"
id="use6556"
transform="translate(182.05803,-160.59599)"
width="100%"
height="100%"
style="display:inline" />
<use
x="0"
y="0"
xlink:href="#g4509"
id="use4512"
transform="translate(44.996417,-23.534383)"
width="100%"
height="100%"
style="display:inline" />
<use
x="0"
y="0"
xlink:href="#g4509"
id="use6562"
transform="translate(227.05448,-184.13038)"
width="100%"
height="100%"
style="display:inline" />
<use
x="0"
y="0"
xlink:href="#g4509"
id="use4514"
transform="translate(89.992857,-47.068753)"
width="100%"
height="100%"
style="display:inline" />
<use
x="0"
y="0"
xlink:href="#g4509"
id="use4518"
transform="translate(134.94954,-70.642865)"
width="100%"
height="100%"
style="display:inline" />
<use
x="0"
y="0"
xlink:href="#g4509"
id="use4516"
transform="translate(-2.1120626,66.418737)"
width="100%"
height="100%"
style="display:inline" />
<use
x="0"
y="0"
xlink:href="#g4509"
id="use6572"
transform="translate(272.01116,-207.70448)"
width="100%"
height="100%"
style="display:inline" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -23,10 +23,10 @@ module TS.SpaceTac {
y: number;
// If the target is a ship, this attribute will be set
ship: Ship;
ship: Ship | null;
// Standard constructor
constructor(x: number, y: number, ship: Ship) {
constructor(x: number, y: number, ship: Ship | null = null) {
this.x = x;
this.y = y;
this.ship = ship;

View file

@ -54,7 +54,10 @@ module TS.SpaceTac.UI {
this.loadImage("battle/actions/fire-powerdepleter.png");
this.loadImage("battle/actions/fire-submunitionmissile.png");
this.loadImage("battle/actions/deploy-repairdrone.png");
this.loadImage("battle/weapon/bullet.png");
this.loadImage("battle/weapon/default.png");
this.loadImage("battle/weapon/bullets.png");
this.loadImage("battle/weapon/hot.png");
this.loadImage("battle/weapon/shield-impact.png");
this.loadImage("battle/attributes/power.png");
this.loadImage("battle/attributes/powercapacity.png");
this.loadImage("battle/attributes/effect-increase.png");

View file

@ -25,6 +25,9 @@ module TS.SpaceTac.UI {
// Currently playing ship
private playing: ArenaShip;
// Layer for particles
layer_weapon_effects: Phaser.Group;
// Create a graphical arena for ship sprites to fight in a 2D space
constructor(battleview: BattleView) {
super(battleview.game);
@ -80,6 +83,9 @@ module TS.SpaceTac.UI {
this.addChild(sprite);
this.ship_sprites.push(sprite);
});
this.layer_weapon_effects = new Phaser.Group(this.game);
this.addChild(this.layer_weapon_effects);
}
// Get the current MainUI instance

View file

@ -68,18 +68,24 @@ module TS.SpaceTac.UI {
}
// Move the sprite to a location
moveTo(x: number, y: number, facing_angle: number, animate: boolean = true) {
moveTo(x: number, y: number, facing_angle: number, animate: boolean = true, on_complete: Function | null = null) {
if (animate) {
var tween_group = this.game.tweens.create(this);
var tween_sprite = this.game.tweens.create(this.sprite);
tween_group.to({ x: x, y: y });
tween_group.start();
Tools.rotationTween(tween_sprite, facing_angle);
if (on_complete) {
tween_sprite.onComplete.addOnce(on_complete);
}
tween_sprite.start();
} else {
this.x = x;
this.y = y;
this.sprite.rotation = facing_angle;
if (on_complete) {
on_complete();
}
}
}
@ -90,7 +96,7 @@ module TS.SpaceTac.UI {
let text = new Phaser.Text(this.game, 0, 20 * this.effects.children.length, message, { font: "14pt Arial", fill: beneficial ? "#afe9c6" : "#e9afaf" });
this.effects.addChild(text);
this.effects.position.set(-this.effects.width / 2, this.sprite.height * 0.7);
this.effects.position.set(-this.effects.width / 2, this.sprite.height * 0.8);
this.game.tweens.removeFrom(this.effects);
this.effects.alpha = 1;

View file

@ -0,0 +1,39 @@
module TS.SpaceTac.UI.Specs {
describe("WeaponEffect", () => {
inbattleview_it("displays shield hit effect", (battleview) => {
let effect = new WeaponEffect(battleview.arena, new Target(0, 0), new Target(0, 0), "test");
effect.shieldImpactEffect({ x: 10, y: 10 }, { x: 20, y: 15 }, 1000, 3000);
let layer = battleview.arena.layer_weapon_effects;
expect(layer.children.length).toBe(2);
expect(layer.children[0] instanceof Phaser.Image).toBe(true);
expect(layer.children[0].rotation).toBeCloseTo(-2.677945044588987, 10);
expect(layer.children[0].position).toEqual(jasmine.objectContaining({ x: 20, y: 15 }));
expect(layer.children[1] instanceof Phaser.Particles.Arcade.Emitter).toBe(true);
});
inbattleview_it("displays gatling gun effect", (battleview) => {
let ship = new Ship();
ship.setArenaPosition(50, 30);
TestTools.setShipHP(ship, 10, 0);
let effect = new WeaponEffect(battleview.arena, new Target(10, 0), Target.newFromShip(ship), "test");
let mock_shield_impact = spyOn(effect, "shieldImpactEffect").and.stub();
effect.gunEffect();
let layer = battleview.arena.layer_weapon_effects;
expect(layer.children.length).toBe(1);
expect(layer.children[0] instanceof Phaser.Particles.Arcade.Emitter).toBe(true);
expect(mock_shield_impact).toHaveBeenCalledTimes(0);
TestTools.setShipHP(ship, 10, 10);
effect.gunEffect();
expect(mock_shield_impact).toHaveBeenCalledTimes(1);
expect(mock_shield_impact).toHaveBeenCalledWith(jasmine.objectContaining({ x: 10, y: 0 }), jasmine.objectContaining({ x: 50, y: 30 }), 100, 800);
});
});
}

View file

@ -7,10 +7,20 @@ module TS.SpaceTac.UI {
}
}
// Renderer of visual effects for a weapon fire
interface Point {
x: number;
y: number;
}
/**
* Visual effects renderer for weapons.
*/
export class WeaponEffect {
// Arena in which to display the effect
private arena: Arena;
// Link to game
private ui: MainUI;
// Display group in which to display the visual effects
private layer: Phaser.Group;
// Firing ship
private source: Target;
@ -25,7 +35,8 @@ module TS.SpaceTac.UI {
private effect: Function;
constructor(arena: Arena, source: Target, destination: Target, weapon: string) {
this.arena = arena;
this.ui = arena.getGame();
this.layer = arena.layer_weapon_effects;
this.source = source;
this.destination = destination;
this.weapon = weapon;
@ -49,15 +60,47 @@ module TS.SpaceTac.UI {
}
}
// Default firing effect (missile)
private defaultEffect(): void {
var missile = new Phaser.Sprite(this.arena.game, this.source.x, this.source.y, "battle-weapon-bullet");
missile.anchor.set(0.5, 0.5);
missile.scale.set(0.2, 0.2);
missile.rotation = this.source.getAngleTo(this.destination);
this.arena.addChild(missile);
/**
* Add a shield impact effect on a ship
*/
shieldImpactEffect(from: Point, ship: Point, delay: number, duration: number) {
let angle = Math.atan2(from.y - ship.y, from.x - ship.x);
var tween = this.arena.game.tweens.create(missile);
let effect = new Phaser.Image(this.ui, ship.x, ship.y, "battle-weapon-shield-impact");
effect.alpha = 0;
effect.rotation = angle;
effect.anchor.set(0.5, 0.5);
this.layer.addChild(effect);
let tween1 = this.ui.add.tween(effect).to({ alpha: 1 }, 100).delay(delay);
let tween2 = this.ui.add.tween(effect).to({ alpha: 0 }, 100).delay(duration);
tween1.chain(tween2);
tween2.onComplete.addOnce(() => effect.destroy());
tween1.start();
let emitter = this.ui.add.emitter(ship.x + Math.cos(angle) * 35, ship.y + Math.sin(angle) * 35, 30);
emitter.minParticleScale = 0.7;
emitter.maxParticleScale = 1.2;
emitter.gravity = 0;
emitter.makeParticles("battle-weapon-hot");
emitter.setSize(10, 10);
emitter.setRotation(0, 0);
emitter.setXSpeed(-Math.cos(angle) * 20, -Math.cos(angle) * 80);
emitter.setYSpeed(-Math.sin(angle) * 20, -Math.sin(angle) * 80);
emitter.start(false, 200, 30, duration / 30);
this.layer.addChild(emitter);
}
/**
* Default firing effect
*/
defaultEffect(): void {
var missile = new Phaser.Sprite(this.ui, this.source.x, this.source.y, "battle-weapon-default");
missile.anchor.set(0.5, 0.5);
missile.rotation = this.source.getAngleTo(this.destination);
this.layer.addChild(missile);
var tween = this.ui.tweens.create(missile);
tween.to({ x: this.destination.x, y: this.destination.y }, 1000);
tween.onComplete.addOnce(() => {
missile.destroy();
@ -65,28 +108,33 @@ module TS.SpaceTac.UI {
tween.start();
}
// Submachine gun effect (small chain of bullets)
private gunEffect(): void {
this.arena.getGame().audio.playOnce("battle-weapon-bullets");
/**
* Submachine gun effect (quick chain of small bullets)
*/
gunEffect(): void {
this.ui.audio.playOnce("battle-weapon-bullets");
var source = this.arena.toGlobal(new PIXI.Point(this.source.x, this.source.y));
var destination = this.arena.toGlobal(new PIXI.Point(this.destination.x, this.destination.y));
var dx = destination.x - source.x;
var dy = destination.y - source.y;
let has_shield = this.destination.ship && this.destination.ship.getValue("shield") > 0;
var dx = this.destination.x - this.source.x;
var dy = this.destination.y - this.source.y;
var angle = Math.atan2(dy, dx);
var distance = Math.sqrt(dx * dx + dy * dy);
var emitter = new Phaser.Particles.Arcade.Emitter(this.arena.game, source.x, source.y, 10);
var speed = 5000;
var scale = 0.1;
var emitter = new Phaser.Particles.Arcade.Emitter(this.ui, this.source.x + Math.cos(angle) * 35, this.source.y + Math.sin(angle) * 35, 10);
var speed = 2000;
emitter.particleClass = BulletParticle;
emitter.gravity = 0;
emitter.setSize(5, 5);
emitter.setRotation(0, 0);
emitter.minParticleSpeed.set(Math.cos(angle) * speed, Math.sin(angle) * speed);
emitter.maxParticleSpeed.set(Math.cos(angle) * speed, Math.sin(angle) * speed);
emitter.minParticleScale = scale;
emitter.maxParticleScale = scale;
emitter.makeParticles(["battle-weapon-bullet"]);
emitter.start(false, 1000 * distance / speed, 50, 10);
emitter.setXSpeed(Math.cos(angle) * speed, Math.cos(angle) * speed);
emitter.setYSpeed(Math.sin(angle) * speed, Math.sin(angle) * speed);
emitter.makeParticles(["battle-weapon-bullets"]);
emitter.start(false, 1000 * (distance - 50 - (has_shield ? 80 : 40)) / speed, 50, 10);
this.layer.addChild(emitter);
if (has_shield) {
this.shieldImpactEffect(this.source, this.destination, 100, 800);
}
}
}
}