362 lines
14 KiB
TypeScript
362 lines
14 KiB
TypeScript
module TK.SpaceTac.UI {
|
|
/**
|
|
* Ship sprite in the arena, with corresponding HUD
|
|
*/
|
|
export class ArenaShip extends UIContainer {
|
|
// Link to the view
|
|
arena: Arena
|
|
battleview: BattleView
|
|
|
|
// Link to displayed ship
|
|
ship: Ship
|
|
|
|
// Boolean to indicate if it is an enemy ship
|
|
enemy: boolean
|
|
|
|
// Ship sprite
|
|
sprite: UIContainer
|
|
|
|
// Stasis effect
|
|
stasis: UIImage
|
|
|
|
// Hull display
|
|
hull: ValueBar
|
|
toggle_hull: Toggle
|
|
|
|
// Frames to indicate the owner, if the ship is selected, and if it is hovered
|
|
frame_owner: UIImage
|
|
frame_selected: UIImage
|
|
frame_hover: UIImage
|
|
|
|
// Effects display
|
|
active_effects_display: UIContainer
|
|
effects_radius: UIGraphics
|
|
effects_messages: UIContainer
|
|
effects_messages_toggle: Toggle
|
|
|
|
// Create a ship sprite usable in the Arena
|
|
constructor(parent: Arena, ship: Ship) {
|
|
super(parent.view);
|
|
this.arena = parent;
|
|
this.battleview = parent.view;
|
|
|
|
let builder = new UIBuilder(this.battleview).in(this);
|
|
|
|
this.ship = ship;
|
|
this.enemy = !this.battleview.player.is(this.ship.fleet.player);
|
|
|
|
// Add effects radius
|
|
this.effects_radius = builder.graphics("effect-radius");
|
|
|
|
// Add frame indicating which side this ship is on
|
|
this.frame_owner = builder.image(this.enemy ? "battle-hud-ship-enemy" : "battle-hud-ship-own", 0, 0, true);
|
|
this.frame_owner.setVisible(this.ship.alive && !this.battleview.isTintSupported());
|
|
this.frame_selected = builder.image(this.enemy ? "battle-hud-ship-enemy-selected" : "battle-hud-ship-own-selected", 0, 0, true);
|
|
this.setSelected(false);
|
|
this.frame_hover = builder.image("battle-hud-ship-hover", 0, 0, true);
|
|
this.frame_hover.setVisible(false);
|
|
|
|
// Add ship sprite
|
|
this.sprite = builder.container("sprite");
|
|
if (this.battleview.isTintSupported()) {
|
|
const outline = this.sprite.getBuilder().image(`ship-${ship.model.code}-sprite`, 0, 0, true);
|
|
outline.setScale(1.1);
|
|
outline.setTintFill(this.enemy ? 0xc06858 : 0x65a898);
|
|
}
|
|
this.sprite.getBuilder().image(`ship-${ship.model.code}-sprite`, 0, 0, true);
|
|
this.sprite.setRotation(ship.arena_angle);
|
|
|
|
// Add stasis effect
|
|
this.stasis = builder.image("battle-hud-ship-stasis", 0, 0, true);
|
|
this.stasis.setAlpha(0.9);
|
|
this.stasis.setVisible(!ship.alive);
|
|
|
|
// Hull display
|
|
const hull_container = builder.container("hull");
|
|
builder.in(hull_container).image("battle-hud-hull-background", -34, 48);
|
|
this.hull = builder.in(hull_container).valuebar("battle-hud-hull-bar", -30, 53);
|
|
this.toggle_hull = this.battleview.animations.newVisibilityToggle(hull_container, 200, false);
|
|
|
|
// Effects display
|
|
this.active_effects_display = builder.container("active-effects", 0, -44);
|
|
this.effects_messages = builder.container("effects-messages");
|
|
this.effects_messages_toggle = this.battleview.animations.newVisibilityToggle(this.effects_messages, 500, false);
|
|
|
|
this.updateHull(this.ship.getValue("hull"));
|
|
this.updateActiveEffects();
|
|
this.updateEffectsRadius();
|
|
|
|
// Set location
|
|
if (this.battleview.battle.turncount == 1) {
|
|
this.setPosition(ship.arena_x - 500 * Math.cos(ship.arena_angle), ship.arena_y - 500 * Math.sin(ship.arena_angle));
|
|
this.moveToArenaLocation(ship.arena_x, ship.arena_y, ship.arena_angle, 1);
|
|
} else {
|
|
this.moveToArenaLocation(ship.arena_x, ship.arena_y, ship.arena_angle, 0);
|
|
}
|
|
|
|
// Log processing
|
|
this.battleview.log_processor.registerForShip(ship, diff => this.processShipDiff(diff));
|
|
}
|
|
|
|
jasmineToString(): string {
|
|
return `ArenaShip ${this.ship.jasmineToString()}`;
|
|
}
|
|
|
|
/**
|
|
* Process a ship diff
|
|
*/
|
|
private processShipDiff(diff: BaseBattleShipDiff): LogProcessorDelegate {
|
|
let timer = this.battleview.timer;
|
|
|
|
if (diff instanceof ShipEffectAddedDiff || diff instanceof ShipEffectRemovedDiff) {
|
|
return {
|
|
background: async () => this.updateActiveEffects()
|
|
}
|
|
} else if (diff instanceof ShipValueDiff) {
|
|
return {
|
|
background: async (speed: number) => {
|
|
if (diff.code == "hull") {
|
|
if (speed) {
|
|
this.toggle_hull.manipulate("value")(true);
|
|
|
|
await timer.sleep(500 / speed);
|
|
|
|
for (let i = 0; i < 3; i++) {
|
|
this.updateHull(this.ship.getValue("hull") - diff.diff, diff.diff);
|
|
await timer.sleep(100 / speed);
|
|
this.updateHull(this.ship.getValue("hull"));
|
|
await timer.sleep(100 / speed);
|
|
}
|
|
|
|
await timer.sleep(500 / speed);
|
|
this.toggle_hull.manipulate("value")(false);
|
|
} else {
|
|
this.updateHull(this.ship.getValue("hull"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (diff instanceof ShipAttributeDiff) {
|
|
return {
|
|
background: async (speed: number) => {
|
|
if (speed) {
|
|
this.displayAttributeChanged(diff, speed);
|
|
await timer.sleep(2000 / speed);
|
|
}
|
|
}
|
|
}
|
|
} else if (diff instanceof ShipDamageDiff) {
|
|
return {
|
|
background: async (speed: number) => {
|
|
if (speed) {
|
|
await this.displayEffect(`${diff.theoretical} damage`, false, speed);
|
|
await timer.sleep(1000 / speed);
|
|
}
|
|
}
|
|
}
|
|
} else if (diff instanceof ShipActionToggleDiff) {
|
|
return {
|
|
foreground: async (speed: number) => {
|
|
let action = this.ship.actions.getById(diff.action);
|
|
if (action) {
|
|
if (speed) {
|
|
if (diff.activated) {
|
|
await this.displayEffect(`${action.name} ON`, true, speed);
|
|
} else {
|
|
await this.displayEffect(`${action.name} OFF`, false, speed);
|
|
}
|
|
}
|
|
|
|
this.updateEffectsRadius();
|
|
await timer.sleep(500 / speed);
|
|
}
|
|
}
|
|
}
|
|
} else if (diff instanceof ShipActionUsedDiff) {
|
|
let action = this.ship.actions.getById(diff.action);
|
|
if (action) {
|
|
if (!(action instanceof ToggleAction)) {
|
|
let action_name = action.name;
|
|
return {
|
|
foreground: async (speed: number) => {
|
|
if (speed) {
|
|
await this.displayEffect(action_name, true, speed);
|
|
await timer.sleep(300 / speed);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
return {};
|
|
}
|
|
} else {
|
|
return {};
|
|
}
|
|
} else if (diff instanceof ShipMoveDiff) {
|
|
let func = async (speed: number) => {
|
|
if (speed) {
|
|
await this.moveToArenaLocation(diff.start.x, diff.start.y, diff.start.angle, 0);
|
|
await this.moveToArenaLocation(diff.end.x, diff.end.y, diff.end.angle, speed, !!diff.engine);
|
|
} else {
|
|
await this.moveToArenaLocation(diff.end.x, diff.end.y, diff.end.angle, 0);
|
|
}
|
|
};
|
|
if (diff.engine) {
|
|
return { foreground: func };
|
|
} else {
|
|
return { background: func };
|
|
}
|
|
} else if (diff instanceof VigilanceAppliedDiff) {
|
|
let action = this.ship.actions.getById(diff.action);
|
|
return {
|
|
foreground: async (speed: number) => {
|
|
if (speed && action) {
|
|
await this.displayEffect(`${action.name} (vigilance)`, true, speed);
|
|
await timer.sleep(300 / speed);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the hovered state on this ship
|
|
*
|
|
* This will show the information HUD accordingly
|
|
*/
|
|
setHovered(hovered: boolean, tactical: boolean) {
|
|
let client = tactical ? "tactical" : "hover";
|
|
this.toggle_hull.manipulate(client)(hovered && this.ship.alive);
|
|
this.battleview.animations.setVisible(this.frame_hover, hovered && this.ship.alive && !tactical, 200);
|
|
}
|
|
|
|
/**
|
|
* Set the selected state on this ship
|
|
*
|
|
* This will alter the HUD frame to show this state
|
|
*/
|
|
async setSelected(selected: boolean, animate = true): Promise<void> {
|
|
this.frame_owner.setVisible(this.ship.alive && !this.battleview.isTintSupported());
|
|
this.frame_selected.setVisible(this.ship.alive && selected);
|
|
|
|
if (selected && animate) {
|
|
this.battleview.audio.playOnce("battle-ship-change");
|
|
await this.battleview.animations.blink(this.frame_selected);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Activate the dead effect (stasis)
|
|
*/
|
|
setDead(dead = true) {
|
|
if (dead) {
|
|
//this.displayEffect("stasis", false);
|
|
this.stasis.visible = true;
|
|
this.stasis.alpha = 0;
|
|
this.battleview.animations.blink(this.stasis, { alpha_on: 0.9, alpha_off: 0.7 });
|
|
} else {
|
|
this.stasis.visible = false;
|
|
}
|
|
this.setSelected(false);
|
|
}
|
|
|
|
/**
|
|
* Move the sprite to a location
|
|
*
|
|
* Return the duration of animation
|
|
*/
|
|
async moveToArenaLocation(x: number, y: number, facing_angle: number, speed = 1, engine = true): Promise<void> {
|
|
if (speed) {
|
|
if (engine) {
|
|
await this.arena.view.animations.moveInSpace(this, x, y, facing_angle, this.sprite, speed);
|
|
} else {
|
|
await this.arena.view.animations.moveTo(this, x, y, facing_angle, this.sprite, speed);
|
|
}
|
|
} else {
|
|
this.setPosition(x, y);
|
|
this.sprite.setRotation(facing_angle);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Briefly show an effect on this ship
|
|
*/
|
|
async displayEffect(message: string, beneficial: boolean, speed: number) {
|
|
if (!this.effects_messages.visible) {
|
|
this.effects_messages.removeAll(true);
|
|
}
|
|
|
|
if (!speed) {
|
|
return;
|
|
}
|
|
|
|
let builder = new UIBuilder(this.arena.view, this.effects_messages);
|
|
builder.text(message, 0, 20 * this.effects_messages.length, {
|
|
color: beneficial ? "#D9F9F1" : "#FFD09A"
|
|
});
|
|
|
|
let arena = this.battleview.arena.getBoundaries();
|
|
this.effects_messages.setPosition(
|
|
(this.ship.arena_x < 100) ? 0 : ((this.ship.arena_x > arena.width - 100) ? (-this.effects_messages.width) : (-this.effects_messages.width * 0.5)),
|
|
(this.ship.arena_y < arena.height * 0.9) ? 76 : (-66 - this.effects_messages.height)
|
|
);
|
|
|
|
this.effects_messages_toggle.manipulate("added")(1400 / speed);
|
|
await this.battleview.timer.sleep(1500 / speed);
|
|
}
|
|
|
|
/**
|
|
* Display interesting changes in ship attributes
|
|
*/
|
|
displayAttributeChanged(event: ShipAttributeDiff, speed = 1) {
|
|
// TODO show final diff, not just cumulative one
|
|
let diff = (event.added.cumulative || 0) - (event.removed.cumulative || 0);
|
|
if (diff) {
|
|
let name = SHIP_VALUES_NAMES[event.code];
|
|
this.displayEffect(`${name} ${diff < 0 ? "-" : "+"}${Math.abs(diff)}`, diff >= 0, speed);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the hull indicator
|
|
*/
|
|
updateHull(current: number, diff = 0): void {
|
|
this.hull.setValue(current, this.ship.getAttribute("hull_capacity"));
|
|
}
|
|
|
|
/**
|
|
* Update the list of effects active on the ship
|
|
*/
|
|
updateActiveEffects() {
|
|
this.active_effects_display.removeAll();
|
|
|
|
let effects = this.ship.active_effects.list().filter(effect => !effect.isInternal());
|
|
|
|
let count = effects.length;
|
|
if (count) {
|
|
let positions = UITools.evenlySpace(70, 17, count);
|
|
|
|
effects.forEach((effect, index) => {
|
|
let name = effect.isBeneficial() ? "battle-hud-ship-effect-good" : "battle-hud-ship-effect-bad";
|
|
let dot = this.battleview.newImage(name, positions[index] - 35, 0);
|
|
this.active_effects_display.add(dot);
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the activated effects radius
|
|
*/
|
|
updateEffectsRadius(): void {
|
|
this.effects_radius.clear();
|
|
this.ship.actions.listToggled().forEach(action => {
|
|
let color = (action instanceof VigilanceAction) ? 0xf4bf42 : 0xe9f2f9;
|
|
this.effects_radius.lineStyle(2, color, 0.5);
|
|
this.effects_radius.fillStyle(color, 0.1);
|
|
this.effects_radius.fillCircle(0, 0, action.radius);
|
|
});
|
|
}
|
|
}
|
|
}
|