Improved battle "flow" (for better information reading)
This commit is contained in:
parent
90ec4491b2
commit
bb780415a3
|
@ -357,8 +357,14 @@ module TK.SpaceTac {
|
|||
applyOneAction(action: BaseAction, target?: Target): boolean {
|
||||
let ship = this.playing_ship;
|
||||
if (ship) {
|
||||
if (!target) {
|
||||
target = action.getDefaultTarget(ship);
|
||||
}
|
||||
if (action.apply(this, ship, target)) {
|
||||
this.performChecks();
|
||||
if (!this.ended) {
|
||||
this.applyDiffs([new ShipActionEndedDiff(ship, action, target)]);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -40,7 +40,11 @@ module TK.SpaceTac {
|
|||
* This may not contain ALL the diffs needed, and should be called again while it returns diffs.
|
||||
*/
|
||||
checkAll(): BaseBattleDiff[] {
|
||||
let diffs: BaseBattleDiff[];
|
||||
let diffs: BaseBattleDiff[] = [];
|
||||
|
||||
if (this.battle.ended) {
|
||||
return diffs;
|
||||
}
|
||||
|
||||
diffs = this.checkAreaEffects();
|
||||
if (diffs.length) {
|
||||
|
|
|
@ -3,7 +3,7 @@ module TK.SpaceTac {
|
|||
export class TestTools {
|
||||
|
||||
// Create a battle between two fleets, with a fixed play order (owned ships, then enemy ships)
|
||||
static createBattle(own_ships = 1, enemy_ships = 0): Battle {
|
||||
static createBattle(own_ships = 1, enemy_ships = 1): Battle {
|
||||
var fleet1 = new Fleet();
|
||||
var fleet2 = new Fleet();
|
||||
|
||||
|
|
|
@ -34,49 +34,73 @@ module TK.SpaceTac.Specs {
|
|||
});
|
||||
|
||||
test.case("generates power for previous ship", check => {
|
||||
let battle = TestTools.createBattle(1, 0);
|
||||
let ship = battle.play_order[0];
|
||||
TestTools.setShipAP(ship, 10, 3);
|
||||
let weapon = TestTools.addWeapon(ship);
|
||||
let battle = TestTools.createBattle(1, 1);
|
||||
let [ship1, ship2] = battle.play_order;
|
||||
TestTools.setShipAP(ship1, 10, 3);
|
||||
let weapon = TestTools.addWeapon(ship1);
|
||||
weapon.action = new ToggleAction(weapon, 2);
|
||||
ship.setValue("power", 6);
|
||||
ship1.setValue("power", 6);
|
||||
|
||||
TestTools.actionChain(check, battle, [
|
||||
[ship, weapon.action, Target.newFromShip(ship)],
|
||||
[ship, weapon.action, Target.newFromShip(ship)],
|
||||
[ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)],
|
||||
[ship, weapon.action, Target.newFromShip(ship)],
|
||||
[ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)],
|
||||
[ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)],
|
||||
[ship, weapon.action, Target.newFromShip(ship)],
|
||||
[ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)],
|
||||
[ship1, weapon.action, Target.newFromShip(ship1)],
|
||||
[ship1, weapon.action, Target.newFromShip(ship1)],
|
||||
[ship1, EndTurnAction.SINGLETON, Target.newFromShip(ship1)],
|
||||
[ship2, EndTurnAction.SINGLETON, Target.newFromShip(ship2)],
|
||||
[ship1, weapon.action, Target.newFromShip(ship1)],
|
||||
[ship1, EndTurnAction.SINGLETON, Target.newFromShip(ship1)],
|
||||
[ship2, EndTurnAction.SINGLETON, Target.newFromShip(ship2)],
|
||||
[ship1, EndTurnAction.SINGLETON, Target.newFromShip(ship1)],
|
||||
[ship2, EndTurnAction.SINGLETON, Target.newFromShip(ship2)],
|
||||
[ship1, weapon.action, Target.newFromShip(ship1)],
|
||||
[ship1, EndTurnAction.SINGLETON, Target.newFromShip(ship1)],
|
||||
], [
|
||||
check => {
|
||||
check.equals(ship.getValue("power"), 6, "power value");
|
||||
check.equals(ship1.getValue("power"), 6, "power value");
|
||||
check.same(battle.playing_ship, ship1);
|
||||
},
|
||||
check => {
|
||||
check.equals(ship.getValue("power"), 4, "power value");
|
||||
check.equals(ship1.getValue("power"), 4, "power value");
|
||||
check.same(battle.playing_ship, ship1);
|
||||
},
|
||||
check => {
|
||||
check.equals(ship.getValue("power"), 6, "power value");
|
||||
check.equals(ship1.getValue("power"), 6, "power value");
|
||||
check.same(battle.playing_ship, ship1);
|
||||
},
|
||||
check => {
|
||||
check.equals(ship.getValue("power"), 9, "power value");
|
||||
check.equals(ship1.getValue("power"), 9, "power value");
|
||||
check.same(battle.playing_ship, ship2);
|
||||
},
|
||||
check => {
|
||||
check.equals(ship.getValue("power"), 7, "power value");
|
||||
check.equals(ship1.getValue("power"), 9, "power value");
|
||||
check.same(battle.playing_ship, ship1);
|
||||
},
|
||||
check => {
|
||||
check.equals(ship.getValue("power"), 8, "power value");
|
||||
check.equals(ship1.getValue("power"), 7, "power value");
|
||||
check.same(battle.playing_ship, ship1);
|
||||
},
|
||||
check => {
|
||||
check.equals(ship.getValue("power"), 9, "power value");
|
||||
check.equals(ship1.getValue("power"), 8, "power value");
|
||||
check.same(battle.playing_ship, ship2);
|
||||
},
|
||||
check => {
|
||||
check.equals(ship.getValue("power"), 10, "power value");
|
||||
check.equals(ship1.getValue("power"), 8, "power value");
|
||||
check.same(battle.playing_ship, ship1);
|
||||
},
|
||||
check => {
|
||||
check.equals(ship.getValue("power"), 10, "power value");
|
||||
check.equals(ship1.getValue("power"), 9, "power value");
|
||||
check.same(battle.playing_ship, ship2);
|
||||
},
|
||||
check => {
|
||||
check.equals(ship1.getValue("power"), 9, "power value");
|
||||
check.same(battle.playing_ship, ship1);
|
||||
},
|
||||
check => {
|
||||
check.equals(ship1.getValue("power"), 10, "power value");
|
||||
check.same(battle.playing_ship, ship1);
|
||||
},
|
||||
check => {
|
||||
check.equals(ship1.getValue("power"), 10, "power value");
|
||||
check.same(battle.playing_ship, ship2);
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
|
|
@ -38,6 +38,7 @@ module TK.SpaceTac.Specs {
|
|||
test.case("applies and reverts", check => {
|
||||
let battle = TestTools.createBattle();
|
||||
let ship = battle.play_order[0];
|
||||
ship.setArenaPosition(500, 600)
|
||||
TestTools.setShipAP(ship, 20);
|
||||
ship.setValue("power", 5);
|
||||
|
||||
|
@ -47,16 +48,16 @@ module TK.SpaceTac.Specs {
|
|||
ship.addSlot(SlotType.Engine).attach(engine);
|
||||
|
||||
TestTools.actionChain(check, battle, [
|
||||
[ship, action, Target.newFromLocation(10, 5)],
|
||||
[ship, action, Target.newFromLocation(510, 605)],
|
||||
], [
|
||||
check => {
|
||||
check.equals(ship.arena_x, 0, "ship X");
|
||||
check.equals(ship.arena_y, 0, "ship Y");
|
||||
check.equals(ship.arena_x, 500, "ship X");
|
||||
check.equals(ship.arena_y, 600, "ship Y");
|
||||
check.equals(ship.getValue("power"), 5, "power");
|
||||
},
|
||||
check => {
|
||||
check.nears(ship.arena_x, 4.382693, 5, "ship X");
|
||||
check.nears(ship.arena_y, 2.191346, 5, "ship Y");
|
||||
check.nears(ship.arena_x, 504.382693, 5, "ship X");
|
||||
check.nears(ship.arena_y, 602.191346, 5, "ship Y");
|
||||
check.equals(ship.getValue("power"), 0, "power");
|
||||
}
|
||||
]);
|
||||
|
|
23
src/core/diffs/ShipActionEndedDiff.ts
Normal file
23
src/core/diffs/ShipActionEndedDiff.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
/// <reference path="BaseBattleDiff.ts"/>
|
||||
|
||||
module TK.SpaceTac {
|
||||
/**
|
||||
* A ship action is fully ended
|
||||
*
|
||||
* This does not do anything, it is just used to mark the effective end of the action diffs (battle checks included)
|
||||
*/
|
||||
export class ShipActionEndedDiff extends BaseBattleShipDiff {
|
||||
// Action applied
|
||||
action: RObjectId
|
||||
|
||||
// Target for the action
|
||||
target: Target
|
||||
|
||||
constructor(ship: Ship, action: BaseAction, target: Target) {
|
||||
super(ship);
|
||||
|
||||
this.action = action.id;
|
||||
this.target = target;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -60,32 +60,44 @@ module TK.SpaceTac.UI {
|
|||
battleview.tooltip.bindStaticText(button, "Game options");
|
||||
|
||||
// Log processing
|
||||
battleview.log_processor.register(event => {
|
||||
if (!(event instanceof BaseBattleShipDiff) || !this.ship || !this.ship.is(event.ship_id)) {
|
||||
return 0;
|
||||
battleview.log_processor.register(diff => {
|
||||
if (!(diff instanceof BaseBattleShipDiff) || !this.ship || !this.ship.is(diff.ship_id)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (event instanceof ShipValueDiff) {
|
||||
if (event.code == "power") {
|
||||
this.updatePower();
|
||||
this.action_icons.forEach(action => action.refresh());
|
||||
if (diff instanceof ShipValueDiff && diff.code == "power") {
|
||||
return {
|
||||
background: async () => {
|
||||
this.updatePower();
|
||||
this.action_icons.forEach(action => action.refresh());
|
||||
}
|
||||
}
|
||||
} else if (event instanceof ShipAttributeDiff) {
|
||||
if (event.code == "power_capacity") {
|
||||
this.updatePower();
|
||||
} else if (diff instanceof ShipAttributeDiff && diff.code == "power_capacity") {
|
||||
return {
|
||||
background: async () => this.updatePower()
|
||||
}
|
||||
} else if (event instanceof ShipActionUsedDiff || event instanceof ShipActionToggleDiff) {
|
||||
this.action_icons.forEach(action => action.refresh());
|
||||
} else if (event instanceof ShipCooldownDiff) {
|
||||
let icons = this.action_icons.filter(icon => icon.action.equipment && icon.action.equipment.is(event.equipment));
|
||||
icons.forEach(icon => icon.refresh());
|
||||
} else if (diff instanceof ShipActionUsedDiff || diff instanceof ShipActionToggleDiff) {
|
||||
return {
|
||||
background: async () => this.action_icons.forEach(action => action.refresh())
|
||||
}
|
||||
} else if (diff instanceof ShipCooldownDiff) {
|
||||
return {
|
||||
background: async () => {
|
||||
let icons = this.action_icons.filter(icon => icon.action.equipment && icon.action.equipment.is(diff.equipment));
|
||||
icons.forEach(icon => icon.refresh());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
battleview.log_processor.watchForShipChange(ship => {
|
||||
this.setShip(ship);
|
||||
return 0;
|
||||
return {
|
||||
background: async () => {
|
||||
this.setShip(ship);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.setInteractive(false);
|
||||
}
|
||||
|
|
|
@ -69,8 +69,11 @@ module TK.SpaceTac.UI {
|
|||
this.container.onDestroy.add(() => this.destroy());
|
||||
|
||||
view.log_processor.watchForShipChange(ship => {
|
||||
this.setShipPlaying(ship);
|
||||
return 0;
|
||||
return {
|
||||
foreground: async () => {
|
||||
await this.setShipPlaying(ship)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -192,19 +195,22 @@ module TK.SpaceTac.UI {
|
|||
}
|
||||
}
|
||||
|
||||
// Set the playing state on a ship sprite
|
||||
setShipPlaying(ship: Ship | null): void {
|
||||
/**
|
||||
* Set the playing state on a ship sprite
|
||||
*/
|
||||
async setShipPlaying(ship: Ship | null, animate = true): Promise<void> {
|
||||
if (this.playing) {
|
||||
this.playing.setPlaying(false);
|
||||
this.playing = null;
|
||||
}
|
||||
|
||||
if (ship) {
|
||||
var arena_ship = this.findShipSprite(ship);
|
||||
let arena_ship = this.findShipSprite(ship);
|
||||
if (arena_ship) {
|
||||
this.layer_ships.bringToTop(arena_ship);
|
||||
arena_ship.setPlaying(true);
|
||||
await arena_ship.setPlaying(true, animate);
|
||||
}
|
||||
|
||||
this.playing = arena_ship;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,8 +128,8 @@ module TK.SpaceTac.UI {
|
|||
}
|
||||
|
||||
// Log processing
|
||||
this.battleview.log_processor.register(event => this.processLogEvent(event));
|
||||
this.battleview.log_processor.registerForShip(ship, event => this.processShipLogEvent(event));
|
||||
this.battleview.log_processor.register(diff => this.processBattleDiff(diff));
|
||||
this.battleview.log_processor.registerForShip(ship, diff => this.processShipDiff(diff));
|
||||
}
|
||||
|
||||
jasmineToString(): string {
|
||||
|
@ -137,72 +137,136 @@ module TK.SpaceTac.UI {
|
|||
}
|
||||
|
||||
/**
|
||||
* Process a battle log event
|
||||
* Process a battle diff
|
||||
*/
|
||||
private processLogEvent(event: BaseBattleDiff): number {
|
||||
if (event instanceof ShipChangeDiff) {
|
||||
private processBattleDiff(diff: BaseBattleDiff) {
|
||||
if (diff instanceof ShipChangeDiff) {
|
||||
this.updatePlayOrder();
|
||||
}
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a log event for this ship
|
||||
* Process a ship diff
|
||||
*/
|
||||
private processShipLogEvent(event: BaseBattleShipDiff): number {
|
||||
if (event instanceof ShipEffectAddedDiff || event instanceof ShipEffectRemovedDiff) {
|
||||
this.updateActiveEffects();
|
||||
return 0;
|
||||
} else if (event instanceof ShipValueDiff) {
|
||||
if (event.code == "hull") {
|
||||
this.toggle_hsp.manipulate("value")(1500);
|
||||
this.hull_bar.setValue(this.ship.getValue("hull"), this.ship.getAttribute("hull_capacity") || 0);
|
||||
this.hull_text.text = `${this.ship.getValue("hull")}`;
|
||||
this.battleview.animations.blink(this.hull_text);
|
||||
} else if (event.code == "shield") {
|
||||
this.toggle_hsp.manipulate("value")(1500);
|
||||
this.shield_bar.setValue(this.ship.getValue("shield"), this.ship.getAttribute("shield_capacity") || 0);
|
||||
this.shield_text.text = `${this.ship.getValue("shield")}`;
|
||||
this.battleview.animations.blink(this.shield_text);
|
||||
/*if (this.shield_bar.getValue() == 0) {
|
||||
this.displayEffect("Shield failure", false);
|
||||
}*/
|
||||
} else if (event.code == "power") {
|
||||
this.toggle_hsp.manipulate("value")(1500);
|
||||
this.power_text.text = `${this.ship.getValue("power")}`;
|
||||
this.battleview.animations.blink(this.power_text);
|
||||
private processShipDiff(diff: BaseBattleShipDiff): LogProcessorDelegate {
|
||||
if (diff instanceof ShipEffectAddedDiff || diff instanceof ShipEffectRemovedDiff) {
|
||||
return {
|
||||
background: async () => this.updateActiveEffects()
|
||||
}
|
||||
return 0;
|
||||
} else if (event instanceof ShipAttributeDiff) {
|
||||
this.displayAttributeChanged(event);
|
||||
return 0;
|
||||
} else if (event instanceof ShipDamageDiff) {
|
||||
this.displayEffect(`${event.hull + event.shield} damage`, false);
|
||||
return 0;
|
||||
} else if (event instanceof ShipActionToggleDiff) {
|
||||
let action = this.ship.getAction(event.action);
|
||||
if (action && action.equipment) {
|
||||
let equname = action.equipment.name;
|
||||
if (event.activated) {
|
||||
this.displayEffect(`${equname} ON`, true);
|
||||
} else {
|
||||
this.displayEffect(`${equname} OFF`, false);
|
||||
} else if (diff instanceof ShipValueDiff) {
|
||||
return {
|
||||
background: async (animate, timer) => {
|
||||
if (animate) {
|
||||
this.toggle_hsp.manipulate("value")(true);
|
||||
}
|
||||
|
||||
if (diff.code == "hull") {
|
||||
this.hull_bar.setValue(this.ship.getValue("hull"), this.ship.getAttribute("hull_capacity") || 0);
|
||||
this.hull_text.text = `${this.ship.getValue("hull")}`;
|
||||
if (animate) {
|
||||
await this.battleview.animations.blink(this.hull_text);
|
||||
}
|
||||
} else if (diff.code == "shield") {
|
||||
this.shield_bar.setValue(this.ship.getValue("shield"), this.ship.getAttribute("shield_capacity") || 0);
|
||||
this.shield_text.text = `${this.ship.getValue("shield")}`;
|
||||
if (animate) {
|
||||
await this.battleview.animations.blink(this.shield_text);
|
||||
}
|
||||
/*if (this.shield_bar.getValue() == 0) {
|
||||
this.displayEffect("Shield failure", false);
|
||||
}*/
|
||||
} else if (diff.code == "power") {
|
||||
this.power_text.text = `${this.ship.getValue("power")}`;
|
||||
if (animate) {
|
||||
await this.battleview.animations.blink(this.power_text);
|
||||
}
|
||||
}
|
||||
|
||||
if (animate) {
|
||||
await timer.sleep(500);
|
||||
this.toggle_hsp.manipulate("value")(false);
|
||||
}
|
||||
}
|
||||
this.updateEffectsRadius();
|
||||
}
|
||||
return 300;
|
||||
} else if (event instanceof ShipActionUsedDiff) {
|
||||
let action = this.ship.getAction(event.action);
|
||||
if (action && !(action instanceof ToggleAction) && action.equipment) {
|
||||
this.displayEffect(action.equipment.name, true);
|
||||
} else if (diff instanceof ShipAttributeDiff) {
|
||||
return {
|
||||
background: async (animate, timer) => {
|
||||
if (animate) {
|
||||
this.displayAttributeChanged(diff)
|
||||
await timer.sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 300;
|
||||
} else if (event instanceof ShipMoveDiff) {
|
||||
this.moveTo(event.start.x, event.start.y, event.start.angle, false);
|
||||
let duration = this.moveTo(event.end.x, event.end.y, event.end.angle, true, !!event.engine);
|
||||
return duration;
|
||||
} else if (diff instanceof ShipDamageDiff) {
|
||||
return {
|
||||
background: async (animate, timer) => {
|
||||
if (animate) {
|
||||
await this.displayEffect(`${diff.hull + diff.shield} damage`, false);
|
||||
await timer.sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (diff instanceof ShipActionToggleDiff) {
|
||||
return {
|
||||
foreground: async (animate, timer) => {
|
||||
let action = this.ship.getAction(diff.action);
|
||||
if (action && action.equipment) {
|
||||
let equname = action.equipment.name;
|
||||
|
||||
if (animate) {
|
||||
if (diff.activated) {
|
||||
await this.displayEffect(`${equname} ON`, true);
|
||||
} else {
|
||||
await this.displayEffect(`${equname} OFF`, false);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateEffectsRadius();
|
||||
await timer.sleep(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (diff instanceof ShipActionUsedDiff) {
|
||||
let action = this.ship.getAction(diff.action);
|
||||
if (action) {
|
||||
if (!(action instanceof ToggleAction) && action.equipment) {
|
||||
let equipment = action.equipment;
|
||||
return {
|
||||
foreground: async (animate, timer) => {
|
||||
if (animate) {
|
||||
await this.displayEffect(equipment.name, true);
|
||||
await timer.sleep(300);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (action instanceof EndTurnAction) {
|
||||
return {
|
||||
foreground: async (animate, timer) => {
|
||||
if (animate) {
|
||||
await this.displayEffect("End turn", true);
|
||||
await timer.sleep(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
} else if (diff instanceof ShipMoveDiff) {
|
||||
return {
|
||||
background: async (animate: boolean, timer: Timer) => {
|
||||
this.moveTo(diff.start.x, diff.start.y, diff.start.angle, false);
|
||||
let duration = this.moveTo(diff.end.x, diff.end.y, diff.end.angle, animate, !!diff.engine);
|
||||
if (duration && animate) {
|
||||
await timer.sleep(duration);
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,12 +294,14 @@ module TK.SpaceTac.UI {
|
|||
*
|
||||
* This will alter the HUD frame to show this state
|
||||
*/
|
||||
setPlaying(playing: boolean) {
|
||||
async setPlaying(playing: boolean, animate = true): Promise<void> {
|
||||
this.frame.alpha = playing ? 1 : 0.35;
|
||||
this.frame.visible = this.ship.alive;
|
||||
/*if (playing) {
|
||||
this.battleview.animations.blink(this.frame);
|
||||
}*/
|
||||
|
||||
if (playing && animate) {
|
||||
this.battleview.audio.playOnce("battle-ship-change");
|
||||
await this.battleview.animations.blink(this.frame);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -274,7 +340,7 @@ module TK.SpaceTac.UI {
|
|||
/**
|
||||
* Briefly show an effect on this ship
|
||||
*/
|
||||
displayEffect(message: string, beneficial: boolean) {
|
||||
async displayEffect(message: string, beneficial: boolean) {
|
||||
if (!this.effects_messages.visible) {
|
||||
this.effects_messages.removeAll(true);
|
||||
}
|
||||
|
@ -289,6 +355,7 @@ module TK.SpaceTac.UI {
|
|||
);
|
||||
|
||||
this.effects_messages_toggle.manipulate("added")(1000);
|
||||
await this.battleview.timer.sleep(1000);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -117,6 +117,7 @@ module TK.SpaceTac.UI {
|
|||
this.action_bar.position.set(0, this.getHeight() - 132);
|
||||
this.ship_list = new ShipList(this, this.battle, this.player, this.toggle_tactical_mode, this,
|
||||
this.layer_borders, this.getWidth() - 112, 0);
|
||||
this.ship_list.bindToLog(this.log_processor);
|
||||
this.ship_tooltip = new ShipTooltip(this);
|
||||
this.character_sheet = new CharacterSheet(this, -this.getWidth());
|
||||
this.layer_sheets.add(this.character_sheet);
|
||||
|
@ -193,7 +194,11 @@ module TK.SpaceTac.UI {
|
|||
if (ship) {
|
||||
let ship_action = first(ship.getAvailableActions(), ac => ac.is(action));
|
||||
if (ship_action) {
|
||||
return this.actual_battle.applyOneAction(action, target);
|
||||
let result = this.actual_battle.applyOneAction(action, target);
|
||||
if (result) {
|
||||
this.setInteractionEnabled(false);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
console.error("Action not found in available list", action, ship.getAvailableActions());
|
||||
return false;
|
||||
|
|
|
@ -11,16 +11,16 @@ module TK.SpaceTac.UI {
|
|||
// Log client (to receive actual battle diffs)
|
||||
private log: BattleLogClient
|
||||
|
||||
// Forward diffs to other subscribers
|
||||
private forwarding: ((diff: BaseBattleDiff) => number)[] = []
|
||||
// Registered subscribers
|
||||
private subscriber: LogProcessorSubscriber[] = []
|
||||
|
||||
// Background delegates promises
|
||||
private background_promises: Promise<void>[] = []
|
||||
|
||||
// Debug indicators
|
||||
private debug = false
|
||||
private ai_disabled = false
|
||||
|
||||
// Time at which the last action was applied
|
||||
private last_action: number
|
||||
|
||||
constructor(view: BattleView) {
|
||||
this.view = view;
|
||||
this.log = new BattleLogClient(view.battle, view.actual_battle.log);
|
||||
|
@ -37,6 +37,15 @@ module TK.SpaceTac.UI {
|
|||
view.inputs.bindCheat("End", "Jump to end", () => {
|
||||
this.log.jumpToEnd();
|
||||
});
|
||||
|
||||
// Internal subscribers
|
||||
this.register((diff) => this.checkReaction(diff));
|
||||
this.register((diff) => this.checkControl(diff));
|
||||
this.register((diff) => this.checkProjectileFired(diff));
|
||||
this.register((diff) => this.checkShipDeath(diff));
|
||||
this.register((diff) => this.checkBattleEnded(diff));
|
||||
this.register((diff) => this.checkDroneDeployed(diff));
|
||||
this.register((diff) => this.checkDroneRecalled(diff));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,7 +59,6 @@ module TK.SpaceTac.UI {
|
|||
}
|
||||
|
||||
await this.processBattleDiff(diff);
|
||||
this.transferControl();
|
||||
});
|
||||
|
||||
this.transferControl();
|
||||
|
@ -68,7 +76,6 @@ module TK.SpaceTac.UI {
|
|||
while (diff = this.log.forward()) {
|
||||
this.processBattleDiff(diff, false);
|
||||
}
|
||||
this.transferControl();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,48 +103,70 @@ module TK.SpaceTac.UI {
|
|||
}
|
||||
|
||||
/**
|
||||
* Register a sub-subscriber.
|
||||
*
|
||||
* The difference with registering directly to the BattleLog is that diffs may add delay
|
||||
* for animations.
|
||||
*
|
||||
* The callback may return the duration it needs to display the change.
|
||||
* Register a diff subscriber
|
||||
*/
|
||||
register(callback: (diff: BaseBattleDiff) => number) {
|
||||
this.forwarding.push(callback);
|
||||
register(subscriber: LogProcessorSubscriber) {
|
||||
this.subscriber.push(subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a sub-subscriber, to receive diffs for a specific ship
|
||||
* Register a diff for a specific ship
|
||||
*/
|
||||
registerForShip(ship: Ship, callback: (diff: BaseBattleShipDiff) => number) {
|
||||
this.register(event => {
|
||||
if (event instanceof BaseBattleShipDiff && event.ship_id === ship.id) {
|
||||
return callback(event);
|
||||
registerForShip(ship: Ship, subscriber: (diff: BaseBattleShipDiff) => LogProcessorDelegate) {
|
||||
this.register(diff => {
|
||||
if (diff instanceof BaseBattleShipDiff && diff.ship_id === ship.id) {
|
||||
return subscriber(diff);
|
||||
} else {
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register to playing ship changes
|
||||
*
|
||||
* If *initial* is true, the callback will be fired once at register time
|
||||
*
|
||||
* If *immediate* is true, the ShipChangeDiff is watched, otherwise the end of the EndTurn action
|
||||
*/
|
||||
watchForShipChange(callback: (ship: Ship) => number, initial = true) {
|
||||
watchForShipChange(callback: (ship: Ship) => LogProcessorDelegate, initial = true, immediate = false) {
|
||||
this.register(diff => {
|
||||
if (diff instanceof ShipChangeDiff) {
|
||||
let changed = false;
|
||||
if (immediate && diff instanceof ShipChangeDiff) {
|
||||
changed = true;
|
||||
} else if (!immediate && diff instanceof ShipActionEndedDiff) {
|
||||
let ship = this.view.battle.getShip(diff.ship_id);
|
||||
if (ship && ship.getAction(diff.action) instanceof EndTurnAction) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
let ship = this.view.battle.playing_ship;
|
||||
if (ship) {
|
||||
return callback(ship);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (initial) {
|
||||
let ship = this.view.battle.playing_ship;
|
||||
if (ship) {
|
||||
callback(ship);
|
||||
let result = callback(ship);
|
||||
let timer = new Timer(true);
|
||||
if (result.foreground) {
|
||||
let promise = result.foreground(false, timer);
|
||||
if (result.background) {
|
||||
let next = result.background;
|
||||
promise.then(() => next(false, timer));
|
||||
}
|
||||
} else if (result.background) {
|
||||
result.background(false, timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -149,56 +178,28 @@ module TK.SpaceTac.UI {
|
|||
if (this.debug) {
|
||||
console.log("Battle diff", diff);
|
||||
}
|
||||
let timer = timed ? this.view.timer : new Timer(true);
|
||||
|
||||
let durations = this.forwarding.map(subscriber => subscriber(diff));
|
||||
let t = (new Date()).getTime()
|
||||
// TODO add priority to sort the delegates
|
||||
let delegates = this.subscriber.map(subscriber => subscriber(diff));
|
||||
let foregrounds = nna(delegates.map(delegate => delegate.foreground || null));
|
||||
let backgrounds = nna(delegates.map(delegate => delegate.background || null));
|
||||
|
||||
if (diff instanceof ShipActionUsedDiff) {
|
||||
let ship = this.view.actual_battle.getShip(diff.ship_id);
|
||||
if (ship) {
|
||||
if (!ship.getPlayer().is(this.view.player)) {
|
||||
// AI is playing, do not make it play too fast
|
||||
let since_last = t - this.last_action;
|
||||
if (since_last < 2000) {
|
||||
durations.push(2000 - since_last);
|
||||
}
|
||||
}
|
||||
if (foregrounds.length > 0) {
|
||||
if (this.background_promises.length > 0) {
|
||||
console.log("wait", this.background_promises);
|
||||
await Promise.all(this.background_promises);
|
||||
this.background_promises = [];
|
||||
}
|
||||
this.last_action = t;
|
||||
} else if (diff instanceof ProjectileFiredDiff) {
|
||||
let ship = this.view.battle.getShip(diff.ship_id);
|
||||
if (ship) {
|
||||
let equipment = ship.getEquipment(diff.equipment);
|
||||
if (equipment && equipment.slot_type == SlotType.Weapon) {
|
||||
let effect = new WeaponEffect(this.view.arena, ship, diff.target, equipment);
|
||||
durations.push(effect.start());
|
||||
}
|
||||
}
|
||||
} else if (diff instanceof ShipChangeDiff) {
|
||||
durations.push(this.processShipChangeEvent(diff));
|
||||
} else if (diff instanceof ShipDeathDiff) {
|
||||
durations.push(this.processDeathEvent(diff));
|
||||
} else if (diff instanceof ShipDamageDiff) {
|
||||
durations.push(this.processDamageEvent(diff));
|
||||
} else if (diff instanceof EndBattleDiff) {
|
||||
durations.push(this.processEndBattleEvent(diff));
|
||||
} else if (diff instanceof DroneDeployedDiff) {
|
||||
durations.push(this.processDroneDeployedEvent(diff));
|
||||
} else if (diff instanceof DroneRecalledDiff) {
|
||||
durations.push(this.processDroneRecalledEvent(diff));
|
||||
|
||||
let promises = foregrounds.map(foreground => foreground(timed, timer));
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
let delay = max([0].concat(durations));
|
||||
if (delay && timed) {
|
||||
await this.view.timer.sleep(delay);
|
||||
}
|
||||
|
||||
if (this.log.isPlaying()) {
|
||||
let reaction = this.view.session.reactions.check(this.view.player, this.view.battle, this.view.battle.playing_ship, diff);
|
||||
if (reaction) {
|
||||
await this.processReaction(reaction);
|
||||
}
|
||||
}
|
||||
console.log("start backgrounds");
|
||||
let promises = backgrounds.map(background => background(timed, timed ? this.view.timer : new Timer(true)));
|
||||
this.background_promises = this.background_promises.concat(promises);
|
||||
console.log("added", diff, this.background_promises);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -220,78 +221,143 @@ module TK.SpaceTac.UI {
|
|||
}
|
||||
|
||||
/**
|
||||
* Process a personality reaction
|
||||
* Check if a personality reaction should be triggered for a diff
|
||||
*/
|
||||
private async processReaction(reaction: PersonalityReaction): Promise<void> {
|
||||
if (reaction instanceof PersonalityReactionConversation) {
|
||||
let conversation = UIConversation.newFromPieces(this.view, reaction.messages);
|
||||
await conversation.waitEnd();
|
||||
} else {
|
||||
console.warn("[LogProcessor] Unknown personality reaction type", reaction);
|
||||
}
|
||||
}
|
||||
|
||||
// Playing ship changed
|
||||
private processShipChangeEvent(event: ShipChangeDiff): number {
|
||||
this.view.ship_list.refresh();
|
||||
if (event.ship_id != event.new_ship) {
|
||||
this.view.audio.playOnce("battle-ship-change");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Damage to ship
|
||||
private processDamageEvent(event: ShipDamageDiff): number {
|
||||
var item = this.view.ship_list.findItem(event.ship_id);
|
||||
if (item) {
|
||||
item.setDamageHit();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// A ship died
|
||||
private processDeathEvent(event: ShipDeathDiff): number {
|
||||
let ship = this.view.battle.getShip(event.ship_id);
|
||||
|
||||
if (ship) {
|
||||
if (this.view.ship_hovered === ship) {
|
||||
this.view.setShipHovered(null);
|
||||
private checkReaction(diff: BaseBattleDiff): LogProcessorDelegate {
|
||||
if (this.log.isPlaying()) {
|
||||
let reaction = this.view.session.reactions.check(this.view.player, this.view.battle, this.view.battle.playing_ship, diff);
|
||||
if (reaction) {
|
||||
return {
|
||||
foreground: async () => {
|
||||
if (reaction instanceof PersonalityReactionConversation) {
|
||||
let conversation = UIConversation.newFromPieces(this.view, reaction.messages);
|
||||
await conversation.waitEnd();
|
||||
} else {
|
||||
console.warn("[LogProcessor] Unknown personality reaction type", reaction);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
this.view.arena.markAsDead(ship);
|
||||
this.view.ship_list.refresh();
|
||||
}
|
||||
|
||||
return 3000;
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if control should be transferred to the player, or an AI, after a diff
|
||||
*/
|
||||
private checkControl(diff: BaseBattleDiff): LogProcessorDelegate {
|
||||
if (diff instanceof ShipActionEndedDiff) {
|
||||
return {
|
||||
foreground: async () => this.transferControl()
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Battle ended (victory or defeat)
|
||||
private processEndBattleEvent(event: EndBattleDiff): number {
|
||||
this.view.endBattle();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// New drone deployed
|
||||
private processDroneDeployedEvent(event: DroneDeployedDiff): number {
|
||||
let duration = this.view.arena.addDrone(event.drone);
|
||||
|
||||
if (duration) {
|
||||
this.view.gameui.audio.playOnce("battle-drone-deploy");
|
||||
/**
|
||||
* Check if a projectile is fired
|
||||
*/
|
||||
private checkProjectileFired(diff: BaseBattleDiff): LogProcessorDelegate {
|
||||
if (diff instanceof ProjectileFiredDiff) {
|
||||
let ship = this.view.battle.getShip(diff.ship_id);
|
||||
if (ship) {
|
||||
let equipment = ship.getEquipment(diff.equipment);
|
||||
if (equipment && equipment.slot_type == SlotType.Weapon) {
|
||||
let effect = new WeaponEffect(this.view.arena, ship, diff.target, equipment);
|
||||
return {
|
||||
foreground: async (animate, timer) => {
|
||||
if (animate) {
|
||||
await this.view.timer.sleep(effect.start())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return duration;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Drone destroyed
|
||||
private processDroneRecalledEvent(event: DroneRecalledDiff): number {
|
||||
let duration = this.view.arena.removeDrone(event.drone);
|
||||
/**
|
||||
* Check if a ship died
|
||||
*/
|
||||
private checkShipDeath(diff: BaseBattleDiff): LogProcessorDelegate {
|
||||
if (diff instanceof ShipDeathDiff) {
|
||||
let ship = this.view.battle.getShip(diff.ship_id);
|
||||
|
||||
if (duration) {
|
||||
this.view.gameui.audio.playOnce("battle-drone-destroy");
|
||||
if (ship) {
|
||||
let dead_ship = ship;
|
||||
return {
|
||||
foreground: async (animate) => {
|
||||
if (dead_ship.is(this.view.ship_hovered)) {
|
||||
this.view.setShipHovered(null);
|
||||
}
|
||||
this.view.arena.markAsDead(dead_ship);
|
||||
this.view.ship_list.refresh();
|
||||
if (animate) {
|
||||
await this.view.timer.sleep(2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return duration;
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the battle ended
|
||||
*/
|
||||
private checkBattleEnded(diff: BaseBattleDiff): LogProcessorDelegate {
|
||||
if (diff instanceof EndBattleDiff) {
|
||||
return {
|
||||
foreground: async () => this.view.endBattle()
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a new drone as been deployed
|
||||
*/
|
||||
private checkDroneDeployed(diff: BaseBattleDiff): LogProcessorDelegate {
|
||||
if (diff instanceof DroneDeployedDiff) {
|
||||
return {
|
||||
foreground: async (animate) => {
|
||||
let duration = this.view.arena.addDrone(diff.drone, animate);
|
||||
if (duration) {
|
||||
this.view.gameui.audio.playOnce("battle-drone-deploy");
|
||||
if (animate) {
|
||||
await this.view.timer.sleep(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a drone as been recalled
|
||||
*/
|
||||
private checkDroneRecalled(diff: BaseBattleDiff): LogProcessorDelegate {
|
||||
if (diff instanceof DroneRecalledDiff) {
|
||||
return {
|
||||
foreground: async () => {
|
||||
let duration = this.view.arena.removeDrone(diff.drone);
|
||||
if (duration) {
|
||||
this.view.gameui.audio.playOnce("battle-drone-destroy");
|
||||
await this.view.timer.sleep(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Drone applied
|
||||
|
@ -310,4 +376,20 @@ module TK.SpaceTac.UI {
|
|||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Effective work done by a subscriber
|
||||
*
|
||||
* *foreground* is started when no other delegate (background or foreground) is working
|
||||
* *background* is started when no other foreground delegate is working or pending
|
||||
*/
|
||||
export type LogProcessorDelegate = {
|
||||
foreground?: (animate: boolean, timer: Timer) => Promise<void>,
|
||||
background?: (animate: boolean, timer: Timer) => Promise<void>,
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscriber to receive diffs from the battle log
|
||||
*/
|
||||
type LogProcessorSubscriber = (diff: BaseBattleDiff) => LogProcessorDelegate
|
||||
}
|
||||
|
|
|
@ -67,6 +67,34 @@ module TK.SpaceTac.UI {
|
|||
this.refresh(animate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind to a log processor, to watch for events
|
||||
*/
|
||||
bindToLog(log: LogProcessor): void {
|
||||
log.watchForShipChange(ship => {
|
||||
return {
|
||||
foreground: async (animate) => {
|
||||
this.refresh(animate)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
log.register(diff => {
|
||||
if (diff instanceof ShipDamageDiff) {
|
||||
return {
|
||||
background: async () => {
|
||||
let item = this.findItem(diff.ship_id);
|
||||
if (item) {
|
||||
item.setDamageHit();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a ship card
|
||||
*/
|
||||
|
|
|
@ -151,6 +151,9 @@ module TK.SpaceTac.UI {
|
|||
resolve();
|
||||
});
|
||||
tween.start();
|
||||
|
||||
// By security, if the tween is destroyed before completion, we resolve the promise using the timer
|
||||
Timer.global.schedule(delay + duration, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue