diff --git a/src/core/Battle.ts b/src/core/Battle.ts
index cf832f1..6cf0977 100644
--- a/src/core/Battle.ts
+++ b/src/core/Battle.ts
@@ -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;
}
}
diff --git a/src/core/events/BaseBattleEvent.ts b/src/core/events/BaseBattleEvent.ts
index f77231a..aa12e78 100644
--- a/src/core/events/BaseBattleEvent.ts
+++ b/src/core/events/BaseBattleEvent.ts
@@ -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");
}
}
diff --git a/src/core/events/DroneDeployedEvent.ts b/src/core/events/DroneDeployedEvent.ts
index fd220ad..de86aa7 100644
--- a/src/core/events/DroneDeployedEvent.ts
+++ b/src/core/events/DroneDeployedEvent.ts
@@ -11,5 +11,9 @@ module TS.SpaceTac {
this.drone = drone;
}
+
+ getReverse(): BaseBattleEvent {
+ return new DroneDestroyedEvent(this.drone);
+ }
}
}
diff --git a/src/core/events/DroneDestroyedEvent.ts b/src/core/events/DroneDestroyedEvent.ts
index 0755874..5f23157 100644
--- a/src/core/events/DroneDestroyedEvent.ts
+++ b/src/core/events/DroneDestroyedEvent.ts
@@ -11,5 +11,9 @@ module TS.SpaceTac {
this.drone = drone;
}
+
+ getReverse(): BaseBattleEvent {
+ return new DroneDeployedEvent(this.drone);
+ }
}
}
diff --git a/src/core/events/ShipChangeEvent.spec.ts b/src/core/events/ShipChangeEvent.spec.ts
new file mode 100644
index 0000000..d024655
--- /dev/null
+++ b/src/core/events/ShipChangeEvent.spec.ts
@@ -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));
+ });
+ });
+}
\ No newline at end of file
diff --git a/src/core/events/ShipChangeEvent.ts b/src/core/events/ShipChangeEvent.ts
index f63d5a4..5371a22 100644
--- a/src/core/events/ShipChangeEvent.ts
+++ b/src/core/events/ShipChangeEvent.ts
@@ -1,7 +1,9 @@
///
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);
+ }
}
}
diff --git a/src/core/events/ValueChangeEvent.spec.ts b/src/core/events/ValueChangeEvent.spec.ts
new file mode 100644
index 0000000..b72ca20
--- /dev/null
+++ b/src/core/events/ValueChangeEvent.spec.ts
@@ -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));
+ });
+ });
+}
\ No newline at end of file
diff --git a/src/core/events/ValueChangeEvent.ts b/src/core/events/ValueChangeEvent.ts
index 12bbd9d..38dacb5 100644
--- a/src/core/events/ValueChangeEvent.ts
+++ b/src/core/events/ValueChangeEvent.ts
@@ -1,7 +1,9 @@
///
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);
+ }
}
}
diff --git a/src/ui/battle/BattleView.ts b/src/ui/battle/BattleView.ts
index 10ead13..c93861c 100644
--- a/src/ui/battle/BattleView.ts
+++ b/src/ui/battle/BattleView.ts
@@ -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();
diff --git a/src/ui/battle/LogProcessor.spec.ts b/src/ui/battle/LogProcessor.spec.ts
index 88319a3..27b7a1e 100644
--- a/src/ui/battle/LogProcessor.spec.ts
+++ b/src/ui/battle/LogProcessor.spec.ts
@@ -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);
})
})
}
\ No newline at end of file
diff --git a/src/ui/battle/LogProcessor.ts b/src/ui/battle/LogProcessor.ts
index 144551a..28e8dda 100644
--- a/src/ui/battle/LogProcessor.ts
+++ b/src/ui/battle/LogProcessor.ts
@@ -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