1
0
Fork 0

Refactored MoveEvent for new battle log flow

This commit is contained in:
Michaël Lemaire 2017-05-30 18:24:55 +02:00
parent 83807beb20
commit 8c0aa73ab7
14 changed files with 199 additions and 43 deletions

35
src/core/ArenaLocation.ts Normal file
View file

@ -0,0 +1,35 @@
module TS.SpaceTac {
/**
* Location in the arena
*/
export class ArenaLocation {
x: number
y: number
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
/**
* Get the distance to another location
*/
getDistanceTo(other: ArenaLocation) {
let dx = this.x - other.x;
let dy = this.y - other.y;
return Math.sqrt(dx * dx + dy * dy);
}
}
/**
* Location in the arena, with a facing angle in radians
*/
export class ArenaLocationAngle extends ArenaLocation {
angle: number
constructor(x = 0, y = 0, angle = 0) {
super(x, y);
this.angle = angle;
}
}
}

View file

@ -284,7 +284,7 @@ module TS.SpaceTac {
// Simulate initial ship placement
this.play_order.forEach(ship => {
let event = new MoveEvent(ship, ship.arena_x, ship.arena_y, 0);
let event = new MoveEvent(ship, ship.location, ship.location);
event.initial = true;
result.push(event);
});

View file

@ -46,11 +46,11 @@ module TS.SpaceTac.Specs {
stats.processLog(battle.log, battle.fleets[0]);
expect(stats.stats).toEqual({});
battle.log.add(new MoveEvent(attacker, 0, 0, 10));
battle.log.add(new MoveEvent(attacker, new ArenaLocationAngle(0, 0), new ArenaLocationAngle(10, 0)));
stats.processLog(battle.log, battle.fleets[0]);
expect(stats.stats).toEqual({ "Move distance (km)": [10, 0] });
battle.log.add(new MoveEvent(defender, 0, 0, 58));
battle.log.add(new MoveEvent(defender, new ArenaLocationAngle(10, 5), new ArenaLocationAngle(10, 63)));
stats.processLog(battle.log, battle.fleets[0]);
expect(stats.stats).toEqual({ "Move distance (km)": [10, 58] });
})

View file

