Added drone animation when applying effects
This commit is contained in:
parent
27057bbef9
commit
12ee9b823b
6
TODO
6
TODO
|
@ -4,16 +4,14 @@
|
|||
* Quick loading does not cancel pending "setTimeout"s.
|
||||
* 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
|
||||
* Drones: fix not being removed when owner is in statis (owner's turn is skipped)
|
||||
* More sound effects
|
||||
* Do not apply effects on ships in stasis
|
||||
* 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
|
||||
* Allow to cancel last moves
|
||||
* Identify ships in emergency stasis more clearly
|
||||
* Add more visual effects to weapons, hits and explosions
|
||||
* Effect should be random in a range (eg. "damage target 50-75")
|
||||
* Add an overload/cooling system
|
||||
* Add auto-move to attack
|
||||
|
@ -37,4 +35,6 @@
|
|||
* 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
|
||||
* Missions/quests system
|
||||
* Main story arc
|
||||
* Multiplayer
|
|
@ -123,5 +123,22 @@ module TS.SpaceTac {
|
|||
drone.onTurnStart(owner);
|
||||
expect(removeDrone).toHaveBeenCalledWith(drone);
|
||||
});
|
||||
|
||||
it("logs each activation", function () {
|
||||
let battle = new Battle();
|
||||
let ship = new Ship();
|
||||
ship.fleet.setBattle(battle);
|
||||
let other = new Ship();
|
||||
|
||||
let drone = new Drone(ship);
|
||||
drone.apply([ship, other]);
|
||||
drone.apply([]);
|
||||
drone.apply([other]);
|
||||
|
||||
expect(battle.log.events).toEqual([
|
||||
new DroneAppliedEvent(drone, [ship, other]),
|
||||
new DroneAppliedEvent(drone, [other])
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -32,30 +32,32 @@ module TS.SpaceTac {
|
|||
}
|
||||
|
||||
/**
|
||||
* Call a function for each ship in radius.
|
||||
* Filter the list of ships in radius.
|
||||
*/
|
||||
forEachInRadius(ships: Ship[], callback: (ship: Ship) => any) {
|
||||
ships.forEach(ship => {
|
||||
if (ship.isInCircle(this.x, this.y, this.radius)) {
|
||||
callback(ship);
|
||||
}
|
||||
});
|
||||
filterShipsInRadius(ships: Ship[]): Ship[] {
|
||||
return ships.filter(ship => ship.isInCircle(this.x, this.y, this.radius));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the effects on a single ship.
|
||||
* Apply the effects on a list of ships
|
||||
*
|
||||
* This does not check if the ship is in range.
|
||||
* This does not check if the ships are in range.
|
||||
*/
|
||||
singleApply(ship: Ship) {
|
||||
this.effects.forEach(effect => effect.applyOnShip(ship));
|
||||
apply(ships: Ship[], log = true) {
|
||||
if (ships.length > 0) {
|
||||
let battle = this.owner.getBattle();
|
||||
if (battle && log) {
|
||||
battle.log.add(new DroneAppliedEvent(this, ships));
|
||||
}
|
||||
ships.forEach(ship => this.effects.forEach(effect => effect.applyOnShip(ship)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the drone is first deployed.
|
||||
*/
|
||||
onDeploy(ships: Ship[]) {
|
||||
this.forEachInRadius(ships, ship => this.singleApply(ship));
|
||||
this.apply(this.filterShipsInRadius(ships));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +91,7 @@ module TS.SpaceTac {
|
|||
*/
|
||||
onTurnEnd(ship: Ship) {
|
||||
if (this.duration > 0 && ship.isInCircle(this.x, this.y, this.radius) && contains(this.inside_at_start, ship)) {
|
||||
this.singleApply(ship);
|
||||
this.apply([ship]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +101,7 @@ module TS.SpaceTac {
|
|||
onShipMove(ship: Ship) {
|
||||
if (this.duration > 0 && ship.isInCircle(this.x, this.y, this.radius)) {
|
||||
if (add(this.inside, ship)) {
|
||||
this.singleApply(ship);
|
||||
this.apply([ship]);
|
||||
}
|
||||
} else {
|
||||
remove(this.inside, ship);
|
||||
|
|
|
@ -18,9 +18,9 @@ module TS.SpaceTac.Equipments {
|
|||
expect(drone.duration).toBe(1);
|
||||
ship.setAttribute("hull_capacity", 100);
|
||||
ship.setValue("hull", 85);
|
||||
drone.singleApply(ship);
|
||||
drone.apply([ship]);
|
||||
expect(ship.getValue("hull")).toBe(95);
|
||||
drone.singleApply(ship);
|
||||
drone.apply([ship]);
|
||||
expect(ship.getValue("hull")).toBe(100);
|
||||
});
|
||||
});
|
||||
|
|
21
src/core/events/DroneAppliedEvent.ts
Normal file
21
src/core/events/DroneAppliedEvent.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/// <reference path="BaseLogEvent.ts"/>
|
||||
|
||||
module TS.SpaceTac {
|
||||
/**
|
||||
* Event logged when a drone applies its effects
|
||||
*/
|
||||
export class DroneAppliedEvent extends BaseLogEvent {
|
||||
// Pointer to the drone
|
||||
drone: Drone;
|
||||
|
||||
// List of impacted ships
|
||||
ships: Ship[];
|
||||
|
||||
constructor(drone: Drone, ships: Ship[]) {
|
||||
super("droneapply", drone.owner);
|
||||
|
||||
this.drone = drone;
|
||||
this.ships = ships;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -139,22 +139,38 @@ module TS.SpaceTac.UI {
|
|||
this.battleview.gameui.audio.playOnce("battle-ship-change");
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an ArenaDrone displaying a Drone.
|
||||
*/
|
||||
findDrone(drone: Drone): ArenaDrone | null {
|
||||
return first(this.drone_sprites, sprite => sprite.drone == drone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn a new drone
|
||||
*
|
||||
* Return the duration of deploy animation
|
||||
*/
|
||||
addDrone(drone: Drone): number {
|
||||
if (!any(this.drone_sprites, sprite => sprite.drone == drone)) {
|
||||
addDrone(drone: Drone, animate = true): number {
|
||||
if (!this.findDrone(drone)) {
|
||||
let sprite = new ArenaDrone(this.battleview, drone);
|
||||
let angle = Math.atan2(drone.y - drone.owner.arena_y, drone.x - drone.owner.arena_x);
|
||||
this.addChild(sprite);
|
||||
this.drone_sprites.push(sprite);
|
||||
|
||||
sprite.position.set(drone.owner.arena_x, drone.owner.arena_y);
|
||||
this.game.tweens.create(sprite.position).to({ x: drone.x, y: drone.y }, 1800, Phaser.Easing.Sinusoidal.InOut, true, 200);
|
||||
this.game.tweens.create(sprite.radius.scale).from({ x: 0.01, y: 0.01 }, 1800, Phaser.Easing.Linear.None, true, 200);
|
||||
if (animate) {
|
||||
sprite.position.set(drone.owner.arena_x, drone.owner.arena_y);
|
||||
sprite.rotation = drone.owner.arena_angle;
|
||||
let move_duration = Animation.moveInSpace(sprite, drone.x, drone.y, angle);
|
||||
this.game.tweens.create(sprite.radius).from({ alpha: 0 }, 500, Phaser.Easing.Cubic.In, true, move_duration);
|
||||
|
||||
return move_duration + 500;
|
||||
} else {
|
||||
sprite.position.set(drone.x, drone.y);
|
||||
sprite.rotation = angle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 2000;
|
||||
} else {
|
||||
console.error("Drone added twice to arena", drone);
|
||||
return 0;
|
||||
|
@ -163,7 +179,7 @@ module TS.SpaceTac.UI {
|
|||
|
||||
// Remove a destroyed drone
|
||||
removeDrone(drone: Drone): void {
|
||||
let sprite = first(this.drone_sprites, sprite => sprite.drone == drone);
|
||||
let sprite = this.findDrone(drone);
|
||||
if (sprite) {
|
||||
remove(this.drone_sprites, sprite);
|
||||
sprite.destroy();
|
||||
|
|
|
@ -7,27 +7,52 @@ module TS.SpaceTac.UI {
|
|||
drone: Drone;
|
||||
|
||||
// Sprite
|
||||
sprite: Phaser.Button;
|
||||
sprite: Phaser.Image;
|
||||
|
||||
// Radius
|
||||
radius: Phaser.Graphics;
|
||||
|
||||
// Activation effect
|
||||
activation: Phaser.Graphics;
|
||||
|
||||
constructor(battleview: BattleView, drone: Drone) {
|
||||
super(battleview.game);
|
||||
|
||||
this.drone = drone;
|
||||
|
||||
this.radius = new Phaser.Graphics(this.game, 0, 0);
|
||||
this.radius.lineStyle(3, 0xe9f2f9, 0.5);
|
||||
this.radius.lineStyle(2, 0xe9f2f9, 0.3);
|
||||
this.radius.beginFill(0xe9f2f9, 0.0);
|
||||
this.radius.drawCircle(0, 0, drone.radius * 2);
|
||||
this.radius.endFill();
|
||||
this.addChild(this.radius);
|
||||
|
||||
this.sprite = new Phaser.Button(this.game, 0, 0, `battle-actions-deploy-${drone.code}`);
|
||||
this.activation = new Phaser.Graphics(this.game, 0, 0);
|
||||
this.activation.lineStyle(2, 0xe9f2f9, 0.7);
|
||||
this.activation.beginFill(0xe9f2f9, 0.0);
|
||||
this.activation.drawCircle(0, 0, drone.radius * 2);
|
||||
this.activation.endFill();
|
||||
this.activation.visible = false;
|
||||
this.addChild(this.activation);
|
||||
|
||||
this.sprite = new Phaser.Image(this.game, 0, 0, `battle-actions-deploy-${drone.code}`);
|
||||
this.sprite.anchor.set(0.5, 0.5);
|
||||
this.sprite.scale.set(0.1, 0.1);
|
||||
this.addChild(this.sprite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the activation animation
|
||||
*
|
||||
* Return the animation duration
|
||||
*/
|
||||
setApplied(): number {
|
||||
this.activation.scale.set(0.001, 0.001);
|
||||
this.activation.visible = true;
|
||||
let tween = this.game.tweens.create(this.activation.scale).to({ x: 1, y: 1 }, 500);
|
||||
tween.onComplete.addOnce(() => this.activation.visible = false);
|
||||
tween.start();
|
||||
return 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,6 @@ module TS.SpaceTac.UI {
|
|||
// Effects display
|
||||
effects: Phaser.Group;
|
||||
|
||||
// Previous position
|
||||
private prevx;
|
||||
private prevy;
|
||||
|
||||
// Create a ship sprite usable in the Arena
|
||||
constructor(parent: Arena, ship: Ship) {
|
||||
super(parent.game);
|
||||
|
@ -56,17 +52,15 @@ module TS.SpaceTac.UI {
|
|||
Tools.setHoverClick(this.sprite, () => battleview.cursorOnShip(ship), () => battleview.cursorOffShip(ship), () => battleview.cursorClicked());
|
||||
|
||||
// Set location
|
||||
this.prevx = ship.arena_x;
|
||||
this.prevy = ship.arena_y;
|
||||
this.position.set(ship.arena_x, ship.arena_y);
|
||||
}
|
||||
|
||||
update() {
|
||||
if (this.prevx != this.x || this.prevy != this.y) {
|
||||
/*if (this.prevx != this.x || this.prevy != this.y) {
|
||||
this.sprite.rotation = Math.atan2(this.y - this.prevy, this.x - this.prevx);
|
||||
}
|
||||
this.prevx = this.x;
|
||||
this.prevy = this.y;
|
||||
this.prevy = this.y;*/
|
||||
}
|
||||
|
||||
// Set the hovered state on this ship
|
||||
|
@ -88,24 +82,7 @@ module TS.SpaceTac.UI {
|
|||
*/
|
||||
moveTo(x: number, y: number, facing_angle: number, animate = true): number {
|
||||
if (animate) {
|
||||
if (x == this.x && y == this.y) {
|
||||
let tween = this.game.tweens.create(this.sprite);
|
||||
let duration = Animation.rotationTween(tween, facing_angle, 0.3);
|
||||
tween.start();
|
||||
return duration;
|
||||
} else {
|
||||
let distance = Target.newFromLocation(this.x, this.y).getDistanceTo(Target.newFromLocation(x, y));
|
||||
var tween = this.game.tweens.create(this);
|
||||
let duration = Math.sqrt(distance / 1000) * 3000;
|
||||
let curve_force = distance * 0.4;
|
||||
tween.to({
|
||||
x: [this.x + Math.cos(this.sprite.rotation) * curve_force, x - Math.cos(facing_angle) * curve_force, x],
|
||||
y: [this.y + Math.sin(this.sprite.rotation) * curve_force, y - Math.sin(facing_angle) * curve_force, y]
|
||||
}, duration, Phaser.Easing.Sinusoidal.InOut);
|
||||
tween.interpolation((v, k) => Phaser.Math.bezierInterpolation(v, k));
|
||||
tween.start();
|
||||
return duration;
|
||||
}
|
||||
return Animation.moveInSpace(this, x, y, facing_angle, this.sprite);
|
||||
} else {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
|
|
@ -161,15 +161,13 @@ module TS.SpaceTac.UI {
|
|||
this.ship_hovered = ship;
|
||||
this.arena.setShipHovered(ship);
|
||||
this.ship_list.setHovered(ship);
|
||||
this.ship_tooltip.setShip(ship);
|
||||
if (this.targetting) {
|
||||
if (ship) {
|
||||
this.targetting.setTargetShip(ship);
|
||||
} else {
|
||||
this.targetting.unsetTarget();
|
||||
}
|
||||
this.ship_tooltip.setShip(null);
|
||||
} else {
|
||||
this.ship_tooltip.setShip(ship);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,8 @@ module TS.SpaceTac.UI {
|
|||
this.processDroneDeployedEvent(event);
|
||||
} else if (event instanceof DroneDestroyedEvent) {
|
||||
this.processDroneDestroyedEvent(event);
|
||||
} else if (event instanceof DroneAppliedEvent) {
|
||||
this.processDroneAppliedEvent(event);
|
||||
} else if (event.code == "effectadd" || event.code == "effectduration" || event.code == "effectdel") {
|
||||
this.processEffectEvent(event);
|
||||
}
|
||||
|
@ -183,5 +185,14 @@ module TS.SpaceTac.UI {
|
|||
private processDroneDestroyedEvent(event: DroneDestroyedEvent): void {
|
||||
this.view.arena.removeDrone(event.drone);
|
||||
}
|
||||
|
||||
// Drone applied
|
||||
private processDroneAppliedEvent(event: DroneAppliedEvent): void {
|
||||
let drone = this.view.arena.findDrone(event.drone);
|
||||
if (drone) {
|
||||
let duration = drone.setApplied();
|
||||
this.delayNextEvents(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
module TS.SpaceTac.UI {
|
||||
interface PhaserGraphics {
|
||||
x: number;
|
||||
y: number;
|
||||
rotation: number;
|
||||
game: Phaser.Game;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility functions for animation
|
||||
*/
|
||||
|
@ -60,5 +67,40 @@ module TS.SpaceTac.UI {
|
|||
|
||||
return duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an object move toward a location in space, with a ship-like animation.
|
||||
*
|
||||
* Returns the animation duration.
|
||||
*/
|
||||
static moveInSpace(obj: PhaserGraphics, x: number, y: number, angle: number, rotated_obj = obj): number {
|
||||
if (x == obj.x && y == obj.y) {
|
||||
let tween = obj.game.tweens.create(rotated_obj);
|
||||
let duration = Animation.rotationTween(tween, angle, 0.3);
|
||||
tween.start();
|
||||
return duration;
|
||||
} else {
|
||||
let distance = Target.newFromLocation(obj.x, obj.y).getDistanceTo(Target.newFromLocation(x, y));
|
||||
var tween = obj.game.tweens.create(obj);
|
||||
let duration = Math.sqrt(distance / 1000) * 3000;
|
||||
let curve_force = distance * 0.4;
|
||||
tween.to({
|
||||
x: [obj.x + Math.cos(rotated_obj.rotation) * curve_force, x - Math.cos(angle) * curve_force, x],
|
||||
y: [obj.y + Math.sin(rotated_obj.rotation) * curve_force, y - Math.sin(angle) * curve_force, y]
|
||||
}, duration, Phaser.Easing.Sinusoidal.InOut);
|
||||
tween.interpolation((v, k) => Phaser.Math.bezierInterpolation(v, k));
|
||||
let prevx = obj.x;
|
||||
let prevy = obj.y;
|
||||
tween.onUpdateCallback(() => {
|
||||
if (prevx != obj.x || prevy != obj.y) {
|
||||
rotated_obj.rotation = Math.atan2(obj.y - prevy, obj.x - prevx);
|
||||
}
|
||||
prevx = obj.x;
|
||||
prevy = obj.y;
|
||||
});
|
||||
tween.start();
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue