1
0
Fork 0
spacetac/src/ui/battle/Arena.ts

285 lines
9.8 KiB
TypeScript
Raw Normal View History

2017-09-24 22:23:22 +00:00
module TK.SpaceTac.UI {
2017-05-17 21:55:39 +00:00
/**
* Graphical representation of a battle
*
* This is the area in the BattleView that will display ships with their real positions
*/
2017-09-19 15:09:06 +00:00
export class Arena {
// Link to battleview
2017-09-19 15:09:06 +00:00
view: BattleView
2017-05-17 21:55:39 +00:00
// Boundaries of the arena
2017-09-28 23:18:46 +00:00
boundaries: IBounded = { x: 0, y: 0, width: 1808, height: 948 }
2015-03-03 00:00:00 +00:00
// Hint for weapon or move range
2017-05-17 21:55:39 +00:00
range_hint: RangeHint
// Input capture
private mouse_capture: Phaser.Button
2015-03-03 00:00:00 +00:00
// Input callback to receive mouse move events
2017-05-17 21:55:39 +00:00
private input_callback: any = null
// List of ship sprites
2017-05-17 21:55:39 +00:00
private ship_sprites: ArenaShip[] = []
2017-02-08 18:54:02 +00:00
// List of drone sprites
2017-05-17 21:55:39 +00:00
private drone_sprites: ArenaDrone[] = []
// Currently hovered ship
2017-05-17 21:55:39 +00:00
private hovered: ArenaShip | null
// Currently playing ship
2017-05-17 21:55:39 +00:00
private playing: ArenaShip | null
// Layer for particles
2017-09-19 15:09:06 +00:00
container: Phaser.Group
2017-05-17 21:55:39 +00:00
layer_garbage: Phaser.Group
layer_hints: Phaser.Group
layer_drones: Phaser.Group
layer_ships: Phaser.Group
layer_weapon_effects: Phaser.Group
layer_targetting: Phaser.Group
2017-09-19 15:09:06 +00:00
// Callbacks to receive cursor events
callbacks_hover: ((location: ArenaLocation | null, ship: Ship | null) => void)[] = []
callbacks_click: (() => void)[] = []
2017-09-19 15:09:06 +00:00
// Create a graphical arena for ship sprites to fight in a 2D space
constructor(view: BattleView, container?: Phaser.Group) {
this.view = view;
this.playing = null;
this.hovered = null;
2017-03-09 17:11:00 +00:00
this.range_hint = new RangeHint(this);
2017-09-19 15:09:06 +00:00
this.container = container || new Phaser.Group(view.game, undefined, "arena");
this.container.position.set(this.boundaries.x, this.boundaries.y);
this.setupMouseCapture();
this.layer_garbage = this.container.add(new Phaser.Group(view.game, undefined, "garbage"));
this.layer_hints = this.container.add(new Phaser.Group(view.game, undefined, "hints"));
this.layer_drones = this.container.add(new Phaser.Group(view.game, undefined, "drones"));
this.layer_ships = this.container.add(new Phaser.Group(view.game, undefined, "ships"));
this.layer_weapon_effects = this.container.add(new Phaser.Group(view.game, undefined, "effects"));
this.layer_targetting = this.container.add(new Phaser.Group(view.game, undefined, "targetting"));
this.range_hint.setLayer(this.layer_hints);
this.addShipSprites();
2017-05-17 21:55:39 +00:00
2017-09-19 15:09:06 +00:00
this.container.onDestroy.add(() => this.destroy());
}
/**
* Move to a specific layer
*/
moveToLayer(layer: Phaser.Group): void {
layer.add(this.container);
2017-05-17 21:55:39 +00:00
}
/**
* Setup the mouse capture for targetting events
*/
setupMouseCapture() {
2017-09-19 15:09:06 +00:00
let view = this.view;
2017-09-19 15:09:06 +00:00
var background = new Phaser.Button(view.game, 0, 0, "battle-arena-background");
background.name = "mouse-capture";
2017-05-17 21:55:39 +00:00
background.scale.set(this.boundaries.width / background.width, this.boundaries.height / background.height);
this.mouse_capture = background;
// Capture clicks on background
background.onInputUp.add(() => {
2017-09-19 15:09:06 +00:00
this.callbacks_click.forEach(callback => callback());
});
background.onInputOut.add(() => {
2017-09-19 15:09:06 +00:00
this.callbacks_hover.forEach(callback => callback(null, null));
});
// Watch mouse move to capture hovering over background
2017-09-19 15:09:06 +00:00
this.input_callback = this.view.input.addMoveCallback((pointer: Phaser.Pointer) => {
var point = new Phaser.Point();
2017-09-19 15:09:06 +00:00
if (view.input.hitTest(background, pointer, point)) {
let location = new ArenaLocation(point.x * background.scale.x, point.y * background.scale.y);
let ship = this.getShip(location);
this.callbacks_hover.forEach(callback => callback(location, ship));
}
}, null);
2017-09-19 15:09:06 +00:00
this.container.add(this.mouse_capture);
}
2017-09-19 15:09:06 +00:00
/**
* Get the ship under a cursor location
*/
getShip(location: ArenaLocation): Ship | null {
let nearest = minBy(this.ship_sprites, sprite => arenaDistance(location, sprite.ship.location));
if (nearest && arenaDistance(location, nearest) < 50) {
return nearest.ship;
} else {
return null;
2017-05-17 21:55:39 +00:00
}
}
2017-05-17 21:55:39 +00:00
/**
2017-09-19 15:09:06 +00:00
* Call when the arena is destroyed to properly remove input handlers
2017-05-17 21:55:39 +00:00
*/
2017-09-19 15:09:06 +00:00
destroy() {
if (this.input_callback) {
this.view.input.deleteMoveCallback(this.input_callback);
this.input_callback = null;
}
2017-05-17 21:55:39 +00:00
}
/**
* Add the sprites for all ships
*/
addShipSprites() {
2017-09-19 15:09:06 +00:00
iforeach(this.view.battle.iships(), ship => {
2017-05-17 21:55:39 +00:00
let sprite = new ArenaShip(this, ship);
this.layer_ships.add(sprite);
this.ship_sprites.push(sprite);
});
}
2017-09-19 15:09:06 +00:00
/**
* Get the current MainUI instance
*/
get game(): MainUI {
return this.view.gameui;
}
2017-05-09 20:41:35 +00:00
/**
* Get the current battle displayed
*/
getBattle(): Battle {
2017-09-19 15:09:06 +00:00
return this.view.battle;
2017-05-09 20:41:35 +00:00
}
2015-02-18 00:00:00 +00:00
// Remove a ship sprite
2017-02-09 00:00:35 +00:00
markAsDead(ship: Ship): void {
2015-02-18 00:00:00 +00:00
var sprite = this.findShipSprite(ship);
if (sprite) {
2017-02-15 22:34:27 +00:00
sprite.setDead(true);
2017-05-17 23:24:42 +00:00
this.layer_garbage.add(sprite);
2015-02-18 00:00:00 +00:00
}
}
// Find the sprite for a ship
2017-03-09 17:11:00 +00:00
findShipSprite(ship: Ship): ArenaShip | null {
var result: ArenaShip | null = null;
this.ship_sprites.forEach((sprite: ArenaShip) => {
if (sprite.ship === ship) {
result = sprite;
}
});
return result;
}
// Set the hovered state on a ship sprite
2017-03-09 17:11:00 +00:00
setShipHovered(ship: Ship | null): void {
if (this.hovered) {
2017-05-23 16:42:55 +00:00
this.hovered.setHovered(false, false);
}
2017-03-09 17:11:00 +00:00
if (ship) {
var arena_ship = this.findShipSprite(ship);
if (arena_ship) {
2017-05-23 16:42:55 +00:00
arena_ship.setHovered(true, false);
2017-05-17 21:55:39 +00:00
this.layer_ships.bringToTop(arena_ship);
2017-03-09 17:11:00 +00:00
}
this.hovered = arena_ship;
} else {
this.hovered = null;
}
}
// Set the playing state on a ship sprite
2017-03-09 17:11:00 +00:00
setShipPlaying(ship: Ship | null): void {
if (this.playing) {
this.playing.setPlaying(false);
2017-03-09 17:11:00 +00:00
this.playing = null;
}
2017-03-09 17:11:00 +00:00
if (ship) {
var arena_ship = this.findShipSprite(ship);
if (arena_ship) {
2017-05-17 21:55:39 +00:00
this.layer_ships.bringToTop(arena_ship);
2017-03-09 17:11:00 +00:00
arena_ship.setPlaying(true);
}
this.playing = arena_ship;
}
}
2017-02-08 18:54:02 +00:00
/**
* 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, animate = true): number {
if (!this.findDrone(drone)) {
2017-09-19 15:09:06 +00:00
let sprite = new ArenaDrone(this.view, drone);
let angle = Math.atan2(drone.y - drone.owner.arena_y, drone.x - drone.owner.arena_x);
2017-05-17 21:55:39 +00:00
this.layer_drones.add(sprite);
2017-02-08 18:54:02 +00:00
this.drone_sprites.push(sprite);
if (animate) {
sprite.position.set(drone.owner.arena_x, drone.owner.arena_y);
2017-05-22 23:05:01 +00:00
sprite.sprite.rotation = drone.owner.arena_angle;
let move_duration = Animations.moveInSpace(sprite, drone.x, drone.y, angle, sprite.sprite);
2017-09-19 15:09:06 +00:00
this.view.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);
2017-05-22 23:05:01 +00:00
sprite.sprite.rotation = angle;
return 0;
}
2017-02-08 18:54:02 +00:00
} else {
console.error("Drone added twice to arena", drone);
return 0;
2017-02-08 18:54:02 +00:00
}
}
2017-05-10 23:13:56 +00:00
/**
* Remove a destroyed drone
*/
2017-02-09 00:00:35 +00:00
removeDrone(drone: Drone): void {
let sprite = this.findDrone(drone);
2017-02-08 18:54:02 +00:00
if (sprite) {
remove(this.drone_sprites, sprite);
2017-05-10 23:13:56 +00:00
sprite.setDestroyed();
2017-05-17 21:55:39 +00:00
this.layer_garbage.add(sprite);
2017-02-08 18:54:02 +00:00
} else {
console.error("Drone not found in arena for removal", drone);
}
}
2017-05-15 18:30:44 +00:00
/**
* Switch the tactical mode (shows information on all ships, and fades background)
2017-05-15 18:30:44 +00:00
*/
setTacticalMode(active: boolean): void {
2017-05-23 16:42:55 +00:00
this.ship_sprites.forEach(sprite => sprite.setHovered(active, true));
this.drone_sprites.forEach(drone => drone.setTacticalMode(active));
2017-09-19 15:09:06 +00:00
this.view.animations.setVisible(this.layer_garbage, !active, 200);
if (this.view.background) {
this.view.animations.setVisible(this.view.background, !active, 200);
2017-05-15 18:30:44 +00:00
}
}
2017-05-16 23:12:05 +00:00
/**
* Get the boundaries of the arena on display
*/
getBoundaries(): IBounded {
return this.boundaries;
2017-05-16 23:12:05 +00:00
}
}
}