1
0
Fork 0

Allow to select any ship to plan its turn

This commit is contained in:
Michaël Lemaire 2019-05-20 00:32:15 +02:00
parent 906322003f
commit 0b71a531cc
54 changed files with 180 additions and 1050 deletions

View file

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View file

@ -1,30 +1,5 @@
module TK.SpaceTac { module TK.SpaceTac {
testing("Battle", test => { testing("Battle", test => {
test.case("defines play order by initiative throws", check => {
var fleet1 = new Fleet();
var fleet2 = new Fleet();
var ship1 = new Ship(fleet1, "F1S1");
TestTools.setAttribute(ship1, "initiative", 2);
var ship2 = new Ship(fleet1, "F1S2");
TestTools.setAttribute(ship2, "initiative", 4);
var ship3 = new Ship(fleet1, "F1S3");
TestTools.setAttribute(ship3, "initiative", 1);
var ship4 = new Ship(fleet2, "F2S1");
TestTools.setAttribute(ship4, "initiative", 8);
var ship5 = new Ship(fleet2, "F2S2");
TestTools.setAttribute(ship5, "initiative", 2);
var battle = new Battle(fleet1, fleet2);
check.equals(battle.play_order.length, 0);
var gen = new SkewedRandomGenerator([1.0, 0.1, 1.0, 0.2, 0.6]);
battle.throwInitiative(gen);
check.equals(battle.play_order.length, 5);
check.equals(battle.play_order, [ship1, ship4, ship5, ship3, ship2]);
});
test.case("places ships on lines, facing the arena center", check => { test.case("places ships on lines, facing the arena center", check => {
var fleet1 = new Fleet(); var fleet1 = new Fleet();
var fleet2 = new Fleet(); var fleet2 = new Fleet();
@ -59,74 +34,7 @@ module TK.SpaceTac {
check.nears(ship5.arena_angle, Math.PI); check.nears(ship5.arena_angle, Math.PI);
}); });
test.case("advances to next ship in play order", check => { test.case("detects victory condition and logs a final EndBattleDiff", check => {
var fleet1 = new Fleet();
var fleet2 = new Fleet();
var ship1 = new Ship(fleet1, "ship1");
var ship2 = new Ship(fleet1, "ship2");
var ship3 = new Ship(fleet2, "ship3");
var battle = new Battle(fleet1, fleet2);
battle.ships.list().forEach(ship => TestTools.setShipModel(ship, 10, 0));
// Check empty play_order case
check.equals(battle.playing_ship, null);
battle.advanceToNextShip();
check.equals(battle.playing_ship, null);
// Force play order
iforeach(battle.iships(), ship => TestTools.setAttribute(ship, "initiative", 1));
var gen = new SkewedRandomGenerator([0.1, 0.2, 0.0]);
battle.throwInitiative(gen);
check.equals(battle.playing_ship, null);
battle.advanceToNextShip();
check.same(battle.playing_ship, ship2);
battle.advanceToNextShip();
check.same(battle.playing_ship, ship1);
battle.advanceToNextShip();
check.same(battle.playing_ship, ship3);
battle.advanceToNextShip();
check.same(battle.playing_ship, ship2);
// A dead ship is skipped
ship1.setDead();
battle.advanceToNextShip();
check.same(battle.playing_ship, ship3);
// Playing ship dies
ship3.setDead();
battle.advanceToNextShip();
check.same(battle.playing_ship, ship2);
});
test.case("handles the suicide case (playing ship dies because of its action)", check => {
let battle = TestTools.createBattle(3, 1);
let [ship1, ship2, ship3, ship4] = battle.play_order;
ship1.setArenaPosition(0, 0);
ship2.setArenaPosition(0, 0);
ship3.setArenaPosition(1000, 1000);
ship4.setArenaPosition(1000, 1000);
let weapon = TestTools.addWeapon(ship1, 8000, 0, 50, 100);
check.in("initially", check => {
check.same(battle.playing_ship, ship1, "playing ship");
check.equals(battle.ships.list().filter(ship => ship.alive), [ship1, ship2, ship3, ship4], "alive ships");
});
let result = battle.applyOneAction(weapon.id, Target.newFromLocation(0, 0));
check.equals(result, true, "action applied successfully");
check.in("after weapon", check => {
check.same(battle.playing_ship, ship3, "playing ship");
check.equals(battle.ships.list().filter(ship => ship.alive), [ship3, ship4], "alive ships");
});
});
test.case("detects victory condition and logs a final EndBattleEvent", check => {
var fleet1 = new Fleet(); var fleet1 = new Fleet();
var fleet2 = new Fleet(); var fleet2 = new Fleet();
@ -137,12 +45,11 @@ module TK.SpaceTac {
var battle = new Battle(fleet1, fleet2); var battle = new Battle(fleet1, fleet2);
battle.ships.list().forEach(ship => TestTools.setShipModel(ship, 10, 0)); battle.ships.list().forEach(ship => TestTools.setShipModel(ship, 10, 0));
battle.start(); battle.start();
battle.play_order = [ship3, ship2, ship1];
check.equals(battle.ended, false); check.equals(battle.ended, false);
ship1.setDead(); ship1.setDead();
ship2.setDead(); ship2.setDead();
battle.advanceToNextShip(); battle.applyTurnPlan({ fleets: [] });
check.equals(battle.ended, true); check.equals(battle.ended, true);
let diff = battle.log.get(battle.log.count() - 1); let diff = battle.log.get(battle.log.count() - 1);
@ -197,10 +104,9 @@ module TK.SpaceTac {
ship4.setArenaPosition(12, 12); ship4.setArenaPosition(12, 12);
var battle = new Battle(fleet1); var battle = new Battle(fleet1);
battle.throwInitiative(new SkewedRandomGenerator([5, 4, 3, 2]));
var result = battle.collectShipsInCircle(Target.newFromLocation(5, 8), 3); var result = battle.collectShipsInCircle(Target.newFromLocation(5, 8), 3);
check.equals(result, [ship2, ship3]); check.equals(sortedBy(result, ship => ship.name), [ship2, ship3]);
}); });
test.case("adds and remove drones", check => { test.case("adds and remove drones", check => {
@ -223,60 +129,6 @@ module TK.SpaceTac {
check.equals(battle.drones.count(), 0); check.equals(battle.drones.count(), 0);
}); });
test.case("checks if a player is able to play", check => {
let battle = new Battle();
let player = new Player();
check.equals(battle.canPlay(player), false);
let ship = new Ship();
TestTools.setShipPlaying(battle, ship);
check.equals(battle.canPlay(player), false);
ship.fleet.setPlayer(player);
check.equals(battle.canPlay(player), true);
});
test.case("gets the number of turns before a specific ship plays", check => {
let battle = TestTools.createBattle(2, 1);
check.in("initial", check => {
check.same(battle.playing_ship, battle.play_order[0], "first ship playing");
check.equals(battle.getPlayOrder(battle.play_order[0]), 0);
check.equals(battle.getPlayOrder(battle.play_order[1]), 1);
check.equals(battle.getPlayOrder(battle.play_order[2]), 2);
});
battle.advanceToNextShip();
check.in("1 step", check => {
check.same(battle.playing_ship, battle.play_order[1], "second ship playing");
check.equals(battle.getPlayOrder(battle.play_order[0]), 2);
check.equals(battle.getPlayOrder(battle.play_order[1]), 0);
check.equals(battle.getPlayOrder(battle.play_order[2]), 1);
});
battle.advanceToNextShip();
check.in("2 steps", check => {
check.same(battle.playing_ship, battle.play_order[2], "third ship playing");
check.equals(battle.getPlayOrder(battle.play_order[0]), 1);
check.equals(battle.getPlayOrder(battle.play_order[1]), 2);
check.equals(battle.getPlayOrder(battle.play_order[2]), 0);
});
battle.advanceToNextShip();
check.in("3 steps", check => {
check.same(battle.playing_ship, battle.play_order[0], "first ship playing");
check.equals(battle.getPlayOrder(battle.play_order[0]), 0);
check.equals(battle.getPlayOrder(battle.play_order[1]), 1);
check.equals(battle.getPlayOrder(battle.play_order[2]), 2);
});
});
test.case("lists area effects", check => { test.case("lists area effects", check => {
let battle = new Battle(); let battle = new Battle();
let ship = battle.fleets[0].addShip(); let ship = battle.fleets[0].addShip();
@ -318,15 +170,12 @@ module TK.SpaceTac {
test.case("is serializable", check => { test.case("is serializable", check => {
let battle = Battle.newQuickRandom(); let battle = Battle.newQuickRandom();
battle.ai_playing = true;
let serializer = new Serializer(TK.SpaceTac); let serializer = new Serializer(TK.SpaceTac);
let data = serializer.serialize(battle); let data = serializer.serialize(battle);
let loaded = serializer.unserialize(data); let loaded = serializer.unserialize(data);
check.equals(loaded.ai_playing, false, "ai playing is reset");
battle.ai_playing = false;
check.equals(loaded, battle, "unserialized == initial"); check.equals(loaded, battle, "unserialized == initial");
let session = new GameSession(); let session = new GameSession();
@ -340,15 +189,15 @@ module TK.SpaceTac {
check.greaterorequal(ratio, 1.2, `quick battle serialized size (${data.length}) should be larger than campaign's (${data1.length})`); check.greaterorequal(ratio, 1.2, `quick battle serialized size (${data.length}) should be larger than campaign's (${data1.length})`);
}); });
test.case("can revert the last action", check => { test.case("can revert the last turn", check => {
let battle = new Battle(); let battle = new Battle();
let ship = battle.fleets[0].addShip(); let ship = battle.fleets[0].addShip();
ship.setValue("hull", 13); ship.setValue("hull", 13);
battle.log.clear(); battle.log.clear();
battle.log.add(new ShipValueDiff(ship, "hull", 4)); battle.log.add(new ShipValueDiff(ship, "hull", 4));
battle.log.add(new ShipActionUsedDiff(ship, EndTurnAction.SINGLETON, Target.newFromShip(ship))); battle.log.add(new TurnStartDiff([]));
battle.log.add(new ShipValueDiff(ship, "hull", 7)); battle.log.add(new ShipValueDiff(ship, "hull", 7));
battle.log.add(new ShipActionUsedDiff(ship, EndTurnAction.SINGLETON, Target.newFromShip(ship))); battle.log.add(new TurnStartDiff([]));
battle.log.add(new ShipValueDiff(ship, "hull", 2)); battle.log.add(new ShipValueDiff(ship, "hull", 2));
check.in("initial state", check => { check.in("initial state", check => {
@ -356,23 +205,23 @@ module TK.SpaceTac {
check.equals(battle.log.count(), 5, "log count=5"); check.equals(battle.log.count(), 5, "log count=5");
}); });
battle.revertOneAction(); battle.revertOneTurn();
check.in("revert 1 action", check => { check.in("revert 1 turn", check => {
check.equals(ship.getValue("hull"), 11, "hull=11"); check.equals(ship.getValue("hull"), 11, "hull=11");
check.equals(battle.log.count(), 3, "log count=3"); check.equals(battle.log.count(), 3, "log count=3");
}); });
battle.revertOneAction(); battle.revertOneTurn();
check.in("revert 2 actions", check => { check.in("revert 2 turns", check => {
check.equals(ship.getValue("hull"), 4, "hull=4"); check.equals(ship.getValue("hull"), 4, "hull=4");
check.equals(battle.log.count(), 1, "log count=1"); check.equals(battle.log.count(), 1, "log count=1");
}); });
battle.revertOneAction(); battle.revertOneTurn();
check.in("revert 3 actions", check => { check.in("revert 3 turns", check => {
check.equals(ship.getValue("hull"), 0, "hull=0"); check.equals(ship.getValue("hull"), 0, "hull=0");
check.equals(battle.log.count(), 0, "log count=0"); check.equals(battle.log.count(), 0, "log count=0");
}); });

View file

@ -18,11 +18,7 @@ module TK.SpaceTac {
// Container of all engaged ships // Container of all engaged ships
ships: RObjectContainer<Ship> ships: RObjectContainer<Ship>
// List of playing ships, sorted by their initiative throw // Current battle turn count
play_order: Ship[]
play_index = -1
// Current battle "cycle" (one cycle is one turn done for all ships in the play order)
cycle = 0 cycle = 0
// List of deployed drones // List of deployed drones
@ -32,13 +28,9 @@ module TK.SpaceTac {
width: number width: number
height: number height: number
// Indicator that an AI is playing
ai_playing = false
constructor(fleet1 = new Fleet(new Player("Attacker")), fleet2 = new Fleet(new Player("Defender")), width = 1920, height = 1080) { constructor(fleet1 = new Fleet(new Player("Attacker")), fleet2 = new Fleet(new Player("Defender")), width = 1920, height = 1080) {
this.fleets = [fleet1, fleet2]; this.fleets = [fleet1, fleet2];
this.ships = new RObjectContainer(fleet1.ships.concat(fleet2.ships)); this.ships = new RObjectContainer(fleet1.ships.concat(fleet2.ships));
this.play_order = [];
this.width = width; this.width = width;
this.height = height; this.height = height;
@ -50,10 +42,6 @@ module TK.SpaceTac {
}); });
} }
postUnserialize() {
this.ai_playing = false;
}
/** /**
* Property is true if the battle has ended * Property is true if the battle has ended
*/ */
@ -64,9 +52,10 @@ module TK.SpaceTac {
/** /**
* Apply a turn plan through a resolution * Apply a turn plan through a resolution
*/ */
applyTurnPlan(plan: TurnPlan): void { applyTurnPlan(plan: TurnPlan, random?: RandomGenerator): void {
const resolution = new TurnResolution(this, plan); const resolution = new TurnResolution(this, plan, random);
resolution.resolve(); resolution.resolve();
this.performChecks();
} }
/** /**
@ -93,13 +82,6 @@ module TK.SpaceTac {
return result; return result;
} }
/**
* Get the currently playing ship
*/
get playing_ship(): Ship | null {
return this.play_order[this.play_index] || null;
}
/** /**
* Get a ship by its ID. * Get a ship by its ID.
*/ */
@ -139,86 +121,7 @@ module TK.SpaceTac {
* This can be used by the UI to determine if player interaction is allowed * This can be used by the UI to determine if player interaction is allowed
*/ */
canPlay(player: Player): boolean { canPlay(player: Player): boolean {
if (this.ended) { return !this.ended;
return false;
} else if (this.playing_ship && player.is(this.playing_ship.fleet.player)) {
return this.playing_ship.isAbleToPlay(false);
} else {
return false;
}
}
// Create play order, performing an initiative throw
throwInitiative(gen: RandomGenerator = new RandomGenerator()): void {
var play_order: Ship[] = [];
// Throw each ship's initiative
this.fleets.forEach(function (fleet: Fleet) {
fleet.ships.forEach(function (ship: Ship) {
ship.throwInitiative(gen);
play_order.push(ship);
});
});
// Sort by throw result
play_order.sort(function (ship1: Ship, ship2: Ship) {
return (ship2.play_priority - ship1.play_priority);
});
this.play_order = play_order;
this.play_index = -1;
}
/**
* Get the number of turns before a specific ship plays (currently playing ship will return 0).
*
* Returns -1 if the ship is not in the play list.
*/
getPlayOrder(ship: Ship): number {
let index = this.play_order.indexOf(ship);
if (index < 0) {
return -1;
} else {
let result = index - this.play_index;
return (result < 0) ? (result + this.play_order.length) : result;
}
}
/**
* Add a ship in the play order list
*/
removeFromPlayOrder(idx: number): void {
this.play_order.splice(idx, 1);
if (idx <= this.play_index) {
this.play_index -= 1;
}
}
/**
* Remove a ship from the play order list
*/
insertInPlayOrder(idx: number, ship: Ship): void {
this.play_order.splice(idx, 0, ship);
if (idx <= this.play_index) {
this.play_index += 1;
}
}
/**
* Set the currently playing ship
*/
setPlayingShip(ship: Ship): void {
let current = this.playing_ship;
if (current) {
current.playing = false;
}
this.play_index = this.play_order.indexOf(ship);
this.ai_playing = false;
current = this.playing_ship;
if (current) {
current.playing = true;
}
} }
// Defines the initial ship positions of all engaged fleets // Defines the initial ship positions of all engaged fleets
@ -245,25 +148,12 @@ module TK.SpaceTac {
} }
/** /**
* Get the next playing ship * Make an AI play the current turn
*/
getNextShip(): Ship {
return this.play_order[(this.play_index + 1) % this.play_order.length];
}
/**
* Make an AI play the current ship
*
* This will run asynchronous work in background, until the playing ship is changed
*/ */
playAI(debug = false): boolean { playAI(debug = false): boolean {
if (this.playing_ship && !this.ai_playing) { // TODO
this.ai_playing = true; //AIWorker.process(this, debug);
AIWorker.process(this, debug); return false;
return true;
} else {
return false;
}
} }
/** /**
@ -278,19 +168,6 @@ module TK.SpaceTac {
this.cycle = 1; this.cycle = 1;
this.placeShips(); this.placeShips();
iforeach(this.iships(), ship => ship.restoreInitialState()); iforeach(this.iships(), ship => ship.restoreInitialState());
this.throwInitiative();
this.setPlayingShip(this.play_order[0]);
}
/**
* Force current ship's turn to end, then advance to the next one
*/
advanceToNextShip(): void {
if (this.playing_ship) {
this.applyOneAction(EndTurnAction.SINGLETON.id);
} else if (this.play_order.length) {
this.setPlayingShip(this.play_order[0]);
}
} }
/** /**
@ -364,7 +241,7 @@ module TK.SpaceTac {
* At the end of the action, some checks will be applied to ensure the battle state is consistent * At the end of the action, some checks will be applied to ensure the battle state is consistent
*/ */
applyOneAction(action_id: RObjectId, target?: Target): boolean { applyOneAction(action_id: RObjectId, target?: Target): boolean {
let ship = this.playing_ship; let ship = first(this.ships.list(), ship => ship.actions.getById(action_id) !== null);
if (ship) { if (ship) {
let action = ship.actions.getById(action_id); let action = ship.actions.getById(action_id);
if (action) { if (action) {
@ -377,11 +254,6 @@ module TK.SpaceTac {
if (!this.ended) { if (!this.ended) {
this.applyDiffs([new ShipActionEndedDiff(ship, action, target)]); this.applyDiffs([new ShipActionEndedDiff(ship, action, target)]);
if (ship.playing && ship.getValue("hull") <= 0) {
// Playing ship died during its action, force a turn end
this.applyOneAction(EndTurnAction.SINGLETON.id);
}
} }
return true; return true;
@ -399,13 +271,13 @@ module TK.SpaceTac {
} }
/** /**
* Revert the last applied action * Revert the last turn
* *
* This will remove diffs from the log, so pay attention to other log clients! * This will remove diffs from the log, so pay attention to other log clients!
*/ */
revertOneAction(): void { revertOneTurn(): void {
let client = new BattleLogClient(this, this.log); let client = new BattleLogClient(this, this.log);
while (!client.atStart() && !(client.getCurrent() instanceof ShipActionUsedDiff)) { while (!client.atStart() && !(client.getCurrent() instanceof TurnStartDiff)) {
client.backward(); client.backward();
} }
if (!client.atStart()) { if (!client.atStart()) {

View file

@ -28,24 +28,27 @@ module TK.SpaceTac.Specs {
], "fixed values"); ], "fixed values");
}) })
test.case("marks ships as dead, except the playing one", check => { test.case("marks ships as dead", check => {
let battle = TestTools.createBattle(1, 2); let battle = TestTools.createBattle(1, 2);
let [ship1, ship2, ship3] = battle.play_order; const ship1 = battle.fleets[0].ships[0];
const ship2 = battle.fleets[1].ships[0];
let checks = new BattleChecks(battle); let checks = new BattleChecks(battle);
check.equals(checks.checkDeadShips(), [], "no ship to mark as dead"); check.equals(checks.checkDeadShips(), [], "no ship to mark as dead");
battle.ships.list().forEach(ship => ship.setValue("hull", 0)); ship2.setValue("hull", 0);
let result = checks.checkDeadShips(); let result = checks.checkDeadShips();
check.equals(result, [new ShipDeathDiff(battle, ship2)], "ship2 marked as dead"); check.equals(result, [new ShipDeathDiff(battle, ship2)], "ship2 marked as dead");
battle.applyDiffs(result); battle.applyDiffs(result);
ship1.setValue("hull", 0);
result = checks.checkDeadShips(); result = checks.checkDeadShips();
check.equals(result, [new ShipDeathDiff(battle, ship3)], "ship3 marked as dead"); check.equals(result, [new ShipDeathDiff(battle, ship1)], "ship1 marked as dead");
battle.applyDiffs(result); battle.applyDiffs(result);
result = checks.checkDeadShips(); result = checks.checkDeadShips();
check.equals(result, [], "ship1 left playing"); check.equals(result, [], "no ship left");
}) })
test.case("fixes area effects", check => { test.case("fixes area effects", check => {
@ -78,7 +81,6 @@ module TK.SpaceTac.Specs {
let ship2 = battle.fleets[1].addShip(); let ship2 = battle.fleets[1].addShip();
ship2.setArenaPosition(1000, 1000); ship2.setArenaPosition(1000, 1000);
TestTools.setShipModel(ship2, 10); TestTools.setShipModel(ship2, 10);
TestTools.setShipPlaying(battle, ship1);
let vig1 = ship1.actions.addCustom(new VigilanceAction("Vig1", { radius: 100, filter: ActionTargettingFilter.ENEMIES }, { intruder_effects: [new DamageEffect(1)] })); let vig1 = ship1.actions.addCustom(new VigilanceAction("Vig1", { radius: 100, filter: ActionTargettingFilter.ENEMIES }, { intruder_effects: [new DamageEffect(1)] }));
let vig2 = ship1.actions.addCustom(new VigilanceAction("Vig2", { radius: 50, filter: ActionTargettingFilter.ENEMIES }, { intruder_effects: [new DamageEffect(2)] })); let vig2 = ship1.actions.addCustom(new VigilanceAction("Vig2", { radius: 50, filter: ActionTargettingFilter.ENEMIES }, { intruder_effects: [new DamageEffect(2)] }));

View file

@ -113,11 +113,11 @@ module TK.SpaceTac {
} }
/** /**
* Check that not-playing ships with no more hull are dead * Check that ships with no more hull are dead
*/ */
checkDeadShips(): BaseBattleDiff[] { checkDeadShips(): BaseBattleDiff[] {
// We do one ship at a time, because the state of one ship may depend on another // We do one ship at a time, because the state of one ship may depend on another
let dying = ifirst(this.battle.iships(true), ship => !ship.playing && ship.getValue("hull") <= 0); let dying = ifirst(this.battle.iships(true), ship => ship.getValue("hull") <= 0);
if (dying) { if (dying) {
return dying.getDeathDiffs(this.battle); return dying.getDeathDiffs(this.battle);

View file

@ -2,7 +2,7 @@ module TK.SpaceTac {
testing("Drone", test => { testing("Drone", test => {
test.case("applies area effects when deployed", check => { test.case("applies area effects when deployed", check => {
let battle = TestTools.createBattle(); let battle = TestTools.createBattle();
let ship = nn(battle.playing_ship); let ship = battle.fleets[0].ships[0];
TestTools.setShipModel(ship, 100, 0, 10); TestTools.setShipModel(ship, 100, 0, 10);
let weapon = new DeployDroneAction("testdrone", { power: 2 }, { deploy_distance: 300, drone_radius: 30, drone_effects: [new AttributeEffect("evasion", 15)] }); let weapon = new DeployDroneAction("testdrone", { power: 2 }, { deploy_distance: 300, drone_radius: 30, drone_effects: [new AttributeEffect("evasion", 15)] });
ship.actions.addCustom(weapon); ship.actions.addCustom(weapon);

View file

@ -10,9 +10,9 @@ module TK.SpaceTac.Specs {
/** /**
* Apply deterministic game steps * Apply deterministic game steps
*/ */
function applyGameSteps(session: GameSession): void { function applyGameSteps(session: GameSession, random: RandomGenerator): void {
var battle = nn(session.getBattle()); var battle = nn(session.getBattle());
battle.advanceToNextShip(); battle.applyTurnPlan({ fleets: [] }, random);
// TODO Make some fixed moves (AI?) // TODO Make some fixed moves (AI?)
battle.endBattle(battle.fleets[0]); battle.endBattle(battle.fleets[0]);
} }
@ -29,8 +29,9 @@ module TK.SpaceTac.Specs {
compare(loaded_session, session); compare(loaded_session, session);
// Apply game steps // Apply game steps
applyGameSteps(session); const numbers = range(100).map(i => i / 101);
applyGameSteps(loaded_session); applyGameSteps(session, new SkewedRandomGenerator(numbers, true));
applyGameSteps(loaded_session, new SkewedRandomGenerator(numbers, true));
// Check equality after game steps // Check equality after game steps
compare(loaded_session, session); compare(loaded_session, session);

View file

@ -168,7 +168,7 @@ module TK.SpaceTac.Specs {
test.case("produces death diffs", check => { test.case("produces death diffs", check => {
let battle = TestTools.createBattle(1); let battle = TestTools.createBattle(1);
let ship = nn(battle.playing_ship); let ship = battle.fleets[0].ships[0];
check.equals(ship.getDeathDiffs(battle), [ check.equals(ship.getDeathDiffs(battle), [
new ShipValueDiff(ship, "hull", -1), new ShipValueDiff(ship, "hull", -1),

View file

@ -45,12 +45,6 @@ module TK.SpaceTac {
// Personality // Personality
personality = new Personality() personality = new Personality()
// Boolean set to true if the ship is currently playing its turn
playing = false
// Priority in current battle's play_order (used as sort key)
play_priority = 0
// Create a new ship inside a fleet // Create a new ship inside a fleet
constructor(fleet: Fleet | null = null, name: string | null = null, model = new ShipModel()) { constructor(fleet: Fleet | null = null, name: string | null = null, model = new ShipModel()) {
super(); super();
@ -112,8 +106,7 @@ module TK.SpaceTac {
// Make an initiative throw, to resolve play order in a battle // Make an initiative throw, to resolve play order in a battle
throwInitiative(gen: RandomGenerator): number { throwInitiative(gen: RandomGenerator): number {
this.play_priority = gen.random() * this.attributes.initiative.get(); return gen.random() * this.attributes.initiative.get();
return this.play_priority;
} }
/** /**

View file

@ -16,8 +16,6 @@ module TK.SpaceTac {
var battle = new Battle(fleet1, fleet2); var battle = new Battle(fleet1, fleet2);
battle.ships.list().forEach(ship => TestTools.setShipModel(ship, 1, 0)); battle.ships.list().forEach(ship => TestTools.setShipModel(ship, 1, 0));
battle.play_order = fleet1.ships.concat(fleet2.ships);
battle.setPlayingShip(battle.play_order[0]);
return battle; return battle;
} }
@ -45,15 +43,6 @@ module TK.SpaceTac {
return action; return action;
} }
/**
* Force the current playing ship
*/
static setShipPlaying(battle: Battle, ship: Ship): void {
add(battle.play_order, ship);
battle.play_index = battle.play_order.indexOf(ship);
ship.playing = true;
}
/** /**
* Set a ship attributes (by changing its model) * Set a ship attributes (by changing its model)
*/ */
@ -115,18 +104,17 @@ module TK.SpaceTac {
static actionChain(check: TestContext, battle: Battle, actions: [Ship, BaseAction, Target | undefined][], checks: ((check: TestContext) => void)[]): void { static actionChain(check: TestContext, battle: Battle, actions: [Ship, BaseAction, Target | undefined][], checks: ((check: TestContext) => void)[]): void {
checks[0](check.sub("initial state")); checks[0](check.sub("initial state"));
battle.applyDiffs([new TurnStartDiff([])]);
for (let i = 0; i < actions.length; i++) { for (let i = 0; i < actions.length; i++) {
let [ship, action, target] = actions[i]; let [ship, action, target] = actions[i];
battle.setPlayingShip(ship);
let result = battle.applyOneAction(action.id, target); let result = battle.applyOneAction(action.id, target);
check.equals(result, true, `action ${i + 1} successfully applied`); check.equals(result, true, `action ${i + 1} successfully applied`);
checks[i + 1](check.sub(`after action ${i + 1} applied`)); checks[i + 1](check.sub(`after action ${i + 1} applied`));
} }
for (let i = actions.length - 1; i >= 0; i--) { battle.revertOneTurn();
battle.revertOneAction(); checks[0](check.sub(`after turn reverted`));
checks[i](check.sub(`after action ${i + 1} reverted`));
}
} }
} }

View file

@ -2,19 +2,19 @@ module TK.SpaceTac.Specs {
testing("ActionList", test => { testing("ActionList", test => {
test.case("lists actions from ship", check => { test.case("lists actions from ship", check => {
let actions = new ActionList(); let actions = new ActionList();
check.equals(actions.listAll(), [EndTurnAction.SINGLETON]); check.equals(actions.listAll(), []);
let model = new ShipModel(); let model = new ShipModel();
let ship = new Ship(null, null, model); let ship = new Ship(null, null, model);
actions.updateFromShip(ship); actions.updateFromShip(ship);
check.equals(actions.listAll(), [EndTurnAction.SINGLETON]); check.equals(actions.listAll(), []);
let action1 = new BaseAction("test1"); let action1 = new BaseAction("test1");
let action2 = new BaseAction("test2"); let action2 = new BaseAction("test2");
let mock = check.patch(model, "getActions", () => [action1, action2]); let mock = check.patch(model, "getActions", () => [action1, action2]);
ship.level.forceLevel(3); ship.level.forceLevel(3);
actions.updateFromShip(ship); actions.updateFromShip(ship);
check.equals(actions.listAll(), [action1, action2, EndTurnAction.SINGLETON]); check.equals(actions.listAll(), [action1, action2]);
check.called(mock, [[3, []]]); check.called(mock, [[3, []]]);
let up1: ShipUpgrade = { code: "up1" }; let up1: ShipUpgrade = { code: "up1" };
@ -22,7 +22,7 @@ module TK.SpaceTac.Specs {
check.patch(model, "getLevelUpgrades", () => [up1, up2]); check.patch(model, "getLevelUpgrades", () => [up1, up2]);
ship.level.activateUpgrade(up1, true); ship.level.activateUpgrade(up1, true);
actions.updateFromShip(ship); actions.updateFromShip(ship);
check.equals(actions.listAll(), [action1, action2, EndTurnAction.SINGLETON]); check.equals(actions.listAll(), [action1, action2]);
check.called(mock, [[3, ["up1"]]]); check.called(mock, [[3, ["up1"]]]);
}) })

View file

@ -27,7 +27,7 @@ module TK.SpaceTac {
* List all actions * List all actions
*/ */
listAll(): BaseAction[] { listAll(): BaseAction[] {
return this.from_model.concat(this.custom).concat([EndTurnAction.SINGLETON]); return this.from_model.concat(this.custom);
} }
/** /**

View file

@ -2,7 +2,7 @@ module TK.SpaceTac.Specs {
testing("BaseAction", test => { testing("BaseAction", test => {
test.case("may be applied and reverted", check => { test.case("may be applied and reverted", check => {
let battle = TestTools.createBattle(); let battle = TestTools.createBattle();
let ship = nn(battle.playing_ship); let ship = battle.fleets[0].ships[0];
TestTools.setShipModel(ship, 100, 0, 10); TestTools.setShipModel(ship, 100, 0, 10);
let action = TestTools.addWeapon(ship, 0, 3, 100, 50); let action = TestTools.addWeapon(ship, 0, 3, 100, 50);
action.configureCooldown(2, 1); action.configureCooldown(2, 1);
@ -10,7 +10,6 @@ module TK.SpaceTac.Specs {
TestTools.actionChain(check, battle, [ TestTools.actionChain(check, battle, [
[ship, action, Target.newFromLocation(0, 0)], [ship, action, Target.newFromLocation(0, 0)],
[ship, action, Target.newFromLocation(0, 0)], [ship, action, Target.newFromLocation(0, 0)],
[ship, EndTurnAction.SINGLETON, undefined],
], [ ], [
check => { check => {
check.equals(ship.getValue("power"), 10, "power"); check.equals(ship.getValue("power"), 10, "power");
@ -30,12 +29,6 @@ module TK.SpaceTac.Specs {
check.equals(cooldown.uses, 2, "uses"); check.equals(cooldown.uses, 2, "uses");
check.equals(cooldown.heat, 1, "heat"); check.equals(cooldown.heat, 1, "heat");
}, },
check => {
check.equals(ship.getValue("power"), 10, "power");
let cooldown = ship.actions.getCooldown(action);
check.equals(cooldown.uses, 0, "uses");
check.equals(cooldown.heat, 0, "heat");
},
]); ]);
}) })

View file

@ -143,12 +143,6 @@ module TK.SpaceTac {
* Returns an unavalability reason, null otherwise * Returns an unavalability reason, null otherwise
*/ */
checkCannotBeApplied(ship: Ship, remaining_ap: number | null = null): ActionUnavailability | null { checkCannotBeApplied(ship: Ship, remaining_ap: number | null = null): ActionUnavailability | null {
let battle = ship.getBattle();
if (battle && battle.playing_ship !== ship) {
// Ship is not playing
return ActionUnavailability.NOT_PLAYING;
}
if (!ship.actions.getById(this.id)) { if (!ship.actions.getById(this.id)) {
return ActionUnavailability.NO_SUCH_ACTION; return ActionUnavailability.NO_SUCH_ACTION;
} }

View file

@ -28,7 +28,7 @@ module TK.SpaceTac.Specs {
test.case("deploys a new drone", check => { test.case("deploys a new drone", check => {
let battle = TestTools.createBattle(); let battle = TestTools.createBattle();
let ship = battle.play_order[0]; let ship = battle.fleets[0].ships[0];
ship.setArenaPosition(0, 0); ship.setArenaPosition(0, 0);
TestTools.setShipModel(ship, 100, 0, 3); TestTools.setShipModel(ship, 100, 0, 3);

View file

@ -1,199 +0,0 @@
module TK.SpaceTac.Specs {
testing("EndTurnAction", test => {
test.case("can't be applied to non-playing ship", check => {
let battle = new Battle();
battle.fleets[0].addShip();
battle.fleets[0].addShip();
battle.throwInitiative();
battle.setPlayingShip(battle.play_order[0]);
let action = new EndTurnAction();
check.equals(action.checkCannotBeApplied(battle.play_order[0]), ActionUnavailability.NO_SUCH_ACTION);
action = EndTurnAction.SINGLETON;
check.equals(action.checkCannotBeApplied(battle.play_order[0]), null);
check.equals(action.checkCannotBeApplied(battle.play_order[1]), ActionUnavailability.NOT_PLAYING);
});
test.case("changes active ship", check => {
let battle = TestTools.createBattle(2, 0);
TestTools.actionChain(check, battle, [
[battle.play_order[0], EndTurnAction.SINGLETON, undefined],
], [
check => {
check.equals(battle.play_index, 0, "play_index is 0");
check.same(battle.playing_ship, battle.play_order[0], "first ship is playing");
check.equals(battle.play_order[0].playing, true, "first ship is playing");
check.equals(battle.play_order[1].playing, false, "second ship is not playing");
},
check => {
check.equals(battle.play_index, 1, "play_index is 1");
check.same(battle.playing_ship, battle.play_order[1], "second ship is playing");
check.equals(battle.play_order[0].playing, false, "first ship is not playing");
check.equals(battle.play_order[1].playing, true, "second ship is playing");
}
]);
});
test.case("generates power for previous ship", check => {
let battle = TestTools.createBattle(1, 1);
let [ship1, ship2] = battle.play_order;
TestTools.setShipModel(ship1, 100, 0, 10);
let toggle = new ToggleAction("toggle", { power: 2 });
ship1.actions.addCustom(toggle);
ship1.setValue("power", 6);
TestTools.actionChain(check, battle, [
[ship1, toggle, undefined],
[ship1, toggle, undefined],
[ship1, EndTurnAction.SINGLETON, undefined],
[ship2, EndTurnAction.SINGLETON, undefined],
[ship1, toggle, undefined],
[ship1, EndTurnAction.SINGLETON, undefined],
[ship2, EndTurnAction.SINGLETON, undefined],
[ship1, EndTurnAction.SINGLETON, undefined],
[ship2, EndTurnAction.SINGLETON, undefined],
[ship1, toggle, undefined],
[ship1, EndTurnAction.SINGLETON, undefined],
], [
check => {
check.equals(ship1.getValue("power"), 6, "power value");
check.same(battle.playing_ship, ship1);
},
check => {
check.equals(ship1.getValue("power"), 4, "power value");
check.same(battle.playing_ship, ship1);
},
check => {
check.equals(ship1.getValue("power"), 6, "power value");
check.same(battle.playing_ship, ship1);
},
check => {
check.equals(ship1.getValue("power"), 10, "power value");
check.same(battle.playing_ship, ship2);
},
check => {
check.equals(ship1.getValue("power"), 10, "power value");
check.same(battle.playing_ship, ship1);
},
check => {
check.equals(ship1.getValue("power"), 8, "power value");
check.same(battle.playing_ship, ship1);
},
check => {
check.equals(ship1.getValue("power"), 8, "power value");
check.same(battle.playing_ship, ship2);
},
check => {
check.equals(ship1.getValue("power"), 8, "power value");
check.same(battle.playing_ship, ship1);
},
check => {
check.equals(ship1.getValue("power"), 8, "power value");
check.same(battle.playing_ship, ship2);
},
check => {
check.equals(ship1.getValue("power"), 8, "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);
},
]);
});
test.case("cools down equipment for previous ship", check => {
let battle = TestTools.createBattle(1, 0);
let ship = battle.play_order[0];
let equ1 = TestTools.addWeapon(ship);
equ1.configureCooldown(1, 3);
let cd1 = ship.actions.getCooldown(equ1);
cd1.use();
let equ2 = TestTools.addWeapon(ship);
equ2.configureCooldown(1, 2);
let cd2 = ship.actions.getCooldown(equ2);
cd2.use();
let equ3 = TestTools.addWeapon(ship);
let cd3 = ship.actions.getCooldown(equ3);
cd3.use();
TestTools.actionChain(check, battle, [
[ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)],
[ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)],
[ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)],
], [
check => {
check.equals(cd1.heat, 3, "equ1 heat");
check.equals(cd2.heat, 2, "equ2 heat");
check.equals(cd3.heat, 0, "equ3 heat");
},
check => {
check.equals(cd1.heat, 2, "equ1 heat");
check.equals(cd2.heat, 1, "equ2 heat");
check.equals(cd3.heat, 0, "equ3 heat");
},
check => {
check.equals(cd1.heat, 1, "equ1 heat");
check.equals(cd2.heat, 0, "equ2 heat");
check.equals(cd3.heat, 0, "equ3 heat");
},
check => {
check.equals(cd1.heat, 0, "equ1 heat");
check.equals(cd2.heat, 0, "equ2 heat");
check.equals(cd3.heat, 0, "equ3 heat");
}
]);
});
test.case("fades sticky effects for previous ship", check => {
let battle = TestTools.createBattle(1, 0);
let ship = battle.play_order[0];
let effect1 = new BaseEffect("e1");
let effect2 = new StickyEffect(new AttributeLimitEffect("evasion", 7), 2);
ship.active_effects.add(effect1);
ship.active_effects.add(effect2);
effect2.base.getOnDiffs(ship, ship).forEach(effect => effect.apply(battle));
check.patch(battle, "getAreaEffects", (): [Ship, BaseEffect][] => [[ship, effect1]]);
TestTools.actionChain(check, battle, [
[ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)],
[ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)],
[ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)],
], [
check => {
check.equals(ship.active_effects.count(), 2, "effect count");
check.contains(ship.active_effects.ids(), effect2.id, "sticky effect active");
check.equals((<StickyEffect>nn(ship.active_effects.get(effect2.id))).duration, 2, "duration sticky effect");
check.equals(ship.attributes.evasion.getMaximal(), 7, "max evasion");
},
check => {
check.equals(ship.active_effects.count(), 2, "effect count");
check.contains(ship.active_effects.ids(), effect2.id, "sticky effect active");
check.equals((<StickyEffect>nn(ship.active_effects.get(effect2.id))).duration, 1, "duration sticky effect");
check.equals(ship.attributes.evasion.getMaximal(), 7, "max evasion");
},
check => {
check.equals(ship.active_effects.count(), 1, "effect count");
check.notcontains(ship.active_effects.ids(), effect2.id, "sticky effect removed");
check.equals(ship.attributes.evasion.getMaximal(), Infinity, "max evasion");
},
check => {
check.equals(ship.active_effects.count(), 1, "effect count");
check.notcontains(ship.active_effects.ids(), effect2.id, "sticky effect removed");
check.equals(ship.attributes.evasion.getMaximal(), Infinity, "max evasion");
}
]);
});
});
}

View file

@ -1,74 +0,0 @@
/// <reference path="BaseAction.ts" />
module TK.SpaceTac {
/**
* Action to end the ship's turn
*
* This action is always available (through its singleton)
*/
export class EndTurnAction extends BaseAction {
// Singleton that may be used for all ships
static SINGLETON = new EndTurnAction();
constructor() {
super("End turn");
}
getVerb(ship: Ship): string {
return this.name;
}
getTitle(ship: Ship): string {
return this.name;
}
getPowerUsage(ship: Ship, target: Target | null): number {
let toggled_cost = sum(ship.getToggleActions(true).map(action => action.power));
return ship.getValue("power") + toggled_cost - ship.getAttribute("power_capacity");
}
getSpecificDiffs(ship: Ship, battle: Battle, target: Target): BaseBattleDiff[] {
if (ship.is(battle.playing_ship)) {
let result: BaseBattleDiff[] = [];
let new_ship = battle.getNextShip();
// Cool down actions
ship.actions.listAll().forEach(action => {
if (ship.actions.getCooldown(action).heat > 0) {
result.push(new ShipCooldownDiff(ship, action, 1));
}
})
// "On turn end" effects
iforeach(ship.active_effects.iterator(), effect => {
result = result.concat(effect.getTurnEndDiffs(ship));
});
// Change the active ship
let cycle_diff = (battle.play_order.indexOf(new_ship) == 0) ? 1 : 0;
result.push(new ShipChangeDiff(ship, new_ship, cycle_diff));
// "On turn start" effects
iforeach(new_ship.active_effects.iterator(), effect => {
result = result.concat(effect.getTurnStartDiffs(ship));
});
return result;
} else {
return [];
}
}
protected checkShipTarget(ship: Ship, target: Target): Target | null {
return ship.is(target.ship_id) ? target : null;
}
getTargettingMode(ship: Ship): ActionTargettingMode {
return ship.getValue("power") ? ActionTargettingMode.SELF_CONFIRM : ActionTargettingMode.SELF;
}
getEffectsDescription(): string {
return "End the current ship's turn.\nWill also generate power and cool down equipments.";
}
}
}

View file

@ -1,9 +1,8 @@
module TK.SpaceTac.Specs { module TK.SpaceTac.Specs {
testing("MoveAction", test => { testing("MoveAction", test => {
test.case("checks movement against remaining AP", check => { test.case("checks movement against remaining AP", check => {
var ship = new Ship(); const battle = new Battle();
var battle = new Battle(ship.fleet); const ship = battle.fleets[0].addShip();
TestTools.setShipPlaying(battle, ship);
ship.setValue("power", 6); ship.setValue("power", 6);
ship.arena_x = 0; ship.arena_x = 0;
ship.arena_y = 0; ship.arena_y = 0;
@ -36,7 +35,7 @@ module TK.SpaceTac.Specs {
test.case("applies and reverts", check => { test.case("applies and reverts", check => {
let battle = TestTools.createBattle(); let battle = TestTools.createBattle();
let ship = battle.play_order[0]; let ship = battle.fleets[0].ships[0];
ship.setArenaPosition(500, 600) ship.setArenaPosition(500, 600)
TestTools.setShipModel(ship, 100, 0, 20); TestTools.setShipModel(ship, 100, 0, 20);
ship.setValue("power", 5); ship.setValue("power", 5);

View file

@ -28,8 +28,6 @@ module TK.SpaceTac.Specs {
ship3.alive = false; ship3.alive = false;
let battle = new Battle(fleet); let battle = new Battle(fleet);
battle.play_order = [ship, ship1, ship2, ship3];
TestTools.setShipPlaying(battle, ship);
fleet.setBattle(battle); fleet.setBattle(battle);
action.apply(battle, ship, Target.newFromLocation(50, 50)); action.apply(battle, ship, Target.newFromLocation(50, 50));
@ -102,7 +100,7 @@ module TK.SpaceTac.Specs {
test.case("rotates toward the target", check => { test.case("rotates toward the target", check => {
let battle = TestTools.createBattle(); let battle = TestTools.createBattle();
let ship = battle.play_order[0]; let ship = battle.fleets[0].ships[0];
let action = TestTools.addWeapon(ship, 1, 0, 100, 30); let action = TestTools.addWeapon(ship, 1, 0, 100, 30);
check.patch(action, "checkTarget", (ship: Ship, target: Target) => target); check.patch(action, "checkTarget", (ship: Ship, target: Target) => target);
check.equals(ship.arena_angle, 0); check.equals(ship.arena_angle, 0);

View file

@ -84,7 +84,6 @@ module TK.SpaceTac.Specs {
check.equals(ship2a.getValue("hull"), 10); check.equals(ship2a.getValue("hull"), 10);
check.equals(ship2b.getValue("hull"), 10); check.equals(ship2b.getValue("hull"), 10);
TestTools.setShipPlaying(battle, ship2b);
battle.applyOneAction(engine.id, Target.newFromLocation(500, 0)); battle.applyOneAction(engine.id, Target.newFromLocation(500, 0));
check.equals(ship1a.active_effects.list(), []); check.equals(ship1a.active_effects.list(), []);

View file

@ -26,7 +26,7 @@ module TK.SpaceTac {
constructor(battle: Battle, debug = false) { constructor(battle: Battle, debug = false) {
this.battle = battle; this.battle = battle;
this.ship = nn(battle.playing_ship); this.ship = battle.ships.list()[0]; // FIXME
this.debug = debug; this.debug = debug;
} }

View file

@ -45,20 +45,11 @@ module TK.SpaceTac {
async play(): Promise<void> { async play(): Promise<void> {
this.started = (new Date()).getTime(); this.started = (new Date()).getTime();
if (!this.ship.playing) {
console.error(`${this.name} tries to play a ship out of turn`);
return;
}
// Work loop // Work loop
this.initWork(); this.initWork();
let last = new Date().getTime(); let last = new Date().getTime();
let ship = this.ship; let ship = this.ship;
while (this.doWorkUnit()) { while (this.doWorkUnit()) {
if (!this.ship.playing || this.ship != ship) {
console.error(`${this.name} switched to another ship in unit work`);
break;
}
if (this.getDuration() >= 10000) { if (this.getDuration() >= 10000) {
console.warn(`${this.name} takes too long to play, forcing turn end`); console.warn(`${this.name} takes too long to play, forcing turn end`);
break; break;
@ -70,11 +61,6 @@ module TK.SpaceTac {
last = t + 10; last = t + 10;
} }
} }
// End the ship turn
if (this.ship.playing) {
this.feedback(new Maneuver(this.ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)));
}
} }
/** /**

View file

@ -52,20 +52,6 @@ module TK.SpaceTac {
return any(this.simulation.parts, part => part.possible); return any(this.simulation.parts, part => part.possible);
} }
/**
* Returns true if the maneuver cannot be fully done this turn
*/
isIncomplete(): boolean {
return (this.simulation.need_move && !this.simulation.can_end_move) || (this.simulation.need_fire && !this.simulation.can_fire);
}
/**
* Returns true if another maneuver could be done next on the same ship
*/
mayContinue(): boolean {
return this.ship.playing && !this.isIncomplete() && !(this.action instanceof EndTurnAction);
}
/** /**
* Get the location of the ship after the action * Get the location of the ship after the action
*/ */
@ -88,16 +74,13 @@ module TK.SpaceTac {
* Standard feedback for this maneuver. It will apply it on the battle state. * Standard feedback for this maneuver. It will apply it on the battle state.
*/ */
apply(battle: Battle): boolean { apply(battle: Battle): boolean {
if (!this.ship.is(battle.playing_ship)) { if (!this.simulation.success) {
console.error("Maneuver was not produced for the playing ship", this, battle);
return false;
} else if (!this.simulation.success) {
return false; return false;
} else { } else {
let parts = this.simulation.parts; let parts = this.simulation.parts;
for (let i = 0; i < parts.length; i++) { for (let i = 0; i < parts.length; i++) {
let part = parts[i]; let part = parts[i];
if (part.action instanceof EndTurnAction || part.possible) { if (part.possible) {
if (!battle.applyOneAction(part.action.id, part.target)) { if (!battle.applyOneAction(part.action.id, part.target)) {
return false; return false;
} }
@ -105,7 +88,7 @@ module TK.SpaceTac {
return false; return false;
} }
} }
return this.mayContinue(); return false;
} }
} }
} }

View file

@ -22,8 +22,6 @@ module TK.SpaceTac.Specs {
test.case("applies the highest evaluated maneuver", check => { test.case("applies the highest evaluated maneuver", check => {
let battle = new Battle(); let battle = new Battle();
let ship = battle.fleets[0].addShip(); let ship = battle.fleets[0].addShip();
TestTools.setShipPlaying(battle, ship);
ship.playing = true;
let ai = new TacticalAI(ship, maneuver => { let ai = new TacticalAI(ship, maneuver => {
if (maneuver instanceof FixedManeuver) { if (maneuver instanceof FixedManeuver) {

View file

@ -68,10 +68,6 @@ module TK.SpaceTac {
console.log("AI maneuver", this.name, this.ship.name, best_maneuver, this.best_score); console.log("AI maneuver", this.name, this.ship.name, best_maneuver, this.best_score);
} }
if (this.best.action instanceof EndTurnAction) {
return false;
}
let success = this.feedback(best_maneuver); let success = this.feedback(best_maneuver);
if (success) { if (success) {
// Try to play another maneuver // Try to play another maneuver
@ -98,7 +94,6 @@ module TK.SpaceTac {
*/ */
getDefaultProducers() { getDefaultProducers() {
let producers = [ let producers = [
TacticalAIHelpers.produceEndTurn,
TacticalAIHelpers.produceDirectShots, TacticalAIHelpers.produceDirectShots,
TacticalAIHelpers.produceBlastShots, TacticalAIHelpers.produceBlastShots,
TacticalAIHelpers.produceToggleActions, TacticalAIHelpers.produceToggleActions,

View file

@ -8,7 +8,6 @@ module TK.SpaceTac.Specs {
let ship1b = battle.fleets[1].addShip(new Ship(null, "1B")); let ship1b = battle.fleets[1].addShip(new Ship(null, "1B"));
TestTools.setShipModel(ship0a, 100, 0, 10); TestTools.setShipModel(ship0a, 100, 0, 10);
TestTools.setShipPlaying(battle, ship0a);
let result = imaterialize(TacticalAIHelpers.produceDirectShots(ship0a, battle)); let result = imaterialize(TacticalAIHelpers.produceDirectShots(ship0a, battle));
check.equals(result.length, 0); check.equals(result.length, 0);
@ -30,7 +29,6 @@ module TK.SpaceTac.Specs {
let ship = battle.fleets[0].addShip(); let ship = battle.fleets[0].addShip();
TestTools.setShipModel(ship, 100, 0, 10); TestTools.setShipModel(ship, 100, 0, 10);
TestTools.setShipPlaying(battle, ship);
let result = imaterialize(TacticalAIHelpers.produceRandomMoves(ship, battle, 2, 1)); let result = imaterialize(TacticalAIHelpers.produceRandomMoves(ship, battle, 2, 1));
check.equals(result.length, 0); check.equals(result.length, 0);
@ -51,7 +49,6 @@ module TK.SpaceTac.Specs {
let ship = battle.fleets[0].addShip(); let ship = battle.fleets[0].addShip();
let weapon = TestTools.addWeapon(ship, 50, 1, 1000, 105); let weapon = TestTools.addWeapon(ship, 50, 1, 1000, 105);
TestTools.setShipModel(ship, 100, 0, 10, 1, [weapon]); TestTools.setShipModel(ship, 100, 0, 10, 1, [weapon]);
TestTools.setShipPlaying(battle, ship);
let result = imaterialize(TacticalAIHelpers.produceInterestingBlastShots(ship, battle)); let result = imaterialize(TacticalAIHelpers.produceInterestingBlastShots(ship, battle));
check.equals(result.length, 0); check.equals(result.length, 0);
@ -89,7 +86,6 @@ module TK.SpaceTac.Specs {
let action3 = new VigilanceAction("Vigilance", { radius: 150 }); let action3 = new VigilanceAction("Vigilance", { radius: 150 });
TestTools.setShipModel(ship, 100, 0, 10, 1, [action1, action2, action3]); TestTools.setShipModel(ship, 100, 0, 10, 1, [action1, action2, action3]);
TestTools.addEngine(ship, 1000); TestTools.addEngine(ship, 1000);
TestTools.setShipPlaying(battle, ship);
check.patch(TacticalAIHelpers, "scanArena", () => iarray([ check.patch(TacticalAIHelpers, "scanArena", () => iarray([
Target.newFromLocation(1, 0), Target.newFromLocation(1, 0),
@ -158,16 +154,10 @@ module TK.SpaceTac.Specs {
maneuver = new Maneuver(ship, engine, Target.newFromLocation(0, 48)); maneuver = new Maneuver(ship, engine, Target.newFromLocation(0, 48));
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), -0.9, "move only, at full power"); check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), -0.9, "move only, at full power");
maneuver = new Maneuver(ship, EndTurnAction.SINGLETON, Target.newFromShip(ship));
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), -1, "end turn, at full power");
ship.setValue("power", 2); ship.setValue("power", 2);
maneuver = new Maneuver(ship, engine, Target.newFromLocation(0, 48)); maneuver = new Maneuver(ship, engine, Target.newFromLocation(0, 48));
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), -0.1, "move only, at reduced power"); check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), -0.1, "move only, at reduced power");
maneuver = new Maneuver(ship, EndTurnAction.SINGLETON, Target.newFromShip(ship));
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), -0.2, "end turn, at reduced power");
}); });
test.case("evaluates damage to enemies", check => { test.case("evaluates damage to enemies", check => {
@ -276,7 +266,7 @@ module TK.SpaceTac.Specs {
let battle = TestTools.createBattle(); let battle = TestTools.createBattle();
let ship = battle.fleets[0].ships[0]; let ship = battle.fleets[0].ships[0];
let enemy = battle.fleets[1].ships[0]; let enemy = battle.fleets[1].ships[0];
TestTools.setShipModel(ship, 1, 0, 1); TestTools.setShipModel(ship, 5, 0, 1);
TestTools.setShipModel(enemy, 5, 5); TestTools.setShipModel(enemy, 5, 5);
let action = new TriggerAction("Test", { range: 100, power: 1 }); let action = new TriggerAction("Test", { range: 100, power: 1 });
ship.actions.addCustom(action); ship.actions.addCustom(action);

View file

@ -55,13 +55,6 @@ module TK.SpaceTac {
}); });
} }
/**
* Produce a turn end.
*/
static produceEndTurn(ship: Ship, battle: Battle): TacticalProducer {
return isingle(new Maneuver(ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)));
}
/** /**
* Produce all "direct hit" weapon shots. * Produce all "direct hit" weapon shots.
*/ */
@ -151,9 +144,7 @@ module TK.SpaceTac {
static evaluateIdling(ship: Ship, battle: Battle, maneuver: Maneuver): number { static evaluateIdling(ship: Ship, battle: Battle, maneuver: Maneuver): number {
let power_capacity = ship.getAttribute("power_capacity") || 1; let power_capacity = ship.getAttribute("power_capacity") || 1;
if (maneuver.action instanceof EndTurnAction) { if (maneuver.action instanceof TriggerAction) {
return -ship.getValue("power") / power_capacity;
} else if (maneuver.action instanceof TriggerAction) {
return 0.5; return 0.5;
} else if (maneuver.action instanceof ToggleAction) { } else if (maneuver.action instanceof ToggleAction) {
return ship.actions.isToggled(maneuver.action) ? -0.2 : 0.5; return ship.actions.isToggled(maneuver.action) ? -0.2 : 0.5;

View file

@ -2,8 +2,9 @@ module TK.SpaceTac.Specs {
testing("DroneDeployedDiff", test => { testing("DroneDeployedDiff", test => {
test.case("applies and reverts", check => { test.case("applies and reverts", check => {
let battle = TestTools.createBattle(); let battle = TestTools.createBattle();
let drone1 = new Drone(battle.play_order[0]); let ship = battle.fleets[0].ships[0];
let drone2 = new Drone(battle.play_order[0], "test"); let drone1 = new Drone(ship);
let drone2 = new Drone(ship, "test");
TestTools.diffChain(check, battle, [ TestTools.diffChain(check, battle, [
new DroneDeployedDiff(drone1), new DroneDeployedDiff(drone1),

View file

@ -1,36 +0,0 @@
module TK.SpaceTac.Specs {
testing("ShipChangeDiff", test => {
test.case("applies and reverts", check => {
let battle = new Battle();
let attacker1 = battle.fleets[0].addShip();
let attacker2 = battle.fleets[0].addShip();
let defender1 = battle.fleets[1].addShip();
battle.play_order = [defender1, attacker2, attacker1];
battle.play_index = 0;
battle.cycle = 1;
TestTools.diffChain(check, battle, [
new ShipChangeDiff(battle.play_order[0], battle.play_order[1]),
new ShipChangeDiff(battle.play_order[1], battle.play_order[2]),
new ShipChangeDiff(battle.play_order[2], battle.play_order[0], 1),
], [
check => {
check.same(battle.playing_ship, defender1, "first ship playing");
check.equals(battle.cycle, 1, "first cycle");
},
check => {
check.same(battle.playing_ship, attacker2, "second ship playing");
check.equals(battle.cycle, 1, "first cycle");
},
check => {
check.same(battle.playing_ship, attacker1, "third ship playing");
check.equals(battle.cycle, 1, "first cycle");
},
check => {
check.same(battle.playing_ship, defender1, "first ship playing again");
check.equals(battle.cycle, 2, "second cycle");
},
]);
});
});
}

View file

@ -1,39 +0,0 @@
/// <reference path="BaseBattleDiff.ts"/>
module TK.SpaceTac {
/**
* Current playing ship changes
*/
export class ShipChangeDiff extends BaseBattleShipDiff {
// ID of the new playing ship
new_ship: RObjectId
// Diff in the cycle count
cycle_diff: number
constructor(ship: Ship | RObjectId, new_ship: Ship | RObjectId, cycle_diff = 0) {
super(ship);
this.new_ship = (new_ship instanceof Ship) ? new_ship.id : new_ship;
this.cycle_diff = cycle_diff;
}
applyOnShip(ship: Ship, battle: Battle) {
if (ship.is(battle.playing_ship)) {
let new_ship = battle.getShip(this.new_ship);
if (new_ship) {
battle.setPlayingShip(new_ship);
battle.cycle += this.cycle_diff;
} else {
console.error("Cannot apply diff - new ship not found", this);
}
} else {
console.error("Cannot apply diff - ship is not playing", this);
}
}
getReverse(): BaseBattleDiff {
return new ShipChangeDiff(this.new_ship, this.ship_id, -this.cycle_diff);
}
}
}

View file

@ -2,7 +2,7 @@ module TK.SpaceTac.Specs {
testing("ShipCooldownDiff", test => { testing("ShipCooldownDiff", test => {
test.case("applies and reverts", check => { test.case("applies and reverts", check => {
let battle = TestTools.createBattle(); let battle = TestTools.createBattle();
let ship = battle.play_order[0]; let ship = battle.fleets[0].ships[0];
let weapon = TestTools.addWeapon(ship); let weapon = TestTools.addWeapon(ship);
weapon.configureCooldown(1, 3); weapon.configureCooldown(1, 3);
let cooldown = ship.actions.getCooldown(weapon); let cooldown = ship.actions.getCooldown(weapon);

View file

@ -2,7 +2,7 @@ module TK.SpaceTac.Specs {
testing("ShipDamageDiff", test => { testing("ShipDamageDiff", test => {
test.case("applies and reverts", check => { test.case("applies and reverts", check => {
let battle = TestTools.createBattle(); let battle = TestTools.createBattle();
let ship = battle.play_order[0]; let ship = battle.fleets[0].ships[0];
TestTools.setShipModel(ship, 80, 100); TestTools.setShipModel(ship, 80, 100);
TestTools.diffChain(check, battle, [ TestTools.diffChain(check, battle, [

View file

@ -5,7 +5,6 @@ module TK.SpaceTac.Specs {
let ship1 = battle.fleets[0].addShip(); let ship1 = battle.fleets[0].addShip();
let ship2 = battle.fleets[0].addShip(); let ship2 = battle.fleets[0].addShip();
let ship3 = battle.fleets[1].addShip(); let ship3 = battle.fleets[1].addShip();
battle.play_order = [ship3, ship2, ship1];
TestTools.diffChain(check, battle, [ TestTools.diffChain(check, battle, [
new ShipDeathDiff(battle, ship2) new ShipDeathDiff(battle, ship2)
@ -16,7 +15,6 @@ module TK.SpaceTac.Specs {
check.equals(imaterialize(battle.iships(true)), [ship1, ship2, ship3], "in alive ships"); check.equals(imaterialize(battle.iships(true)), [ship1, ship2, ship3], "in alive ships");
check.equals(battle.fleets[0].ships, [ship1, ship2], "fleet1"); check.equals(battle.fleets[0].ships, [ship1, ship2], "fleet1");
check.equals(battle.fleets[1].ships, [ship3], "fleet2"); check.equals(battle.fleets[1].ships, [ship3], "fleet2");
check.equals(battle.play_order, [ship3, ship2, ship1], "in play order");
}, },
check => { check => {
check.equals(ship2.alive, false, "dead"); check.equals(ship2.alive, false, "dead");
@ -24,7 +22,6 @@ module TK.SpaceTac.Specs {
check.equals(imaterialize(battle.iships(true)), [ship1, ship3], "not in alive ships anymore"); check.equals(imaterialize(battle.iships(true)), [ship1, ship3], "not in alive ships anymore");
check.equals(battle.fleets[0].ships, [ship1, ship2], "fleet1"); check.equals(battle.fleets[0].ships, [ship1, ship2], "fleet1");
check.equals(battle.fleets[1].ships, [ship3], "fleet2"); check.equals(battle.fleets[1].ships, [ship3], "fleet2");
check.equals(battle.play_order, [ship3, ship1], "removed from play order");
}, },
]); ]);
}); });

View file

@ -5,30 +5,19 @@ module TK.SpaceTac {
* A ship dies (or rather is put in emergency stasis mode) * A ship dies (or rather is put in emergency stasis mode)
* *
* This typically happens when the ship's hull reaches 0. * This typically happens when the ship's hull reaches 0.
* A dead ship cannot be interacted with, and will be removed from play order. * A dead ship cannot be interacted with.
*/ */
export class ShipDeathDiff extends BaseBattleShipDiff { export class ShipDeathDiff extends BaseBattleShipDiff {
// Index in the play order at which the ship was
play_index: number
constructor(battle: Battle, ship: Ship) { constructor(battle: Battle, ship: Ship) {
super(ship); super(ship);
this.play_index = battle.play_order.indexOf(ship);
} }
protected applyOnShip(ship: Ship, battle: Battle): void { protected applyOnShip(ship: Ship, battle: Battle): void {
ship.alive = false; ship.alive = false;
if (this.play_index >= 0) {
battle.removeFromPlayOrder(this.play_index);
}
} }
protected revertOnShip(ship: Ship, battle: Battle): void { protected revertOnShip(ship: Ship, battle: Battle): void {
ship.alive = true; ship.alive = true;
if (this.play_index >= 0) {
battle.insertInPlayOrder(this.play_index, ship);
}
} }
} }
} }

View file

@ -2,7 +2,7 @@ module TK.SpaceTac.Specs {
testing("ShipEffectAddedDiff", test => { testing("ShipEffectAddedDiff", test => {
test.case("applies and reverts", check => { test.case("applies and reverts", check => {
let battle = TestTools.createBattle(); let battle = TestTools.createBattle();
let ship = battle.play_order[0]; let ship = battle.fleets[0].ships[0];
let effect1 = new BaseEffect("e1"); let effect1 = new BaseEffect("e1");
let effect2 = new BaseEffect("e2"); let effect2 = new BaseEffect("e2");

View file

@ -2,7 +2,7 @@ module TK.SpaceTac.Specs {
testing("ShipEffectChangedDiff", test => { testing("ShipEffectChangedDiff", test => {
test.case("applies and reverts", check => { test.case("applies and reverts", check => {
let battle = TestTools.createBattle(); let battle = TestTools.createBattle();
let ship = battle.play_order[0]; let ship = battle.fleets[0].ships[0];
let effect1 = new BaseEffect("e1"); let effect1 = new BaseEffect("e1");
let effect2 = new StickyEffect(new BaseEffect("e2"), 2); let effect2 = new StickyEffect(new BaseEffect("e2"), 2);
@ -41,7 +41,7 @@ module TK.SpaceTac.Specs {
test.case("leaves original effect untouched", check => { test.case("leaves original effect untouched", check => {
let battle = TestTools.createBattle(); let battle = TestTools.createBattle();
let ship = battle.play_order[0]; let ship = battle.fleets[0].ships[0];
let effect = new StickyEffect(new BaseEffect("effect"), 2); let effect = new StickyEffect(new BaseEffect("effect"), 2);
let effect_at_removal = copy(effect); let effect_at_removal = copy(effect);

View file

@ -85,10 +85,6 @@ module TK.SpaceTac.UI.Specs {
view.splash = false; view.splash = false;
let battle = Battle.newQuickRandom(); let battle = Battle.newQuickRandom();
if (nn(battle.playing_ship).fleet != battle.fleets[0]) {
// Ensure the player plays first (to not trigger AI)
battle.fleets.push(nn(battle.fleets.shift()));
}
let player = battle.fleets[0].player; let player = battle.fleets[0].player;
return [view, { player, battle }]; return [view, { player, battle }];

View file

@ -10,24 +10,23 @@ module TK.SpaceTac.UI.Specs {
bar.setShip(ship); bar.setShip(ship);
check.equals(bar.action_icons.length, 0); check.equals(bar.action_icons.length, 0);
// Ship with no equipment (only endturn action) // Ship with no equipment
let player = new Player(); let player = new Player();
ship.fleet.setPlayer(player); ship.fleet.setPlayer(player);
testgame.view.player = player; testgame.view.player = player;
bar.setShip(ship); bar.setShip(ship);
check.equals(bar.action_icons.length, 1); check.equals(bar.action_icons.length, 0);
check.equals(bar.action_icons[0].action.code, "endturn");
// Add an engine, with move action // Add an engine, with move action
TestTools.addEngine(ship, 50); TestTools.addEngine(ship, 50);
bar.setShip(ship); bar.setShip(ship);
check.equals(bar.action_icons.length, 2); check.equals(bar.action_icons.length, 1);
check.equals(bar.action_icons[0].action.code, "move"); check.equals(bar.action_icons[0].action.code, "move");
// Add a weapon, with fire action // Add a weapon, with fire action
TestTools.addWeapon(ship, 10, 1, 100); TestTools.addWeapon(ship, 10, 1, 100);
bar.setShip(ship); bar.setShip(ship);
check.equals(bar.action_icons.length, 3); check.equals(bar.action_icons.length, 2);
check.equals(bar.action_icons[1].action.code, "weapon"); check.equals(bar.action_icons[1].action.code, "weapon");
}); });

View file

@ -88,24 +88,11 @@ module TK.SpaceTac.UI {
icons.forEach(icon => icon.refresh()); icons.forEach(icon => icon.refresh());
} }
} }
} else if (diff instanceof ShipChangeDiff) {
return {
background: async () => {
this.setShip(null);
}
}
} else { } else {
return {} return {}
} }
}); });
battleview.log_processor.watchForShipChange(ship => {
return {
background: async () => {
this.setShip(ship);
}
}
});
this.setInteractivity(false); this.setInteractivity(false);
} }

View file

@ -59,15 +59,13 @@ module TK.SpaceTac.UI {
this.img_action.setAlpha(0.2); this.img_action.setAlpha(0.2);
// Hotkey indicator // Hotkey indicator
if (!(action instanceof EndTurnAction)) { this.shortcut_container = builder.container("shortcut", 0, -47);
this.shortcut_container = builder.container("shortcut", 0, -47); builder.in(this.shortcut_container, builder => {
builder.in(this.shortcut_container, builder => { builder.image("battle-actionbar-hotkey", 0, 0, true);
builder.image("battle-actionbar-hotkey", 0, 0, true); builder.text(`${(position + 1) % 10}`, 0, -4, {
builder.text(`${(position + 1) % 10}`, 0, -4, { size: 12, color: "#d1d1d1", shadow: true, center: true, vcenter: true
size: 12, color: "#d1d1d1", shadow: true, center: true, vcenter: true
});
}); });
} });
// Bottom indicator // Bottom indicator
this.img_bottom = builder.image("battle-actionbar-bottom-disabled", 0, 40, true); this.img_bottom = builder.image("battle-actionbar-bottom-disabled", 0, 40, true);

View file

@ -9,7 +9,6 @@ module TK.SpaceTac.UI.Specs {
let action1 = ship.actions.addCustom(new MoveAction("Thruster")); let action1 = ship.actions.addCustom(new MoveAction("Thruster"));
let action2 = ship.actions.addCustom(new TriggerAction("Superweapon", { effects: [new DamageEffect(12)], power: 2, range: 50 })); let action2 = ship.actions.addCustom(new TriggerAction("Superweapon", { effects: [new DamageEffect(12)], power: 2, range: 50 }));
let action3 = ship.actions.addCustom(new EndTurnAction());
ActionTooltip.fill(tooltip.getBuilder(), ship, action1, 0); ActionTooltip.fill(tooltip.getBuilder(), ship, action1, 0);
checkText(check, tooltip.container.content.list[1], "Use Thruster"); checkText(check, tooltip.container.content.list[1], "Use Thruster");
@ -23,12 +22,6 @@ module TK.SpaceTac.UI.Specs {
checkText(check, tooltip.container.content.list[2], "Cost: 2 power"); checkText(check, tooltip.container.content.list[2], "Cost: 2 power");
checkText(check, tooltip.container.content.list[3], "Fire (power 2, range 50km):\n• do 12 damage on target"); checkText(check, tooltip.container.content.list[3], "Fire (power 2, range 50km):\n• do 12 damage on target");
checkText(check, tooltip.container.content.list[4], "[ 2 ]"); checkText(check, tooltip.container.content.list[4], "[ 2 ]");
tooltip.hide();
ActionTooltip.fill(tooltip.getBuilder(), ship, action3, 2);
checkText(check, tooltip.container.content.list[1], "End turn");
checkText(check, tooltip.container.content.list[2], "End the current ship's turn.\nWill also generate power and cool down equipments.");
checkText(check, tooltip.container.content.list[3], "[ space ]");
}); });
}); });
} }

View file

@ -51,9 +51,7 @@ module TK.SpaceTac.UI {
if (typeof position != "undefined") { if (typeof position != "undefined") {
let shortcut = ""; let shortcut = "";
if (action instanceof EndTurnAction) { if (position == 9) {
shortcut = "[ space ]";
} else if (position == 9) {
shortcut = "[ 0 ]"; shortcut = "[ 0 ]";
} else if (position >= 0 && position < 9) { } else if (position >= 0 && position < 9) {
shortcut = `[ ${position + 1} ]`; shortcut = `[ ${position + 1} ]`;

View file

@ -22,8 +22,8 @@ module TK.SpaceTac.UI {
// Currently hovered ship // Currently hovered ship
private hovered: ArenaShip | null private hovered: ArenaShip | null
// Currently playing ship // Currently selected ship
private playing: ArenaShip | null private selected: ArenaShip | null
// Layer for particles // Layer for particles
container: UIContainer container: UIContainer
@ -41,7 +41,7 @@ module TK.SpaceTac.UI {
// Create a graphical arena for ship sprites to fight in a 2D space // Create a graphical arena for ship sprites to fight in a 2D space
constructor(view: BattleView, container?: UIContainer) { constructor(view: BattleView, container?: UIContainer) {
this.view = view; this.view = view;
this.playing = null; this.selected = null;
this.hovered = null; this.hovered = null;
this.range_hint = new RangeHint(this); this.range_hint = new RangeHint(this);
@ -69,13 +69,6 @@ module TK.SpaceTac.UI {
view.log_processor.register(diff => this.checkDroneDeployed(diff)); view.log_processor.register(diff => this.checkDroneDeployed(diff));
view.log_processor.register(diff => this.checkDroneRecalled(diff)); view.log_processor.register(diff => this.checkDroneRecalled(diff));
view.log_processor.watchForShipChange(ship => {
return {
foreground: async () => {
await this.setShipPlaying(ship)
}
}
});
} }
/** /**
@ -201,22 +194,22 @@ module TK.SpaceTac.UI {
} }
/** /**
* Set the playing state on a ship sprite * Set the currently selected ship
*/ */
async setShipPlaying(ship: Ship | null, animate = true): Promise<void> { async setSelectedShip(ship: Ship | null, animate = true): Promise<void> {
if (this.playing) { if (this.selected) {
this.playing.setPlaying(false); this.selected.setSelected(false);
this.playing = null; this.selected = null;
} }
if (ship) { if (ship) {
let arena_ship = this.findShipSprite(ship); let arena_ship = this.findShipSprite(ship);
if (arena_ship) { if (arena_ship) {
this.layer_ships.bringToTop(arena_ship); this.layer_ships.bringToTop(arena_ship);
await arena_ship.setPlaying(true, animate); await arena_ship.setSelected(true, animate);
} }
this.playing = arena_ship; this.selected = arena_ship;
} }
} }

View file

@ -3,7 +3,7 @@ module TK.SpaceTac.UI.Specs {
let testgame = setupBattleview(test); let testgame = setupBattleview(test);
test.case("adds effects display", check => { test.case("adds effects display", check => {
let ship = nn(testgame.view.battle.playing_ship); let ship = testgame.view.battle.fleets[0].ships[0];
let sprite = nn(testgame.view.arena.findShipSprite(ship)); let sprite = nn(testgame.view.arena.findShipSprite(ship));
check.equals(sprite.effects_messages.list.length, 0); check.equals(sprite.effects_messages.list.length, 0);
@ -16,7 +16,7 @@ module TK.SpaceTac.UI.Specs {
test.case("adds sticky effects display", check => { test.case("adds sticky effects display", check => {
let battle = testgame.view.actual_battle; let battle = testgame.view.actual_battle;
let ship = nn(battle.playing_ship); let ship = testgame.view.battle.fleets[0].ships[0];
let sprite = nn(testgame.view.arena.findShipSprite(ship)); let sprite = nn(testgame.view.arena.findShipSprite(ship));
check.equals(sprite.active_effects_display.list.length, 0); check.equals(sprite.active_effects_display.list.length, 0);

View file

@ -27,14 +27,9 @@ module TK.SpaceTac.UI {
life_evasion: UIContainer life_evasion: UIContainer
toggle_hsp: Toggle toggle_hsp: Toggle
// Play order // Frames to indicate the owner, if the ship is selected, and if it is hovered
play_order_container: UIContainer
play_order: UIText
toggle_play_order: Toggle
// Frames to indicate the owner, if the ship is hovered, and if it is hovered
frame_owner: UIImage frame_owner: UIImage
frame_playing: UIImage frame_selected: UIImage
frame_hover: UIImage frame_hover: UIImage
// Effects display // Effects display
@ -59,8 +54,8 @@ module TK.SpaceTac.UI {
// Add frame indicating which side this ship is on // Add frame indicating which side this ship is on
this.frame_owner = builder.image(this.enemy ? "battle-hud-ship-enemy" : "battle-hud-ship-own", 0, 0, true); this.frame_owner = builder.image(this.enemy ? "battle-hud-ship-enemy" : "battle-hud-ship-own", 0, 0, true);
this.frame_playing = builder.image(this.enemy ? "battle-hud-ship-enemy-playing" : "battle-hud-ship-own-playing", 0, 0, true); this.frame_selected = builder.image(this.enemy ? "battle-hud-ship-enemy-selected" : "battle-hud-ship-own-selected", 0, 0, true);
this.setPlaying(false); this.setSelected(false);
this.frame_hover = builder.image("battle-hud-ship-hover", 0, 0, true); this.frame_hover = builder.image("battle-hud-ship-hover", 0, 0, true);
this.frame_hover.setVisible(false); this.frame_hover.setVisible(false);
@ -83,20 +78,11 @@ module TK.SpaceTac.UI {
this.life_evasion = builder.in(this.hsp).container("evasion"); this.life_evasion = builder.in(this.hsp).container("evasion");
this.toggle_hsp = this.battleview.animations.newVisibilityToggle(this.hsp, 200, false); this.toggle_hsp = this.battleview.animations.newVisibilityToggle(this.hsp, 200, false);
// Play order display
this.play_order_container = builder.container("play_order", -44, 0);
builder.in(this.play_order_container).image("battle-hud-ship-play-order", 0, 0, true);
this.play_order = builder.in(this.play_order_container).text("", -2, 1, {
size: 12, bold: true, color: "#d1d1d1", shadow: true, center: true
});
this.toggle_play_order = this.battleview.animations.newVisibilityToggle(this.play_order_container, 200, false);
// Effects display // Effects display
this.active_effects_display = builder.container("active-effects", 0, -44); this.active_effects_display = builder.container("active-effects", 0, -44);
this.effects_messages = builder.container("effects-messages"); this.effects_messages = builder.container("effects-messages");
this.effects_messages_toggle = this.battleview.animations.newVisibilityToggle(this.effects_messages, 500, false); this.effects_messages_toggle = this.battleview.animations.newVisibilityToggle(this.effects_messages, 500, false);
this.updatePlayOrder();
this.updateHull(this.ship.getValue("hull")); this.updateHull(this.ship.getValue("hull"));
this.updateShield(this.ship.getValue("shield")); this.updateShield(this.ship.getValue("shield"));
this.updateEvasion(this.ship.getAttribute("evasion")); this.updateEvasion(this.ship.getAttribute("evasion"));
@ -104,7 +90,7 @@ module TK.SpaceTac.UI {
this.updateEffectsRadius(); this.updateEffectsRadius();
// Set location // Set location
if (this.battleview.battle.cycle == 1 && this.battleview.battle.play_index == 0 && ship.alive && this.battleview.player.is(ship.fleet.player)) { if (this.battleview.battle.cycle == 1) {
this.setPosition(ship.arena_x - 500 * Math.cos(ship.arena_angle), ship.arena_y - 500 * Math.sin(ship.arena_angle)); this.setPosition(ship.arena_x - 500 * Math.cos(ship.arena_angle), ship.arena_y - 500 * Math.sin(ship.arena_angle));
this.moveToArenaLocation(ship.arena_x, ship.arena_y, ship.arena_angle, 1); this.moveToArenaLocation(ship.arena_x, ship.arena_y, ship.arena_angle, 1);
} else { } else {
@ -112,7 +98,6 @@ module TK.SpaceTac.UI {
} }
// Log processing // Log processing
this.battleview.log_processor.register(diff => this.processBattleDiff(diff));
this.battleview.log_processor.registerForShip(ship, diff => this.processShipDiff(diff)); this.battleview.log_processor.registerForShip(ship, diff => this.processShipDiff(diff));
} }
@ -120,16 +105,6 @@ module TK.SpaceTac.UI {
return `ArenaShip ${this.ship.jasmineToString()}`; return `ArenaShip ${this.ship.jasmineToString()}`;
} }
/**
* Process a battle diff
*/
private processBattleDiff(diff: BaseBattleDiff) {
if (diff instanceof ShipChangeDiff) {
this.updatePlayOrder();
}
return {};
}
/** /**
* Process a ship diff * Process a ship diff
*/ */
@ -222,16 +197,7 @@ module TK.SpaceTac.UI {
} else if (diff instanceof ShipActionUsedDiff) { } else if (diff instanceof ShipActionUsedDiff) {
let action = this.ship.actions.getById(diff.action); let action = this.ship.actions.getById(diff.action);
if (action) { if (action) {
if (action instanceof EndTurnAction) { if (!(action instanceof ToggleAction)) {
return {
foreground: async (speed: number) => {
if (speed) {
await this.displayEffect("End turn", true, speed);
await timer.sleep(500 / speed);
}
}
}
} else if (!(action instanceof ToggleAction)) {
let action_name = action.name; let action_name = action.name;
return { return {
foreground: async (speed: number) => { foreground: async (speed: number) => {
@ -283,32 +249,22 @@ module TK.SpaceTac.UI {
*/ */
setHovered(hovered: boolean, tactical: boolean) { setHovered(hovered: boolean, tactical: boolean) {
let client = tactical ? "tactical" : "hover"; let client = tactical ? "tactical" : "hover";
this.toggle_hsp.manipulate(client)(hovered && this.ship.alive);
if (hovered && this.ship.alive) {
this.toggle_hsp.manipulate(client)(true);
if (tactical) {
this.toggle_play_order.manipulate(client)(true);
}
} else {
this.toggle_hsp.manipulate(client)(false);
this.toggle_play_order.manipulate(client)(false);
}
this.battleview.animations.setVisible(this.frame_hover, hovered && this.ship.alive && !tactical, 200); this.battleview.animations.setVisible(this.frame_hover, hovered && this.ship.alive && !tactical, 200);
} }
/** /**
* Set the playing state on this ship * Set the selected state on this ship
* *
* This will alter the HUD frame to show this state * This will alter the HUD frame to show this state
*/ */
async setPlaying(playing: boolean, animate = true): Promise<void> { async setSelected(selected: boolean, animate = true): Promise<void> {
this.frame_owner.setVisible(this.ship.alive); this.frame_owner.setVisible(this.ship.alive);
this.frame_playing.setVisible(this.ship.alive && playing); this.frame_selected.setVisible(this.ship.alive && selected);
if (playing && animate) { if (selected && animate) {
this.battleview.audio.playOnce("battle-ship-change"); this.battleview.audio.playOnce("battle-ship-change");
await this.battleview.animations.blink(this.frame_playing); await this.battleview.animations.blink(this.frame_selected);
} }
} }
@ -324,7 +280,7 @@ module TK.SpaceTac.UI {
} else { } else {
this.stasis.visible = false; this.stasis.visible = false;
} }
this.setPlaying(false); this.setSelected(false);
} }
/** /**
@ -384,18 +340,6 @@ module TK.SpaceTac.UI {
} }
} }
/**
* Update the play order indicator
*/
updatePlayOrder(): void {
let play_order = this.battleview.battle.getPlayOrder(this.ship);
if (play_order == 0) {
this.play_order.setText("-");
} else {
this.play_order.setText(play_order.toString());
}
}
/** /**
* Reposition the HSP indicators * Reposition the HSP indicators
*/ */

View file

@ -6,11 +6,11 @@ module TK.SpaceTac.UI.Specs {
let battleview = testgame.view; let battleview = testgame.view;
check.equals(battleview.ship_hovered, null, "initial state"); check.equals(battleview.ship_hovered, null, "initial state");
let ship = nn(battleview.battle.playing_ship); let ship = battleview.battle.fleets[0].ships[0];
battleview.cursorHovered(ship.location, ship); battleview.cursorHovered(ship.location, ship);
check.same(battleview.ship_hovered, ship, "ship1 hovered"); check.same(battleview.ship_hovered, ship, "ship1 hovered");
ship = nn(battleview.battle.play_order[1]); ship = battleview.battle.fleets[1].ships[0];
battleview.cursorHovered(ship.location, ship); battleview.cursorHovered(ship.location, ship);
check.same(battleview.ship_hovered, ship, "ship2 hovered"); check.same(battleview.ship_hovered, ship, "ship2 hovered");
@ -20,7 +20,7 @@ module TK.SpaceTac.UI.Specs {
battleview.cursorOnShip(ship); battleview.cursorOnShip(ship);
check.same(battleview.ship_hovered, ship, "force on"); check.same(battleview.ship_hovered, ship, "force on");
battleview.cursorOffShip(battleview.battle.play_order[2]); battleview.cursorOffShip(battleview.battle.fleets[1].ships[1]);
check.same(battleview.ship_hovered, ship, "force off on wrong ship"); check.same(battleview.ship_hovered, ship, "force off on wrong ship");
battleview.cursorOffShip(ship); battleview.cursorOffShip(ship);
@ -32,7 +32,7 @@ module TK.SpaceTac.UI.Specs {
check.equals(battleview.targetting.active, false); check.equals(battleview.targetting.active, false);
battleview.setInteractionEnabled(true); battleview.setInteractionEnabled(true);
let ship = nn(battleview.battle.playing_ship); let ship = battleview.battle.fleets[0].ships[0];
let weapon = TestTools.addWeapon(ship, 10); let weapon = TestTools.addWeapon(ship, 10);
battleview.enterTargettingMode(ship, weapon, ActionTargettingMode.SPACE); battleview.enterTargettingMode(ship, weapon, ActionTargettingMode.SPACE);
check.equals(battleview.targetting.active, true); check.equals(battleview.targetting.active, true);
@ -41,7 +41,7 @@ module TK.SpaceTac.UI.Specs {
check.equals(battleview.targetting.target, Target.newFromLocation(5, 8)); check.equals(battleview.targetting.target, Target.newFromLocation(5, 8));
check.equals(battleview.ship_hovered, null); check.equals(battleview.ship_hovered, null);
ship = battleview.battle.play_order[3]; ship = battleview.battle.fleets[1].ships[0];
battleview.cursorHovered(ship.location, ship); battleview.cursorHovered(ship.location, ship);
check.equals(battleview.targetting.target, Target.newFromLocation(ship.arena_x, ship.arena_y)); check.equals(battleview.targetting.target, Target.newFromLocation(ship.arena_x, ship.arena_y));
check.equals(battleview.ship_hovered, null); check.equals(battleview.ship_hovered, null);
@ -67,6 +67,7 @@ module TK.SpaceTac.UI.Specs {
test.case("allows to choose an action and a target with shortcut keys", check => { test.case("allows to choose an action and a target with shortcut keys", check => {
let battleview = testgame.view; let battleview = testgame.view;
battleview.setInteractionEnabled(true); battleview.setInteractionEnabled(true);
battleview.setShipSelected(battleview.battle.fleets[0].ships[0]);
let action_icon = battleview.action_bar.action_icons[0]; let action_icon = battleview.action_bar.action_icons[0];
check.equals(battleview.targetting.active, false); check.equals(battleview.targetting.active, false);
@ -79,21 +80,23 @@ module TK.SpaceTac.UI.Specs {
battleview.numberPressed(3); battleview.numberPressed(3);
check.equals(battleview.targetting.active, true); check.equals(battleview.targetting.active, true);
check.same(battleview.targetting.action, action_icon.action); check.same(battleview.targetting.action, action_icon.action);
check.equals(battleview.targetting.target, Target.newFromShip(battleview.battle.play_order[3])); check.equals(battleview.targetting.target, Target.newFromShip(battleview.battle.ships.list()[2]));
battleview.numberPressed(4); battleview.numberPressed(4);
check.equals(battleview.targetting.active, true); check.equals(battleview.targetting.active, true);
check.same(battleview.targetting.action, action_icon.action); check.same(battleview.targetting.action, action_icon.action);
check.equals(battleview.targetting.target, Target.newFromShip(battleview.battle.play_order[4])); check.equals(battleview.targetting.target, Target.newFromShip(battleview.battle.ships.list()[3]));
}); });
test.case("adds player actions to plan", check => { test.case("adds player actions to plan", check => {
let battleview = testgame.view; let battleview = testgame.view;
let playing_ship = nn(battleview.battle.playing_ship);
check.equals(battleview.turn_plannings.length, 2); check.equals(battleview.turn_plannings.length, 2);
check.equals(battleview.turn_plannings[0].collectAllActions(), []); check.equals(battleview.turn_plannings[0].collectAllActions(), []);
check.equals(battleview.turn_plannings[1].collectAllActions(), []); check.equals(battleview.turn_plannings[1].collectAllActions(), []);
let action = nn(first(playing_ship.actions.listAll(), action => action instanceof MoveAction)); let ship = battleview.battle.fleets[0].ships[0];
let action = nn(first(ship.actions.listAll(), action => action instanceof MoveAction));
battleview.setShipSelected(ship);
battleview.applyPlayerAction(action, Target.newFromLocation(0, 0)); battleview.applyPlayerAction(action, Target.newFromLocation(0, 0));
check.equals(battleview.turn_plannings.length, 2); check.equals(battleview.turn_plannings.length, 2);
check.equals(battleview.turn_plannings[0].collectAllActions(), [ check.equals(battleview.turn_plannings[0].collectAllActions(), [

View file

@ -52,7 +52,10 @@ module TK.SpaceTac.UI {
action_bar!: ActionBar action_bar!: ActionBar
// Currently hovered ship // Currently hovered ship
ship_hovered!: Ship | null ship_hovered: Ship | null = null
// Currently selected ship
ship_selected: Ship | null = null
// Ship tooltip // Ship tooltip
ship_tooltip!: ShipTooltip ship_tooltip!: ShipTooltip
@ -81,6 +84,7 @@ module TK.SpaceTac.UI {
this.battle = duplicate(data.battle, <any>TK.SpaceTac); this.battle = duplicate(data.battle, <any>TK.SpaceTac);
this.turn_plannings = this.battle.fleets.map(fleet => new TurnPlanning(this.battle, fleet.player)); this.turn_plannings = this.battle.fleets.map(fleet => new TurnPlanning(this.battle, fleet.player));
this.ship_hovered = null; this.ship_hovered = null;
this.ship_selected = null;
this.background = null; this.background = null;
this.multi = new MultiBattle(); this.multi = new MultiBattle();
@ -198,14 +202,19 @@ module TK.SpaceTac.UI {
return false; return false;
} }
let done = false;
this.turn_plannings.forEach(planning => { this.turn_plannings.forEach(planning => {
if (this.action_bar.ship && this.player.is(planning.player)) { const ship = this.ship_selected;
planning.addAction(this.action_bar.ship, action, target); if (ship && this.player.is(planning.player) && this.player.is(ship.getPlayer())) {
planning.addAction(ship, action, target);
done = true;
} }
}); });
this.planningsChanged();
return false; if (done) {
this.planningsChanged();
}
return done;
} }
/** /**
@ -252,7 +261,7 @@ module TK.SpaceTac.UI {
numberPressed(num: number): void { numberPressed(num: number): void {
if (this.interacting) { if (this.interacting) {
if (this.targetting.active) { if (this.targetting.active) {
let ship = ifirst(this.battle.iships(true), ship => this.battle.getPlayOrder(ship) == num % 10); const ship = this.battle.ships.list()[num - 1];
if (ship) { if (ship) {
this.targetting.setTarget(Target.newFromShip(ship)); this.targetting.setTarget(Target.newFromShip(ship));
} }
@ -312,9 +321,8 @@ module TK.SpaceTac.UI {
cursorClicked(): void { cursorClicked(): void {
if (this.targetting.active) { if (this.targetting.active) {
this.validationPressed(); this.validationPressed();
} else if (this.ship_hovered && this.player.is(this.ship_hovered.fleet.player) && this.interacting) { } else if (this.ship_hovered && this.player.is(this.ship_hovered.getPlayer()) && this.interacting) {
this.character_sheet.show(this.ship_hovered); this.setShipSelected(this.ship_hovered);
this.setShipHovered(null);
} else { } else {
this.log_processor.fastForward(); this.log_processor.fastForward();
} }
@ -334,6 +342,21 @@ module TK.SpaceTac.UI {
} }
} }
/**
* Set the currently selected ship
*/
setShipSelected(ship: Ship | null): void {
if (ship && !this.player.is(ship.getPlayer())) {
ship = null;
}
this.setShipHovered(null);
this.ship_selected = ship;
this.arena.setSelectedShip(ship);
this.action_bar.setShip(ship);
}
// Enable or disable the global player interaction // Enable or disable the global player interaction
// Disable interaction when it is the AI turn, or when the current ship can't play // Disable interaction when it is the AI turn, or when the current ship can't play
setInteractionEnabled(enabled: boolean): void { setInteractionEnabled(enabled: boolean): void {

View file

@ -105,18 +105,6 @@ module TK.SpaceTac.UI {
} }
} }
/**
* Check if we need a player or AI to interact at this point
*/
getPlayerNeeded(): Player | null {
if (this.log.isPlaying() && this.log.atEnd()) {
let playing_ship = this.view.actual_battle.playing_ship;
return playing_ship ? playing_ship.getPlayer() : null;
} else {
return null;
}
}
/** /**
* Register a diff subscriber * Register a diff subscriber
*/ */
@ -137,54 +125,6 @@ module TK.SpaceTac.UI {
}); });
} }
/**
* 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) => LogProcessorDelegate, initial = true, immediate = false) {
this.register(diff => {
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.actions.getById(diff.action) instanceof EndTurnAction) {
changed = true;
}
}
if (changed) {
let ship = this.view.battle.playing_ship;
if (ship) {
return callback(ship);
} else {
return {};
}
} else {
return {};
}
});
if (initial) {
let ship = this.view.battle.playing_ship;
if (ship) {
let result = callback(ship);
if (result.foreground) {
let promise = result.foreground(0);
if (result.background) {
let next = result.background;
promise.then(() => next(0));
}
} else if (result.background) {
result.background(0);
}
}
}
}
/** /**
* Process a single battle diff * Process a single battle diff
*/ */
@ -217,18 +157,8 @@ module TK.SpaceTac.UI {
* Transfer control to the needed player (or not) * Transfer control to the needed player (or not)
*/ */
private transferControl() { private transferControl() {
let player = this.getPlayerNeeded(); // TODO Should be done by the battleview itself
if (player) { this.view.setInteractionEnabled(this.view.battle.canPlay(this.view.player));
if (player.is(this.view.player)) {
this.view.setInteractionEnabled(true);
} else if (!this.ai_disabled) {
this.view.playAI();
} else {
this.view.applyPlayerAction(EndTurnAction.SINGLETON);
}
} else {
this.view.setInteractionEnabled(false);
}
} }
/** /**
@ -236,7 +166,8 @@ module TK.SpaceTac.UI {
*/ */
private checkReaction(diff: BaseBattleDiff): LogProcessorDelegate { private checkReaction(diff: BaseBattleDiff): LogProcessorDelegate {
if (this.log.isPlaying()) { if (this.log.isPlaying()) {
let reaction = this.view.session.reactions.check(this.view.player, this.view.battle, this.view.battle.playing_ship, diff); let playing_ship = null; // FIXME
let reaction = this.view.session.reactions.check(this.view.player, this.view.battle, playing_ship, diff);
if (reaction) { if (reaction) {
return { return {
foreground: async () => { foreground: async () => {

View file

@ -4,7 +4,7 @@ module TK.SpaceTac.UI.Specs {
test.case("fills ship details", check => { test.case("fills ship details", check => {
let tooltip = new ShipTooltip(testgame.view); let tooltip = new ShipTooltip(testgame.view);
let ship = testgame.view.battle.play_order[2]; let ship = testgame.view.battle.fleets[0].ships[0];
TestTools.setShipModel(ship, 58, 140, 12); TestTools.setShipModel(ship, 58, 140, 12);
ship.name = "Fury"; ship.name = "Fury";
ship.model = new ShipModel("fake", "Fury"); ship.model = new ShipModel("fake", "Fury");
@ -25,7 +25,7 @@ module TK.SpaceTac.UI.Specs {
check.contains(images, "battle-hud-ship-effect-good"); check.contains(images, "battle-hud-ship-effect-good");
check.contains(images, "battle-hud-ship-effect-bad"); check.contains(images, "battle-hud-ship-effect-bad");
check.equals(texts, [ check.equals(texts, [
"Level 1 Fury", "Plays in 2 turns", "Level 1 Fury",
"57", "max", "58", "100", "max", "140", "7", "9", "max", "12", "57", "max", "58", "100", "max", "140", "7", "9", "max", "12",
"Weapon", "hull capacity +50", "limit shield capacity to 2 for 3 turns", "Weapon", "hull capacity +50", "limit shield capacity to 2 for 3 turns",
"Super ship model !" "Super ship model !"

View file

@ -42,11 +42,6 @@ module TK.SpaceTac.UI {
builder.text(ship.getName(), 230, 0, { color: enemy ? "#c06858" : "#65a898", size: 22, bold: true }); builder.text(ship.getName(), 230, 0, { color: enemy ? "#c06858" : "#65a898", size: 22, bold: true });
if (ship.alive) { if (ship.alive) {
if (battle) {
let turns = battle.getPlayOrder(ship);
builder.text((turns == 0) ? "Playing" : ((turns == 1) ? "Plays next" : `Plays in ${turns} turns`), 230, 36, { color: "#cccccc", size: 18 });
}
ShipTooltip.addValue(builder, 0, "#eb4e4a", "attribute-hull_capacity", ship.getValue("hull"), ship.getAttribute("hull_capacity")); ShipTooltip.addValue(builder, 0, "#eb4e4a", "attribute-hull_capacity", ship.getValue("hull"), ship.getAttribute("hull_capacity"));
ShipTooltip.addValue(builder, 1, "#2ad8dc", "attribute-shield_capacity", ship.getValue("shield"), ship.getAttribute("shield_capacity")); ShipTooltip.addValue(builder, 1, "#2ad8dc", "attribute-shield_capacity", ship.getValue("shield"), ship.getAttribute("shield_capacity"));
ShipTooltip.addValue(builder, 2, "#c1f06b", "attribute-evasion", ship.getAttribute("evasion")); ShipTooltip.addValue(builder, 2, "#c1f06b", "attribute-evasion", ship.getAttribute("evasion"));
@ -55,7 +50,7 @@ module TK.SpaceTac.UI {
let iy = 210; let iy = 210;
ship.actions.listAll().forEach(action => { ship.actions.listAll().forEach(action => {
if (!(action instanceof EndTurnAction) && !(action instanceof MoveAction)) { if (!(action instanceof MoveAction)) {
let icon = builder.image(`action-${action.code}`, 0, iy); let icon = builder.image(`action-${action.code}`, 0, iy);
icon.setScale(0.15); icon.setScale(0.15);
builder.text(action.name, 46, iy + 8); builder.text(action.name, 46, iy + 8);

View file

@ -12,7 +12,7 @@ module TK.SpaceTac.UI.Specs {
test.case("draws simulation parts", check => { test.case("draws simulation parts", check => {
let targetting = newTargetting(); let targetting = newTargetting();
let ship = nn(testgame.view.battle.playing_ship); let ship = testgame.view.battle.fleets[0].ships[0];
ship.setArenaPosition(10, 20); ship.setArenaPosition(10, 20);
let weapon = TestTools.addWeapon(ship); let weapon = TestTools.addWeapon(ship);
let engine = TestTools.addEngine(ship, 12); let engine = TestTools.addEngine(ship, 12);
@ -46,7 +46,7 @@ module TK.SpaceTac.UI.Specs {
test.case("updates impact indicators on ships inside the blast radius", check => { test.case("updates impact indicators on ships inside the blast radius", check => {
let targetting = newTargetting(); let targetting = newTargetting();
let ship = nn(testgame.view.battle.playing_ship); let ship = testgame.view.battle.fleets[0].ships[0];
let impacts = targetting.impact_indicators; let impacts = targetting.impact_indicators;
let action = new TriggerAction("weapon", { range: 50 }); let action = new TriggerAction("weapon", { range: 50 });
@ -81,7 +81,7 @@ module TK.SpaceTac.UI.Specs {
test.case("updates graphics from simulation", check => { test.case("updates graphics from simulation", check => {
let targetting = newTargetting(); let targetting = newTargetting();
let ship = nn(testgame.view.battle.playing_ship); let ship = testgame.view.battle.fleets[0].ships[0];
let engine = TestTools.addEngine(ship, 8000); let engine = TestTools.addEngine(ship, 8000);
let weapon = TestTools.addWeapon(ship, 30, 5, 100, 50); let weapon = TestTools.addWeapon(ship, 30, 5, 100, 50);
@ -123,40 +123,40 @@ module TK.SpaceTac.UI.Specs {
test.case("snaps on ships according to targetting mode", check => { test.case("snaps on ships according to targetting mode", check => {
let targetting = newTargetting(); let targetting = newTargetting();
let playing_ship = nn(testgame.view.battle.playing_ship); let ship = testgame.view.battle.fleets[0].ships[0];
let action = TestTools.addWeapon(playing_ship); let action = TestTools.addWeapon(ship);
let ship1 = testgame.view.battle.play_order[1]; let ship1 = testgame.view.battle.fleets[1].ships[0];
let ship2 = testgame.view.battle.play_order[2]; let ship2 = testgame.view.battle.fleets[1].ships[1];
ship1.setArenaPosition(8000, 50); ship1.setArenaPosition(8000, 50);
ship2.setArenaPosition(8000, 230); ship2.setArenaPosition(8000, 230);
targetting.setAction(playing_ship, action, ActionTargettingMode.SPACE); targetting.setAction(ship, action, ActionTargettingMode.SPACE);
targetting.setTargetFromLocation({ x: 8000, y: 60 }); targetting.setTargetFromLocation({ x: 8000, y: 60 });
check.equals(targetting.target, Target.newFromLocation(8000, 60), "space"); check.equals(targetting.target, Target.newFromLocation(8000, 60), "space");
targetting.setAction(playing_ship, action, ActionTargettingMode.SHIP); targetting.setAction(ship, action, ActionTargettingMode.SHIP);
targetting.setTargetFromLocation({ x: 8000, y: 60 }); targetting.setTargetFromLocation({ x: 8000, y: 60 });
check.equals(targetting.target, Target.newFromShip(ship1), "ship 1"); check.equals(targetting.target, Target.newFromShip(ship1), "ship 1");
targetting.setTargetFromLocation({ x: 8100, y: 200 }); targetting.setTargetFromLocation({ x: 8100, y: 200 });
check.equals(targetting.target, Target.newFromShip(ship2), "ship 2"); check.equals(targetting.target, Target.newFromShip(ship2), "ship 2");
targetting.setAction(playing_ship, action, ActionTargettingMode.SURROUNDINGS); targetting.setAction(ship, action, ActionTargettingMode.SURROUNDINGS);
targetting.setTargetFromLocation({ x: 8000, y: 60 }); targetting.setTargetFromLocation({ x: 8000, y: 60 });
check.equals(targetting.target, new Target(8000, 60, playing_ship), "surroundings 1"); check.equals(targetting.target, new Target(8000, 60, ship), "surroundings 1");
targetting.setTargetFromLocation({ x: playing_ship.arena_x + 10, y: playing_ship.arena_y - 20 }); targetting.setTargetFromLocation({ x: ship.arena_x + 10, y: ship.arena_y - 20 });
check.equals(targetting.target, Target.newFromShip(playing_ship), "surroundings 2"); check.equals(targetting.target, Target.newFromShip(ship), "surroundings 2");
targetting.setAction(playing_ship, action, ActionTargettingMode.SELF); targetting.setAction(ship, action, ActionTargettingMode.SELF);
targetting.setTargetFromLocation({ x: 8000, y: 60 }); targetting.setTargetFromLocation({ x: 8000, y: 60 });
check.equals(targetting.target, Target.newFromShip(playing_ship), "self 1"); check.equals(targetting.target, Target.newFromShip(ship), "self 1");
targetting.setTargetFromLocation({ x: 0, y: 0 }); targetting.setTargetFromLocation({ x: 0, y: 0 });
check.equals(targetting.target, Target.newFromShip(playing_ship), "self 2"); check.equals(targetting.target, Target.newFromShip(ship), "self 2");
}) })
test.case("updates the range hint display", check => { test.case("updates the range hint display", check => {
let targetting = newTargetting(); let targetting = newTargetting();
let ship = nn(testgame.view.battle.playing_ship); let ship = testgame.view.battle.fleets[0].ships[0];
ship.setArenaPosition(0, 0); ship.setArenaPosition(0, 0);
TestTools.setShipModel(ship, 100, 0, 8); TestTools.setShipModel(ship, 100, 0, 8);
let move = TestTools.addEngine(ship, 100); let move = TestTools.addEngine(ship, 100);

View file

@ -31,7 +31,7 @@ module TK.SpaceTac.UI.Specs {
let battleview = testgame.view; let battleview = testgame.view;
battleview.timer = new Timer(); battleview.timer = new Timer();
let ship = nn(battleview.battle.playing_ship); let ship = battleview.battle.fleets[0].ships[0];
let effect = new WeaponEffect(battleview.arena, new Ship(), Target.newFromShip(ship), new TriggerAction("weapon")); let effect = new WeaponEffect(battleview.arena, new Ship(), Target.newFromShip(ship), new TriggerAction("weapon"));
effect.bulletsExecutor(1); effect.bulletsExecutor(1);
@ -44,7 +44,7 @@ module TK.SpaceTac.UI.Specs {
let battleview = testgame.view; let battleview = testgame.view;
battleview.timer = new Timer(); battleview.timer = new Timer();
let ship = nn(battleview.battle.playing_ship); let ship = battleview.battle.fleets[0].ships[0];
ship.setArenaPosition(50, 30); ship.setArenaPosition(50, 30);
let weapon = new TriggerAction("weapon", { effects: [new DamageEffect(1)], range: 500 }); let weapon = new TriggerAction("weapon", { effects: [new DamageEffect(1)], range: 500 });

View file

@ -304,7 +304,7 @@ module TK.SpaceTac.UI {
if (this.ship) { if (this.ship) {
let ship = this.ship; let ship = this.ship;
builder.in(builder.container("items"), builder => { builder.in(builder.container("items"), builder => {
let actions = ship.actions.listAll().filter(action => !(action instanceof EndTurnAction)); let actions = ship.actions.listAll();
actions.forEach(action => { actions.forEach(action => {
let button = builder.button(`action-${action.code}`, 24, 0, undefined, filler => ActionTooltip.fill(filler, ship, action)); let button = builder.button(`action-${action.code}`, 24, 0, undefined, filler => ActionTooltip.fill(filler, ship, action));
button.setScale(0.375); button.setScale(0.375);