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

387 lines
13 KiB
TypeScript
Raw Normal View History

/// <reference path="../BaseView.ts"/>
2017-09-24 22:23:22 +00:00
module TK.SpaceTac.UI {
2017-10-25 22:45:53 +00:00
/**
* Interface for interacting with a ship (hover and click)
*/
export interface IShipButton {
cursorOnShip: (ship: Ship) => void;
cursorOffShip: (ship: Ship) => void;
cursorClicked: () => void;
}
/**
* Interactive view of a Battle
*/
export class BattleView extends BaseView implements IShipButton {
// Internal battle state
2018-01-31 18:19:50 +00:00
actual_battle!: Battle
// Displayed battle state
2018-01-31 18:19:50 +00:00
battle!: Battle
2014-12-29 00:00:00 +00:00
// Interacting player
2018-01-31 18:19:50 +00:00
player!: Player
// Multiplayer sharing
2018-01-31 18:19:50 +00:00
multi!: MultiBattle
2017-03-15 21:40:19 +00:00
// Layers
2018-05-15 14:57:45 +00:00
layer_background!: UIContainer
layer_arena!: UIContainer
layer_borders!: UIContainer
layer_overlay!: UIContainer
layer_sheets!: UIContainer
// Battleground container
2018-01-31 18:19:50 +00:00
arena!: Arena
2015-02-03 00:00:00 +00:00
// Background image
2018-05-15 14:57:45 +00:00
background!: UIImage | null
2015-02-03 00:00:00 +00:00
2014-12-31 00:00:00 +00:00
// Targetting mode (null if we're not in this mode)
2018-01-31 18:19:50 +00:00
targetting!: Targetting
2014-12-31 00:00:00 +00:00
// Ship list
2018-01-31 18:19:50 +00:00
ship_list!: ShipList
// Action bar
2018-01-31 18:19:50 +00:00
action_bar!: ActionBar
2014-12-31 00:00:00 +00:00
// Currently hovered ship
2018-01-31 18:19:50 +00:00
ship_hovered!: Ship | null
2014-12-31 00:00:00 +00:00
2017-01-09 23:24:33 +00:00
// Ship tooltip
2018-01-31 18:19:50 +00:00
ship_tooltip!: ShipTooltip
2017-01-09 23:24:33 +00:00
2017-02-27 23:36:12 +00:00
// Character sheet
2018-01-31 18:19:50 +00:00
character_sheet!: CharacterSheet
2017-02-27 23:36:12 +00:00
2014-12-31 00:00:00 +00:00
// Subscription to the battle log
2018-01-31 18:19:50 +00:00
log_processor!: LogProcessor
2014-12-31 00:00:00 +00:00
// True if player interaction is allowed
2018-01-31 18:19:50 +00:00
interacting!: boolean
// Tactical mode toggle
2018-01-31 18:19:50 +00:00
toggle_tactical_mode!: Toggle
// Toggle for the splash screen display
splash = true
2014-12-29 00:00:00 +00:00
// Init the view, binding it to a specific battle
2018-05-15 14:57:45 +00:00
init(data: { player: Player, battle: Battle }) {
super.init(data);
2018-05-15 14:57:45 +00:00
this.player = data.player;
this.actual_battle = data.battle;
this.battle = duplicate(data.battle, <any>TK.SpaceTac);
2014-12-31 00:00:00 +00:00
this.ship_hovered = null;
2015-02-03 00:00:00 +00:00
this.background = null;
this.multi = new MultiBattle();
2017-02-16 22:59:41 +00:00
this.toggle_tactical_mode = new Toggle(
() => this.arena.setTacticalMode(true),
() => this.arena.setTacticalMode(false)
);
2014-12-29 00:00:00 +00:00
}
// Create view graphics
create() {
super.create();
var game = this.game;
this.interacting = false;
this.log_processor = new LogProcessor(this);
2018-03-08 19:16:05 +00:00
let builder = new UIBuilder(this);
2017-03-15 21:40:19 +00:00
// Add layers
2017-10-11 20:58:08 +00:00
this.layer_background = this.getLayer("background");
this.layer_arena = this.getLayer("arena");
this.layer_borders = this.getLayer("borders");
this.layer_overlay = this.getLayer("overlay");
this.layer_sheets = this.getLayer("character_sheet");
2017-03-15 21:40:19 +00:00
2015-02-03 00:00:00 +00:00
// Background
2018-03-08 19:16:05 +00:00
this.background = builder.in(this.layer_background).image("battle-background");
2017-09-19 15:09:06 +00:00
// Add arena (local battlefield map)
this.arena = new Arena(this, this.layer_arena);
this.arena.callbacks_hover.push(bound(this, "cursorHovered"));
this.arena.callbacks_click.push(bound(this, "cursorClicked"));
// Add UI elements
this.action_bar = new ActionBar(this);
2018-05-15 14:57:45 +00:00
this.action_bar.setPosition(0, this.getHeight() - 132);
this.ship_list = new ShipList(this, this.battle, this.player, this.toggle_tactical_mode, this,
2017-10-25 22:45:53 +00:00
this.layer_borders, this.getWidth() - 112, 0);
this.ship_list.bindToLog(this.log_processor);
2017-01-09 23:24:33 +00:00
this.ship_tooltip = new ShipTooltip(this);
this.character_sheet = new CharacterSheet(this, CharacterSheetMode.DISPLAY);
2018-05-15 14:57:45 +00:00
this.character_sheet.moveToLayer(this.layer_sheets);
2015-01-08 00:00:00 +00:00
// Targetting info
2017-09-19 15:09:06 +00:00
this.targetting = new Targetting(this, this.action_bar, this.toggle_tactical_mode, this.arena.range_hint);
this.targetting.moveToLayer(this.arena.layer_targetting);
// BGM
2018-05-15 14:57:45 +00:00
this.audio.startMusic("mechanolith", 0.2);
// Key mapping
2018-06-11 16:22:56 +00:00
this.inputs.bind("t", "Show tactical view", () => this.ship_list.info_button.toggle());
this.inputs.bind("Enter", "Validate action", () => this.validationPressed());
this.inputs.bind(" ", "Validate action", () => this.validationPressed());
this.inputs.bind("Escape", "Cancel action", () => this.action_bar.actionEnded());
range(10).forEach(i => this.inputs.bind(`Numpad${i % 10}`, `Action/target ${i}`, () => this.numberPressed(i)));
range(10).forEach(i => this.inputs.bind(`Digit${i % 10}`, `Action/target ${i}`, () => this.numberPressed(i)));
this.inputs.bindCheat("w", "Win current battle", () => {
nn(this.player.getCheats()).win();
this.log_processor.fastForward();
});
this.inputs.bindCheat("x", "Lose current battle", () => {
nn(this.player.getCheats()).lose();
this.log_processor.fastForward();
});
2017-05-30 18:23:35 +00:00
this.inputs.bindCheat("a", "Use AI to play", () => this.playAI());
// "Battle" animation, then start processing the log
if (this.battle.ended) {
this.endBattle();
} else if (this.splash) {
this.showSplash().then(() => {
this.log_processor.start();
});
} else {
this.log_processor.start();
}
// If we are on a remote session, start the exchange
if (!this.session.primary && this.gameui.session_token) {
// TODO handle errors or timeout
2017-11-14 00:31:13 +00:00
this.multi.setup(this, this.actual_battle, this.gameui.session_token, false);
}
2014-12-29 00:00:00 +00:00
}
shutdown() {
super.shutdown();
this.log_processor.destroy();
}
2017-05-30 18:23:35 +00:00
/**
* Make the AI play current ship
*
* If the AI is already playing, do nothing
*/
playAI(): void {
2017-11-14 22:45:41 +00:00
if (this.session.spectator) {
return;
}
2018-03-01 23:14:09 +00:00
if (this.actual_battle.playAI(this.debug)) {
2017-05-30 18:23:35 +00:00
if (this.interacting) {
this.action_bar.setShip(new Ship());
}
this.setInteractionEnabled(false);
}
}
/**
* Apply an action to the actual battle
*/
applyAction(action: BaseAction, target?: Target): boolean {
2017-11-14 22:45:41 +00:00
if (this.session.spectator) {
return false;
}
let ship = this.actual_battle.playing_ship;
if (ship) {
let ship_action = ship.actions.getById(action.id);
if (ship_action) {
let result = this.actual_battle.applyOneAction(action.id, target);
if (result) {
this.setInteractionEnabled(false);
}
return result;
} else {
console.error("Action not found in available list", action, ship.actions);
return false;
}
} else {
console.error("Action not applied - ship not playing");
return false;
}
}
2017-07-20 23:09:17 +00:00
/**
* Display the splash screen at the start of battle
2017-07-20 23:09:17 +00:00
*/
showSplash(): Promise<void> {
2017-07-20 23:09:17 +00:00
let splash = new BattleSplash(this, this.battle.fleets[0], this.battle.fleets[1]);
2018-04-16 18:12:26 +00:00
return splash.start(this.layer_overlay);
2014-12-31 00:00:00 +00:00
}
/**
* Handle the pressing of a number key
*
* It may first be used to select an action to play, then to select a target
*/
numberPressed(num: number): void {
if (this.interacting) {
if (this.targetting.active) {
2017-10-25 22:45:53 +00:00
let ship = ifirst(this.battle.iships(true), ship => this.battle.getPlayOrder(ship) == num % 10);
if (ship) {
this.targetting.setTarget(Target.newFromShip(ship));
}
} else {
this.action_bar.keyActionPressed(num - 1);
}
}
}
/**
* Handle the pression of a validation key (enter or space)
*/
validationPressed(): void {
if (this.targetting.active) {
this.targetting.validate((action, target) => this.applyAction(action, target));
} else {
this.action_bar.keyActionPressed(-1);
}
}
2017-09-19 15:09:06 +00:00
/**
* Method called when the arena cursor is hovered
*/
cursorHovered(location: ArenaLocation | null, ship: Ship | null) {
if (this.targetting.active) {
this.targetting.setTargetFromLocation(location);
} else {
if (ship && this.ship_hovered != ship) {
this.cursorOnShip(ship);
} else if (!ship && this.ship_hovered) {
this.cursorOffShip(this.ship_hovered);
}
2017-09-19 15:09:06 +00:00
}
}
/**
* Method called when cursor starts hovering over a ship (or its icon)
*/
2017-02-09 00:00:35 +00:00
cursorOnShip(ship: Ship): void {
2017-09-19 15:09:06 +00:00
if (ship.alive) {
2017-02-15 22:34:27 +00:00
this.setShipHovered(ship);
}
2014-12-31 00:00:00 +00:00
}
2017-09-19 15:09:06 +00:00
/**
* Method called when cursor stops hovering over a ship (or its icon)
*/
2017-02-09 00:00:35 +00:00
cursorOffShip(ship: Ship): void {
2014-12-31 00:00:00 +00:00
if (this.ship_hovered === ship) {
this.setShipHovered(null);
}
}
2017-09-19 15:09:06 +00:00
/**
* Method called when cursor has been clicked (in space or on a ship)
*/
cursorClicked(): void {
if (this.targetting.active) {
this.validationPressed();
} else if (this.ship_hovered && this.player.is(this.ship_hovered.fleet.player) && this.interacting) {
this.character_sheet.show(this.ship_hovered);
this.setShipHovered(null);
} else {
this.log_processor.fastForward();
}
}
2017-09-19 15:09:06 +00:00
/**
* Set the currently hovered ship
*/
2017-03-09 17:11:00 +00:00
setShipHovered(ship: Ship | null): void {
2014-12-31 00:00:00 +00:00
this.ship_hovered = ship;
this.arena.setShipHovered(ship);
2015-02-04 00:00:00 +00:00
this.ship_list.setHovered(ship);
2017-05-14 23:00:36 +00:00
if (ship) {
this.ship_tooltip.setShip(ship);
} else {
this.ship_tooltip.hide();
}
2014-12-29 00:00:00 +00:00
}
2014-12-31 00:00:00 +00:00
// Enable or disable the global player interaction
// Disable interaction when it is the AI turn, or when the current ship can't play
setInteractionEnabled(enabled: boolean): void {
if (this.session.spectator) {
enabled = false;
}
if (enabled != this.interacting) {
2018-05-15 14:57:45 +00:00
this.action_bar.setInteractivity(enabled);
this.exitTargettingMode();
this.interacting = enabled;
if (!enabled) {
this.setShipHovered(null);
}
}
}
2014-12-31 00:00:00 +00:00
// Enter targetting mode
// While in this mode, the Targetting object will receive hover and click events, and handle them
enterTargettingMode(ship: Ship, action: BaseAction, mode: ActionTargettingMode): Targetting | null {
if (!this.interacting) {
return null;
}
this.setShipHovered(null);
this.targetting.setAction(ship, action, mode);
2014-12-31 00:00:00 +00:00
return this.targetting;
}
// Exit targetting mode
exitTargettingMode(): void {
this.targetting.setAction(null, null);
2014-12-31 00:00:00 +00:00
}
2017-03-12 23:32:41 +00:00
/**
* End the battle and show the outcome dialog
*/
endBattle() {
let battle = this.actual_battle;
if (battle.outcome) {
2017-03-12 23:32:41 +00:00
this.setInteractionEnabled(false);
this.session.setBattleEnded();
2017-03-14 22:28:07 +00:00
battle.stats.processLog(battle.log, this.player.fleet);
new OutcomeDialog(this, this.player, battle.outcome, battle.stats);
2017-03-12 23:32:41 +00:00
} else {
console.error("Battle not ended !");
}
}
/**
* Exit the battle, and go back to map
*/
exitBattle() {
this.session.exitBattle();
2018-05-15 14:57:45 +00:00
this.backToRouter();
2017-03-12 23:32:41 +00:00
}
2017-03-14 22:28:07 +00:00
/**
* Revert the battle, and go back to map
*/
revertBattle() {
this.session.revertBattle();
2018-05-15 14:57:45 +00:00
this.backToRouter();
2017-03-14 22:28:07 +00:00
}
2014-12-29 00:00:00 +00:00
}
}