1
0
Fork 0

Refactored battle log processing

This commit is contained in:
Michaël Lemaire 2017-05-30 20:23:35 +02:00
parent 8c0aa73ab7
commit efcb00e955
11 changed files with 157 additions and 33 deletions

View file

@ -36,6 +36,9 @@ module TS.SpaceTac {
// Timer to use for scheduled things
timer = Timer.global
// Indicator that an AI is playing
ai_playing = false
// Create a battle between two fleets
constructor(fleet1 = new Fleet(), fleet2 = new Fleet(), width = 1808, height = 948) {
this.fleets = [fleet1, fleet2];
@ -243,6 +246,8 @@ module TS.SpaceTac {
this.playing_ship.startTurn();
}
this.ai_playing = false;
if (log && previous_ship && this.playing_ship) {
this.log.add(new ShipChangeEvent(previous_ship, this.playing_ship));
}
@ -251,13 +256,17 @@ module TS.SpaceTac {
/**
* Make an AI play the current ship
*/
playAI(ai: AbstractAI | null = null) {
if (this.playing_ship) {
playAI(ai: AbstractAI | null = null): boolean {
if (this.playing_ship && !this.ai_playing) {
this.ai_playing = true;
if (!ai) {
// TODO Use an AI adapted to the fleet
ai = new TacticalAI(this.playing_ship, this.timer);
}
ai.play();
return true;
} else {
return false;
}
}

View file

@ -25,15 +25,28 @@ module TS.SpaceTac {
/**
* Apply the event on a battle state
*
* By default it does nothing
*/
apply(battle: Battle) {
}
/**
* Get the reverse event
*
* By default it returns a stub event that does nothing
*/
getReverse(): BaseBattleEvent {
throw new Error("No reverse implemented");
return new StubBattleEvent();
}
}
/**
* Battle event that does nothing
*/
export class StubBattleEvent extends BaseBattleEvent {
constructor() {
super("stub");
}
}

View file

@ -11,5 +11,9 @@ module TS.SpaceTac {
this.drone = drone;
}
getReverse(): BaseBattleEvent {
return new DroneDestroyedEvent(this.drone);
}
}
}

View file

@ -11,5 +11,9 @@ module TS.SpaceTac {
this.drone = drone;
}
getReverse(): BaseBattleEvent {
return new DroneDeployedEvent(this.drone);
}
}
}

View file

@ -0,0 +1,10 @@
module TS.SpaceTac.Specs {
describe("ShipChangeEvent", function () {
it("get reverse event", function () {
let ship1 = new Ship();
let ship2 = new Ship();
let event = new ShipChangeEvent(ship1, ship2);
expect(event.getReverse()).toEqual(new ShipChangeEvent(ship2, ship1));
});
});
}

View file

@ -1,7 +1,9 @@
/// <reference path="BaseBattleEvent.ts"/>
module TS.SpaceTac {
// Battle event, when a ship turn ended, and advanced to a new one
/**
* Event that changes the current playing ship
*/
export class ShipChangeEvent extends BaseLogShipEvent {
// Ship that starts playing
new_ship: Ship;
@ -11,5 +13,9 @@ module TS.SpaceTac {
this.new_ship = new_ship;
}
getReverse(): BaseBattleEvent {
return new ShipChangeEvent(this.new_ship, this.ship);
}
}
}

View file

@ -0,0 +1,9 @@
module TS.SpaceTac.Specs {
describe("ValueChangeEvent", function () {
it("get reverse event", function () {
let ship = new Ship();
let event = new ValueChangeEvent(ship, new ShipValue("hull", 15, 22), 10);
expect(event.getReverse()).toEqual(new ValueChangeEvent(ship, new ShipValue("hull", 5, 22), -10));
});
});
}

View file

@ -1,7 +1,9 @@
/// <reference path="BaseBattleEvent.ts"/>
module TS.SpaceTac {
// Event logged when a ship value or attribute changed
/**
* Event logged when a ship value or attribute changed
*/
export class ValueChangeEvent extends BaseLogShipEvent {
// Saved version of the current value
value: ShipValue;
@ -15,5 +17,11 @@ module TS.SpaceTac {
this.value = copy(value);
this.diff = diff;
}
getReverse(): BaseBattleEvent {
let value = copy(this.value);
value.set(value.get() - this.diff);
return new ValueChangeEvent(this.ship, value, -this.diff);
}
}
}

View file

@ -128,18 +128,26 @@ module TS.SpaceTac.UI {
});
this.battle.endBattle(first(this.battle.fleets, fleet => fleet.player != this.player));
});
this.inputs.bindCheat("a", "Use AI to play", () => {
if (this.interacting && this.battle.playing_ship) {
this.setInteractionEnabled(false);
this.action_bar.setShip(new Ship());
this.battle.playAI(new TacticalAI(this.battle.playing_ship));
}
});
this.inputs.bindCheat("a", "Use AI to play", () => this.playAI());
// Start processing the log
this.log_processor.start();
}
/**
* Make the AI play current ship
*
* If the AI is already playing, do nothing
*/
playAI(): void {
if (this.battle.playAI()) {
if (this.interacting) {
this.action_bar.setShip(new Ship());
}
this.setInteractionEnabled(false);
}
}
// Leaving the view, we unbind the battle
shutdown() {
this.exitTargettingMode();

View file

@ -32,27 +32,43 @@ module TS.SpaceTac.UI.Specs {
return 0;
});
expect(battle.turn).toBe(1);
expect(processor.atStart()).toBe(true);
expect(processor.atEnd()).toBe(true);
processor.stepForward();
expect(battle.turn).toBe(1);
expect(processor.atStart()).toBe(true);
expect(processor.atEnd()).toBe(true);
battle.log.add(new FakeEvent());
expect(battle.turn).toBe(1);
expect(processor.atStart()).toBe(true);
expect(processor.atEnd()).toBe(false);
processor.stepForward();
expect(battle.turn).toBe(2);
expect(processor.atStart()).toBe(false);
expect(processor.atEnd()).toBe(true);
processor.stepForward();
expect(battle.turn).toBe(2);
expect(processor.atStart()).toBe(false);
expect(processor.atEnd()).toBe(true);
processor.stepBackward();
expect(battle.turn).toBe(1);
expect(processor.atStart()).toBe(true);
expect(processor.atEnd()).toBe(false);
processor.stepBackward();
expect(battle.turn).toBe(1);
expect(processor.atStart()).toBe(true);
expect(processor.atEnd()).toBe(false);
processor.stepForward();
expect(battle.turn).toBe(2);
expect(processor.atStart()).toBe(false);
expect(processor.atEnd()).toBe(true);
})
})
}

