2017-02-09 00:00:35 +00:00
|
|
|
module TS.SpaceTac.UI {
|
2015-02-20 00:00:00 +00:00
|
|
|
// Particle that is rotated to always face its ongoing direction
|
|
|
|
class BulletParticle extends Phaser.Particle {
|
|
|
|
update(): void {
|
|
|
|
super.update();
|
|
|
|
this.rotation = Math.atan2(this.body.velocity.y, this.body.velocity.x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-13 19:31:45 +00:00
|
|
|
/**
|
|
|
|
* Visual effects renderer for weapons.
|
|
|
|
*/
|
2015-02-20 00:00:00 +00:00
|
|
|
export class WeaponEffect {
|
2017-02-13 19:31:45 +00:00
|
|
|
// Link to game
|
2017-06-22 22:37:38 +00:00
|
|
|
private ui: MainUI
|
2017-02-13 19:31:45 +00:00
|
|
|
|
2017-05-09 20:41:35 +00:00
|
|
|
// Link to arena
|
2017-06-22 22:37:38 +00:00
|
|
|
private arena: Arena
|
2017-08-21 22:30:34 +00:00
|
|
|
private view: BattleView
|
2017-05-09 20:41:35 +00:00
|
|
|
|
2017-07-23 17:31:46 +00:00
|
|
|
// Timer to use
|
|
|
|
private timer: Timer
|
|
|
|
|
2017-02-13 19:31:45 +00:00
|
|
|
// Display group in which to display the visual effects
|
2017-06-22 22:37:38 +00:00
|
|
|
private layer: Phaser.Group
|
2015-03-11 00:00:00 +00:00
|
|
|
|
2015-02-20 00:00:00 +00:00
|
|
|
// Firing ship
|
2017-06-22 22:37:38 +00:00
|
|
|
private ship: Ship
|
|
|
|
private source: IArenaLocation
|
2015-02-20 00:00:00 +00:00
|
|
|
|
2017-06-22 22:37:38 +00:00
|
|
|
// Target (ship or space)
|
|
|
|
private target: Target
|
|
|
|
private destination: IArenaLocation
|
2015-02-20 00:00:00 +00:00
|
|
|
|
2017-02-15 16:41:24 +00:00
|
|
|
// Weapon used
|
2017-06-22 22:37:38 +00:00
|
|
|
private weapon: Equipment
|
2015-02-20 00:00:00 +00:00
|
|
|
|
|
|
|
// Effect in use
|
2017-06-22 22:37:38 +00:00
|
|
|
private effect: Function
|
2015-02-20 00:00:00 +00:00
|
|
|
|
2017-06-22 22:37:38 +00:00
|
|
|
constructor(arena: Arena, ship: Ship, target: Target, weapon: Equipment) {
|
2017-02-13 19:31:45 +00:00
|
|
|
this.ui = arena.getGame();
|
2017-05-09 20:41:35 +00:00
|
|
|
this.arena = arena;
|
2017-08-21 22:30:34 +00:00
|
|
|
this.view = arena.battleview;
|
2017-07-23 17:31:46 +00:00
|
|
|
this.timer = arena.battleview.timer;
|
2017-02-13 19:31:45 +00:00
|
|
|
this.layer = arena.layer_weapon_effects;
|
2017-06-22 22:37:38 +00:00
|
|
|
this.ship = ship;
|
|
|
|
this.target = target;
|
2015-02-20 00:00:00 +00:00
|
|
|
this.weapon = weapon;
|
2017-02-15 16:41:24 +00:00
|
|
|
this.effect = this.getEffectForWeapon(weapon.code);
|
2017-06-22 22:37:38 +00:00
|
|
|
|
|
|
|
this.source = this.getCoords(Target.newFromShip(this.ship));
|
|
|
|
this.destination = this.getCoords(this.target);
|
2015-02-20 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
2017-02-14 00:30:50 +00:00
|
|
|
/**
|
|
|
|
* Start the visual effect
|
|
|
|
*
|
|
|
|
* Returns the duration of the effect.
|
|
|
|
*/
|
|
|
|
start(): number {
|
2015-02-20 00:00:00 +00:00
|
|
|
if (this.effect) {
|
2017-02-15 16:41:24 +00:00
|
|
|
return this.effect();
|
2017-02-14 00:30:50 +00:00
|
|
|
} else {
|
|
|
|
return 0;
|
2015-02-20 00:00:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:37:38 +00:00
|
|
|
/**
|
|
|
|
* Get the location of a target (as of current view, not as actual game state)
|
|
|
|
*/
|
|
|
|
getCoords(target: Target): IArenaLocation {
|
|
|
|
if (target.ship) {
|
|
|
|
let sprite = this.arena.findShipSprite(target.ship);
|
|
|
|
if (sprite) {
|
|
|
|
return sprite;
|
|
|
|
} else {
|
|
|
|
return target.ship.location;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-15 16:41:24 +00:00
|
|
|
/**
|
|
|
|
* Get the function that will be called to start the visual effect
|
|
|
|
*/
|
2015-02-20 00:00:00 +00:00
|
|
|
getEffectForWeapon(weapon: string): Function {
|
2015-03-16 00:00:00 +00:00
|
|
|
switch (weapon) {
|
|
|
|
case "gatlinggun":
|
2017-02-15 16:41:24 +00:00
|
|
|
return this.gunEffect.bind(this);
|
2015-03-16 00:00:00 +00:00
|
|
|
default:
|
2017-02-15 16:41:24 +00:00
|
|
|
return this.defaultEffect.bind(this);
|
2015-03-16 00:00:00 +00:00
|
|
|
}
|
2015-02-20 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
2017-02-13 19:31:45 +00:00
|
|
|
/**
|
|
|
|
* Add a shield impact effect on a ship
|
|
|
|
*/
|
2017-06-22 22:37:38 +00:00
|
|
|
shieldImpactEffect(from: IArenaLocation, ship: IArenaLocation, delay: number, duration: number, particles = false) {
|
2017-02-13 19:31:45 +00:00
|
|
|
let angle = Math.atan2(from.y - ship.y, from.x - ship.x);
|
|
|
|
|
2017-08-21 22:30:34 +00:00
|
|
|
let effect = this.view.newImage("battle-effects-shield-impact", ship.x, ship.y);
|
2017-02-13 19:31:45 +00:00
|
|
|
effect.alpha = 0;
|
|
|
|
effect.rotation = angle;
|
|
|
|
effect.anchor.set(0.5, 0.5);
|
2017-07-23 17:31:46 +00:00
|
|
|
this.layer.add(effect);
|
2017-02-13 19:31:45 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2017-05-09 20:41:35 +00:00
|
|
|
if (particles) {
|
2017-08-21 22:30:34 +00:00
|
|
|
let image = this.view.getImageInfo("battle-effects-hot");
|
2017-05-09 20:41:35 +00:00
|
|
|
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;
|
2017-08-21 22:30:34 +00:00
|
|
|
emitter.makeParticles(image.key, image.frame);
|
2017-05-09 20:41:35 +00:00
|
|
|
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);
|
2017-07-23 17:31:46 +00:00
|
|
|
this.timer.schedule(delay, () => emitter.start(false, 200, 30, duration * 0.8 / 30));
|
|
|
|
this.layer.add(emitter);
|
|
|
|
this.timer.schedule(delay + duration + 5000, () => emitter.destroy());
|
2017-05-09 20:41:35 +00:00
|
|
|
}
|
2017-02-14 00:30:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a hull impact effect on a ship
|
|
|
|
*/
|
2017-06-22 22:37:38 +00:00
|
|
|
hullImpactEffect(from: IArenaLocation, ship: IArenaLocation, delay: number, duration: number) {
|
2017-02-14 00:30:50 +00:00
|
|
|
let angle = Math.atan2(from.y - ship.y, from.x - ship.x);
|
|
|
|
|
2017-08-21 22:30:34 +00:00
|
|
|
let image = this.view.getImageInfo("battle-effects-hot");
|
2017-02-14 00:30:50 +00:00
|
|
|
let emitter = this.ui.add.emitter(ship.x + Math.cos(angle) * 10, ship.y + Math.sin(angle) * 10, 30);
|
|
|
|
emitter.minParticleScale = 1.0;
|
|
|
|
emitter.maxParticleScale = 2.0;
|
|
|
|
emitter.gravity = 0;
|
2017-08-21 22:30:34 +00:00
|
|
|
emitter.makeParticles(image.key, image.frame);
|
2017-02-14 00:30:50 +00:00
|
|
|
emitter.setSize(15, 15);
|
|
|
|
emitter.setRotation(0, 0);
|
|
|
|
emitter.setXSpeed(-Math.cos(angle) * 120, -Math.cos(angle) * 260);
|
|
|
|
emitter.setYSpeed(-Math.sin(angle) * 120, -Math.sin(angle) * 260);
|
2017-07-23 17:31:46 +00:00
|
|
|
this.timer.schedule(delay, () => emitter.start(false, 200, 30, duration * 0.8 / 30));
|
|
|
|
this.layer.add(emitter);
|
|
|
|
this.timer.schedule(delay + duration + 5000, () => emitter.destroy());
|
2017-02-13 19:31:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default firing effect
|
|
|
|
*/
|
2017-02-14 00:30:50 +00:00
|
|
|
defaultEffect(): number {
|
2017-05-17 16:21:14 +00:00
|
|
|
this.ui.audio.playOnce("battle-weapon-missile-launch");
|
|
|
|
|
2017-08-21 22:30:34 +00:00
|
|
|
let missile = this.view.newImage("battle-effects-default", this.source.x, this.source.y);
|
2015-03-16 00:00:00 +00:00
|
|
|
missile.anchor.set(0.5, 0.5);
|
2017-06-22 22:37:38 +00:00
|
|
|
missile.rotation = arenaAngle(this.source, this.destination);
|
2017-07-23 17:31:46 +00:00
|
|
|
this.layer.add(missile);
|
2015-03-16 00:00:00 +00:00
|
|
|
|
2017-06-22 22:37:38 +00:00
|
|
|
let blast_radius = this.weapon.action.getBlastRadius(this.ship);
|
2017-04-18 19:51:23 +00:00
|
|
|
|
2017-08-17 21:26:02 +00:00
|
|
|
let projectile_duration = arenaDistance(this.source, this.destination) * 1.5;
|
2017-02-15 16:41:24 +00:00
|
|
|
let tween = this.ui.tweens.create(missile);
|
2017-08-17 21:26:02 +00:00
|
|
|
tween.to({ x: this.destination.x, y: this.destination.y }, projectile_duration || 1);
|
2015-03-16 00:00:00 +00:00
|
|
|
tween.onComplete.addOnce(() => {
|
|
|
|
missile.destroy();
|
2017-04-18 19:51:23 +00:00
|
|
|
if (blast_radius > 0) {
|
2017-05-17 16:21:14 +00:00
|
|
|
this.ui.audio.playOnce("battle-weapon-missile-explosion");
|
|
|
|
|
2017-08-21 22:30:34 +00:00
|
|
|
let blast = this.view.newImage("battle-effects-blast", this.destination.x, this.destination.y);
|
2017-04-18 19:51:23 +00:00
|
|
|
let scaling = blast_radius * 2 / (blast.width * 0.9);
|
2017-02-15 16:41:24 +00:00
|
|
|
blast.anchor.set(0.5, 0.5);
|
|
|
|
blast.scale.set(0.001, 0.001);
|
|
|
|
let tween1 = this.ui.tweens.create(blast.scale).to({ x: scaling, y: scaling }, 1500, Phaser.Easing.Quintic.Out);
|
|
|
|
tween1.onComplete.addOnce(() => blast.destroy());
|
|
|
|
tween1.start();
|
|
|
|
let tween2 = this.ui.tweens.create(blast).to({ alpha: 0 }, 1450, Phaser.Easing.Quadratic.In);
|
|
|
|
tween2.start();
|
2017-07-23 17:31:46 +00:00
|
|
|
this.layer.add(blast);
|
2017-02-15 16:41:24 +00:00
|
|
|
}
|
2015-03-16 00:00:00 +00:00
|
|
|
});
|
|
|
|
tween.start();
|
2017-02-14 00:30:50 +00:00
|
|
|
|
2017-08-17 21:26:02 +00:00
|
|
|
if (blast_radius > 0 && this.weapon.action instanceof FireWeaponAction) {
|
|
|
|
if (any(this.weapon.action.effects, effect => effect instanceof DamageEffect)) {
|
|
|
|
let ships = this.arena.getShipsInCircle(new ArenaCircleArea(this.destination.x, this.destination.y, blast_radius));
|
|
|
|
ships.forEach(sprite => {
|
|
|
|
if (sprite.getValue("shield") > 0) {
|
|
|
|
this.shieldImpactEffect(this.target, sprite, 1200, 800);
|
|
|
|
} else {
|
|
|
|
this.hullImpactEffect(this.target, sprite, 1200, 400);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2017-05-09 20:41:35 +00:00
|
|
|
}
|
|
|
|
|
2017-08-17 21:26:02 +00:00
|
|
|
return projectile_duration + (blast_radius ? 1500 : 0);
|
2015-03-16 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
2017-02-13 19:31:45 +00:00
|
|
|
/**
|
|
|
|
* Submachine gun effect (quick chain of small bullets)
|
|
|
|
*/
|
2017-02-14 00:30:50 +00:00
|
|
|
gunEffect(): number {
|
2017-02-13 19:31:45 +00:00
|
|
|
this.ui.audio.playOnce("battle-weapon-bullets");
|
2015-02-20 00:00:00 +00:00
|
|
|
|
2017-06-22 22:37:38 +00:00
|
|
|
let sprite = this.target.ship ? this.arena.findShipSprite(this.target.ship) : null;
|
|
|
|
let has_shield = sprite && sprite.getValue("shield") > 0;
|
2017-02-13 19:31:45 +00:00
|
|
|
|
2017-08-21 22:30:34 +00:00
|
|
|
let angle = arenaAngle(this.source, this.target);
|
|
|
|
let distance = arenaDistance(this.source, this.target);
|
|
|
|
let image = this.view.getImageInfo("battle-effects-bullets");
|
|
|
|
let emitter = this.ui.add.emitter(this.source.x + Math.cos(angle) * 35, this.source.y + Math.sin(angle) * 35, 10);
|
|
|
|
let speed = 2000;
|
2015-02-20 00:00:00 +00:00
|
|
|
emitter.particleClass = BulletParticle;
|
|
|
|
emitter.gravity = 0;
|
2017-02-13 19:31:45 +00:00
|
|
|
emitter.setSize(5, 5);
|
2015-02-20 00:00:00 +00:00
|
|
|
emitter.setRotation(0, 0);
|
2017-02-13 19:31:45 +00:00
|
|
|
emitter.setXSpeed(Math.cos(angle) * speed, Math.cos(angle) * speed);
|
|
|
|
emitter.setYSpeed(Math.sin(angle) * speed, Math.sin(angle) * speed);
|
2017-08-21 22:30:34 +00:00
|
|
|
emitter.makeParticles(image.key, image.frame);
|
2017-02-14 00:30:50 +00:00
|
|
|
let guard = 50 + (has_shield ? 80 : 40);
|
|
|
|
if (guard + 1 > distance) {
|
|
|
|
guard = distance - 1;
|
|
|
|
}
|
|
|
|
emitter.start(false, 1000 * (distance - guard) / speed, 50, 10);
|
2017-07-23 17:31:46 +00:00
|
|
|
this.layer.add(emitter);
|
|
|
|
this.timer.schedule(5000, () => emitter.destroy());
|
2017-02-13 19:31:45 +00:00
|
|
|
|
|
|
|
if (has_shield) {
|
2017-06-22 22:37:38 +00:00
|
|
|
this.shieldImpactEffect(this.source, this.target, 100, 800, true);
|
2017-02-14 00:30:50 +00:00
|
|
|
} else {
|
2017-06-22 22:37:38 +00:00
|
|
|
this.hullImpactEffect(this.source, this.target, 100, 800);
|
2017-02-13 19:31:45 +00:00
|
|
|
}
|
2017-02-14 00:30:50 +00:00
|
|
|
|
|
|
|
return 1000;
|
2015-02-20 00:00:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|