@ -44,7 +44,7 @@ module TS.SpaceTac {
if (event instanceof DamageEvent) {
this.addStat("Damage dealt", event.hull + event.shield, event.ship.fleet !== attacker);
} else if (event instanceof MoveEvent) {
this.addStat("Move distance (km)", event.distance, event.ship.fleet === attacker);
this.addStat("Move distance (km)", event.getDistance(), event.ship.fleet === attacker);
} else if (event instanceof DroneDeployedEvent) {
this.addStat("Drones deployed", 1, event.ship.fleet === attacker);
}

View file

@ -39,7 +39,7 @@ module TS.SpaceTac.Specs {
expect(battle.log.events).toEqual([]);
ship.moveTo(70, 50);
expect(battle.log.events).toEqual([new MoveEvent(ship, 70, 50, 20)]);
expect(battle.log.events).toEqual([new MoveEvent(ship, new ArenaLocationAngle(50, 50, Math.PI), new ArenaLocationAngle(70, 50, 0))]);
});
it("applies equipment cooldown", function () {

View file

@ -120,6 +120,13 @@ module TS.SpaceTac {
model.slots.forEach(slot => this.addSlot(slot));
}
/**
* Return the current location and angle of this ship
*/
get location(): ArenaLocationAngle {
return new ArenaLocationAngle(this.arena_x, this.arena_y, this.arena_angle);
}
// Returns true if the ship is able to play
// If *check_ap* is true, ap_current=0 will make this function return false
isAbleToPlay(check_ap: boolean = true): boolean {
@ -426,7 +433,7 @@ module TS.SpaceTac {
this.setArenaFacingAngle(angle);
if (log) {
this.addBattleEvent(new MoveEvent(this, this.arena_x, this.arena_y, 0));
this.addBattleEvent(new MoveEvent(this, copy(this.location), new ArenaLocationAngle(this.arena_x, this.arena_y, angle)));
}
}
}
@ -437,13 +444,14 @@ module TS.SpaceTac {
let dx = x - this.arena_x;
let dy = y - this.arena_y;
if (dx != 0 || dy != 0) {
let start = copy(this.location);
let angle = Math.atan2(dy, dx);
this.setArenaFacingAngle(angle);
this.setArenaPosition(x, y);
if (log) {
this.addBattleEvent(new MoveEvent(this, x, y, Math.sqrt(dx * dx + dy * dy)));
this.addBattleEvent(new MoveEvent(this, start, copy(this.location)));
}
}
}

View file

@ -70,10 +70,9 @@ module TS.SpaceTac {
expect(battle.log.events[1].code).toEqual("move");
expect(battle.log.events[1].ship).toBe(ship);
let target: any = battle.log.events[1].target;
expect(target.ship).toBeNull();
expect(target.x).toBeCloseTo(3.535533, 0.00001);
expect(target.y).toBeCloseTo(3.535533, 0.00001);
let dest = (<MoveEvent>battle.log.events[1]).end;
expect(dest.x).toBeCloseTo(3.535533, 0.00001);
expect(dest.y).toBeCloseTo(3.535533, 0.00001);
});
it("can't move too much near another ship", function () {

View file

@ -3,8 +3,6 @@ module TS.SpaceTac {
* Base class for battle events
*
* Events are the proper way to modify the battle state
*
* All events may be applied either forward or backward on a battle state
*/
export class BaseBattleEvent {
// Code of the event (its type)
@ -26,15 +24,16 @@ module TS.SpaceTac {
}
/**
* Apply the event forward on a battle state
* Apply the event on a battle state
*/
apply(battle: Battle) {
}
/**
* Apply the event backward (revert it) on a battle state
* Get the reverse event
*/
revert(battle: Battle) {
getReverse(): BaseBattleEvent {
throw new Error("No reverse implemented");
}
}

View file

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

View file

@ -1,19 +1,32 @@
/// <reference path="BaseBattleEvent.ts"/>
module TS.SpaceTac {
// Event logged when a ship moves
export class MoveEvent extends BaseLogShipTargetEvent {
// Distance traveled
distance: number
/**
* Event making a ship move
*/
export class MoveEvent extends BaseLogShipEvent {
// Previous location
start: ArenaLocationAngle
// New facing angle, in radians
facing_angle: number
// New location
end: ArenaLocationAngle
constructor(ship: Ship, x: number, y: number, distance: number) {
super("move", ship, Target.newFromLocation(x, y));
constructor(ship: Ship, start: ArenaLocationAngle, end: ArenaLocationAngle) {
super("move", ship, Target.newFromLocation(end.x, end.y));
this.distance = distance;
this.facing_angle = ship.arena_angle;
this.start = start;
this.end = end;
}
getReverse(): BaseBattleEvent {
return new MoveEvent(this.ship, this.end, this.start);
}
/**
* Get the distance travelled
*/
getDistance(): number {
return this.start.getDistanceTo(this.end);
}
}
}

View file

@ -166,7 +166,8 @@ module TS.SpaceTac.UI {
this.displayEffect(`${event.hull + event.shield} damage`, false);
return 0;
} else if (event instanceof MoveEvent && !event.initial) {
let duration = this.moveTo(event.target.x, event.target.y, event.facing_angle, true);
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);
return duration;
} else {
return 0;

View file

@ -66,8 +66,6 @@ module TS.SpaceTac.UI {
this.battle.timer = this.timer;
this.log_processor = new LogProcessor(this);
this.toggle_tactical_mode = new Toggle(
() => this.arena.setTacticalMode(true),
() => this.arena.setTacticalMode(false)
@ -79,6 +77,7 @@ module TS.SpaceTac.UI {
super.create();
var game = this.game;
this.log_processor = new LogProcessor(this);
// Add layers
this.layer_background = this.addLayer();

View file

@ -0,0 +1,58 @@
/// <reference path="../TestGame.ts"/>
/// <reference path="../../core/events/BaseBattleEvent.ts"/>
module TS.SpaceTac.UI.Specs {
class FakeEvent extends BaseBattleEvent {
diff: number
constructor(diff = 1) {
super("fake");
this.diff = diff;
}
apply(battle: Battle) {
battle.turn += this.diff;
}
getReverse(): BaseBattleEvent {
return new FakeEvent(-this.diff);
}
}
describe("LogProcessor", function () {
let testgame = setupBattleview();
it("steps forward and backward in time", function () {
let battle = testgame.battleview.battle;
battle.log.clear();
let processor = new LogProcessor(testgame.battleview);
processor.register(event => {
event.apply(battle);
return 0;
});
expect(battle.turn).toBe(1);
processor.stepForward();
expect(battle.turn).toBe(1);
battle.log.add(new FakeEvent());
expect(battle.turn).toBe(1);
processor.stepForward();
expect(battle.turn).toBe(2);
processor.stepForward();
expect(battle.turn).toBe(2);
processor.stepBackward();
expect(battle.turn).toBe(1);
processor.stepBackward();
expect(battle.turn).toBe(1);
processor.stepForward();
expect(battle.turn).toBe(2);
})
})
}

View file

@ -9,32 +9,35 @@ module TS.SpaceTac.UI {
*/
export class LogProcessor {
// Link to the battle view
private view: BattleView;
private view: BattleView
// Link to the battle
private battle: Battle;
private battle: Battle
// Link to the battle log
private log: BattleLog;
private log: BattleLog
// Subscription identifier
private subscription: any = null;
private subscription: any = null
// Delay before processing next events
private delayed = false;
private delayed = false
// Processing queue, when delay is active
private queue: BaseBattleEvent[] = [];
private queue: BaseBattleEvent[] = []
// Forward events to other subscribers
private forwarding: LogSubscriber[] = [];
private forwarding: LogSubscriber[] = []
// Current position in the battle log
private cursor = -1;
constructor(view: BattleView) {
this.view = view;
this.battle = view.battle;
this.log = view.battle.log;
/*view.inputs.bindCheat("PageUp", "Step backward", () => {
view.inputs.bindCheat("PageUp", "Step backward", () => {
this.stepBackward();
});
view.inputs.bindCheat("PageDown", "Step forward", () => {
@ -45,7 +48,7 @@ module TS.SpaceTac.UI {
});
view.inputs.bindCheat("End", "Jump to end", () => {
this.jumpToEnd();
});*/
});
}
/**
@ -56,6 +59,42 @@ module TS.SpaceTac.UI {
this.battle.getBootstrapEvents().forEach(event => this.processBattleEvent(event));
}
/**
* Make a step backward in time
*/
stepBackward() {
if (this.cursor >= 0) {
this.processBattleEvent(this.log.events[this.cursor].getReverse());
this.cursor -= 1;
}
}
/**
* Make a step forward in time
*/
stepForward() {
if (this.cursor < this.log.events.length - 1) {
this.cursor += 1;
this.processBattleEvent(this.log.events[this.cursor]);
}
}
/**
* Jump to the start of the log
*
* This will rewind all applied event
*/
jumpToStart() {
}
/**
* Jump to the end of the log
*
* This will apply all remaining event
*/
jumpToEnd() {
}
/**
* Register a sub-subscriber.
*
@ -111,10 +150,6 @@ module TS.SpaceTac.UI {
* Process a single event
*/
processBattleEvent(event: BaseBattleEvent) {
if (!this.subscription) {
return;
}
if (this.delayed) {
this.queue.push(event);
return;