View file

@ -56,6 +56,7 @@ module TS.SpaceTac.UI {
*/
start() {
this.subscription = this.log.subscribe(event => this.processBattleEvent(event));
this.cursor = this.log.events.length - 1;
this.battle.getBootstrapEvents().forEach(event => this.processBattleEvent(event));
}
@ -63,9 +64,9 @@ module TS.SpaceTac.UI {
* Make a step backward in time
*/
stepBackward() {
if (this.cursor >= 0) {
this.processBattleEvent(this.log.events[this.cursor].getReverse());
if (!this.atStart()) {
this.cursor -= 1;
this.processBattleEvent(this.log.events[this.cursor + 1].getReverse());
}
}
@ -73,7 +74,7 @@ module TS.SpaceTac.UI {
* Make a step forward in time
*/
stepForward() {
if (this.cursor < this.log.events.length - 1) {
if (!this.atEnd()) {
this.cursor += 1;
this.processBattleEvent(this.log.events[this.cursor]);
}
@ -85,6 +86,9 @@ module TS.SpaceTac.UI {
* This will rewind all applied event
*/
jumpToStart() {
while (!this.atStart()) {
this.stepBackward();
}
}
/**
@ -93,6 +97,34 @@ module TS.SpaceTac.UI {
* This will apply all remaining event
*/
jumpToEnd() {
while (!this.atEnd()) {
this.stepForward();
}
}
/**
* Check if we are currently at the start of the log
*/
atStart(): boolean {
return this.cursor < 0;
}
/**
* Check if we are currently at the end of the log
*/
atEnd(): boolean {
return this.cursor >= this.log.events.length - 1;
}
/**
* Check if we need a player or AI to interact at this point
*/
getPlayerNeeded(): Player | null {
if (this.atEnd()) {
return this.battle.playing_ship ? this.battle.playing_ship.getPlayer() : null;
} else {
return null;
}
}
/**
@ -176,6 +208,28 @@ module TS.SpaceTac.UI {
} else if (event instanceof DroneAppliedEvent) {
this.processDroneAppliedEvent(event);
}
// FIXME temporary fix for cursor not being forwarded
let cursor = this.log.events.indexOf(event);
if (cursor >= 0) {
this.cursor = cursor;
}
// Transfer control to the needed player
let player = this.getPlayerNeeded();
if (player) {
if (this.battle.playing_ship && !this.battle.playing_ship.alive) {
this.view.setInteractionEnabled(false);
this.battle.advanceToNextShip();
this.delayNextEvents(200);
} else if (player === this.view.player) {
this.view.setInteractionEnabled(true);
} else {
this.view.playAI();
}
} else {
this.view.setInteractionEnabled(false);
}
}
// Destroy the log processor
@ -191,24 +245,7 @@ module TS.SpaceTac.UI {
private processShipChangeEvent(event: ShipChangeEvent): void {
this.view.arena.setShipPlaying(event.new_ship);
this.view.ship_list.setPlaying(event.new_ship);
if (this.battle.canPlay(this.view.player)) {
// Player turn
this.view.gameui.audio.playOnce("battle-ship-change");
this.view.setInteractionEnabled(true);
} else {
this.view.setInteractionEnabled(false);
if (event.new_ship.isAbleToPlay()) {
// AI turn
this.view.gameui.audio.playOnce("battle-ship-change");
this.battle.playAI();
} else {
// Ship unable to play, skip turn
this.view.timer.schedule(event.new_ship.alive ? 2000 : 200, () => {
this.battle.advanceToNextShip();
});
}
}
this.view.gameui.audio.playOnce("battle-ship-change");
}
// Damage to ship