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 {
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 => {
var fleet1 = new Fleet();
var fleet2 = new Fleet();
@ -59,74 +34,7 @@ module TK.SpaceTac {
check.nears(ship5.arena_angle, Math.PI);
});
test.case("advances to next ship in play order", 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 => {
test.case("detects victory condition and logs a final EndBattleDiff", check => {
var fleet1 = new Fleet();
var fleet2 = new Fleet();
@ -137,12 +45,11 @@ module TK.SpaceTac {
var battle = new Battle(fleet1, fleet2);
battle.ships.list().forEach(ship => TestTools.setShipModel(ship, 10, 0));
battle.start();
battle.play_order = [ship3, ship2, ship1];
check.equals(battle.ended, false);
ship1.setDead();
ship2.setDead();
battle.advanceToNextShip();
battle.applyTurnPlan({ fleets: [] });
check.equals(battle.ended, true);
let diff = battle.log.get(battle.log.count() - 1);
@ -197,10 +104,9 @@ module TK.SpaceTac {
ship4.setArenaPosition(12, 12);
var battle = new Battle(fleet1);
battle.throwInitiative(new SkewedRandomGenerator([5, 4, 3, 2]));
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 => {
@ -223,60 +129,6 @@ module TK.SpaceTac {
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 => {
let battle = new Battle();
let ship = battle.fleets[0].addShip();
@ -318,15 +170,12 @@ module TK.SpaceTac {
test.case("is serializable", check => {
let battle = Battle.newQuickRandom();
battle.ai_playing = true;
let serializer = new Serializer(TK.SpaceTac);
let data = serializer.serialize(battle);
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");
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})`);
});
test.case("can revert the last action", check => {
test.case("can revert the last turn", check => {
let battle = new Battle();
let ship = battle.fleets[0].addShip();
ship.setValue("hull", 13);
battle.log.clear();
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 ShipActionUsedDiff(ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)));
battle.log.add(new TurnStartDiff([]));
battle.log.add(new ShipValueDiff(ship, "hull", 2));
check.in("initial state", check => {
@ -356,23 +205,23 @@ module TK.SpaceTac {
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(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(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(battle.log.count(), 0, "log count=0");
});

View File

@ -18,11 +18,7 @@ module TK.SpaceTac {
// Container of all engaged ships
ships: RObjectContainer<Ship>
// List of playing ships, sorted by their initiative throw
play_order: Ship[]
play_index = -1
// Current battle "cycle" (one cycle is one turn done for all ships in the play order)
// Current battle turn count
cycle = 0
// List of deployed drones
@ -32,13 +28,9 @@ module TK.SpaceTac {
width: 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) {
this.fleets = [fleet1, fleet2];
this.ships = new RObjectContainer(fleet1.ships.concat(fleet2.ships));
this.play_order = [];
this.width = width;
this.height = height;
@ -50,10 +42,6 @@ module TK.SpaceTac {
});
}
postUnserialize() {
this.ai_playing = false;
}
/**
* Property is true if the battle has ended
*/
@ -64,9 +52,10 @@ module TK.SpaceTac {
/**
* Apply a turn plan through a resolution
*/
applyTurnPlan(plan: TurnPlan): void {
const resolution = new TurnResolution(this, plan);
applyTurnPlan(plan: TurnPlan, random?: RandomGenerator): void {
const resolution = new TurnResolution(this, plan, random);
resolution.resolve();
this.performChecks();
}
/**
@ -93,13 +82,6 @@ module TK.SpaceTac {
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.
*/
@ -139,86 +121,7 @@ module TK.SpaceTac {
* This can be used by the UI to determine if player interaction is allowed
*/
canPlay(player: Player): boolean {
if (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;
}
return !this.ended;
}
// Defines the initial ship positions of all engaged fleets
@ -245,25 +148,12 @@ module TK.SpaceTac {
}
/**
* Get the next playing ship
*/
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
* Make an AI play the current turn
*/
playAI(debug = false): boolean {
if (this.playing_ship && !this.ai_playing) {
this.ai_playing = true;
AIWorker.process(this, debug);
return true;
} else {
return false;
}
// TODO
//AIWorker.process(this, debug);
return false;
}
/**
@ -278,19 +168,6 @@ module TK.SpaceTac {
this.cycle = 1;
this.placeShips();
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
*/
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) {
let action = ship.actions.getById(action_id);
if (action) {
@ -377,11 +254,6 @@ module TK.SpaceTac {
if (!this.ended) {
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;
@ -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!
*/
revertOneAction(): void {
revertOneTurn(): void {
let client = new BattleLogClient(this, this.log);
while (!client.atStart() && !(client.getCurrent() instanceof ShipActionUsedDiff)) {
while (!client.atStart() && !(client.getCurrent() instanceof TurnStartDiff)) {
client.backward();
}
if (!client.atStart()) {

View File

@ -28,24 +28,27 @@ module TK.SpaceTac.Specs {
], "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 [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);
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();
check.equals(result, [new ShipDeathDiff(battle, ship2)], "ship2 marked as dead");
battle.applyDiffs(result);
ship1.setValue("hull", 0);
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);
result = checks.checkDeadShips();
check.equals(result, [], "ship1 left playing");
check.equals(result, [], "no ship left");
})
test.case("fixes area effects", check => {
@ -78,7 +81,6 @@ module TK.SpaceTac.Specs {
let ship2 = battle.fleets[1].addShip();
ship2.setArenaPosition(1000, 1000);
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 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[] {
// 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) {
return dying.getDeathDiffs(this.battle);

View File

@ -2,7 +2,7 @@ module TK.SpaceTac {
testing("Drone", test => {
test.case("applies area effects when deployed", check => {
let battle = TestTools.createBattle();
let ship = nn(battle.playing_ship);
let ship = battle.fleets[0].ships[0];
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)] });
ship.actions.addCustom(weapon);

View File

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

View File

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

View File

@ -45,12 +45,6 @@ module TK.SpaceTac {
// 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
constructor(fleet: Fleet | null = null, name: string | null = null, model = new ShipModel()) {
super();
@ -112,8 +106,7 @@ module TK.SpaceTac {
// Make an initiative throw, to resolve play order in a battle
throwInitiative(gen: RandomGenerator): number {
this.play_priority = gen.random() * this.attributes.initiative.get();
return this.play_priority;
return gen.random() * this.attributes.initiative.get();
}
/**

View File

@ -16,8 +16,6 @@ module TK.SpaceTac {
var battle = new Battle(fleet1, fleet2);
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;
}
@ -45,15 +43,6 @@ module TK.SpaceTac {
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)
*/
@ -115,18 +104,17 @@ module TK.SpaceTac {
static actionChain(check: TestContext, battle: Battle, actions: [Ship, BaseAction, Target | undefined][], checks: ((check: TestContext) => void)[]): void {
checks[0](check.sub("initial state"));
battle.applyDiffs([new TurnStartDiff([])]);
for (let i = 0; i < actions.length; i++) {
let [ship, action, target] = actions[i];
battle.setPlayingShip(ship);
let result = battle.applyOneAction(action.id, target);
check.equals(result, true, `action ${i + 1} successfully applied`);
checks[i + 1](check.sub(`after action ${i + 1} applied`));
}
for (let i = actions.length - 1; i >= 0; i--) {
battle.revertOneAction();
checks[i](check.sub(`after action ${i + 1} reverted`));
}
battle.revertOneTurn();
checks[0](check.sub(`after turn reverted`));
}
}

View File

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

View File

@ -27,7 +27,7 @@ module TK.SpaceTac {
* List all actions
*/
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 => {
test.case("may be applied and reverted", check => {
let battle = TestTools.createBattle();
let ship = nn(battle.playing_ship);
let ship = battle.fleets[0].ships[0];
TestTools.setShipModel(ship, 100, 0, 10);
let action = TestTools.addWeapon(ship, 0, 3, 100, 50);
action.configureCooldown(2, 1);
@ -10,7 +10,6 @@ module TK.SpaceTac.Specs {
TestTools.actionChain(check, battle, [
[ship, action, Target.newFromLocation(0, 0)],
[ship, action, Target.newFromLocation(0, 0)],
[ship, EndTurnAction.SINGLETON, undefined],
], [
check => {
check.equals(ship.getValue("power"), 10, "power");
@ -30,12 +29,6 @@ module TK.SpaceTac.Specs {
check.equals(cooldown.uses, 2, "uses");
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
*/
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)) {
return ActionUnavailability.NO_SUCH_ACTION;
}

View File

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

View File

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

View File

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

View File

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

View File

@ -45,20 +45,11 @@ module TK.SpaceTac {
async play(): Promise<void> {
this.started = (new Date()).getTime();
if (!this.ship.playing) {
console.error(`${this.name} tries to play a ship out of turn`);
return;
}
// Work loop
this.initWork();
let last = new Date().getTime();
let ship = this.ship;
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) {
console.warn(`${this.name} takes too long to play, forcing turn end`);
break;
@ -70,11 +61,6 @@ module TK.SpaceTac {
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);
}
/**
* 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
*/
@ -88,16 +74,13 @@ module TK.SpaceTac {
* Standard feedback for this maneuver. It will apply it on the battle state.
*/
apply(battle: Battle): boolean {
if (!this.ship.is(battle.playing_ship)) {
console.error("Maneuver was not produced for the playing ship", this, battle);
return false;
} else if (!this.simulation.success) {
if (!this.simulation.success) {
return false;
} else {
let parts = this.simulation.parts;
for (let i = 0; i < parts.length; i++) {
let part = parts[i];
if (part.action instanceof EndTurnAction || part.possible) {
if (part.possible) {
if (!battle.applyOneAction(part.action.id, part.target)) {
return false;
}
@ -105,7 +88,7 @@ module TK.SpaceTac {
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 => {
let battle = new Battle();
let ship = battle.fleets[0].addShip();
TestTools.setShipPlaying(battle, ship);
ship.playing = true;
let ai = new TacticalAI(ship, maneuver => {
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);
}
if (this.best.action instanceof EndTurnAction) {
return false;
}
let success = this.feedback(best_maneuver);
if (success) {
// Try to play another maneuver
@ -98,7 +94,6 @@ module TK.SpaceTac {
*/
getDefaultProducers() {
let producers = [
TacticalAIHelpers.produceEndTurn,
TacticalAIHelpers.produceDirectShots,
TacticalAIHelpers.produceBlastShots,
TacticalAIHelpers.produceToggleActions,

View File

@ -8,7 +8,6 @@ module TK.SpaceTac.Specs {
let ship1b = battle.fleets[1].addShip(new Ship(null, "1B"));
TestTools.setShipModel(ship0a, 100, 0, 10);
TestTools.setShipPlaying(battle, ship0a);
let result = imaterialize(TacticalAIHelpers.produceDirectShots(ship0a, battle));
check.equals(result.length, 0);
@ -30,7 +29,6 @@ module TK.SpaceTac.Specs {
let ship = battle.fleets[0].addShip();
TestTools.setShipModel(ship, 100, 0, 10);
TestTools.setShipPlaying(battle, ship);
let result = imaterialize(TacticalAIHelpers.produceRandomMoves(ship, battle, 2, 1));
check.equals(result.length, 0);
@ -51,7 +49,6 @@ module TK.SpaceTac.Specs {
let ship = battle.fleets[0].addShip();
let weapon = TestTools.addWeapon(ship, 50, 1, 1000, 105);
TestTools.setShipModel(ship, 100, 0, 10, 1, [weapon]);
TestTools.setShipPlaying(battle, ship);
let result = imaterialize(TacticalAIHelpers.produceInterestingBlastShots(ship, battle));
check.equals(result.length, 0);
@ -89,7 +86,6 @@ module TK.SpaceTac.Specs {
let action3 = new VigilanceAction("Vigilance", { radius: 150 });
TestTools.setShipModel(ship, 100, 0, 10, 1, [action1, action2, action3]);
TestTools.addEngine(ship, 1000);
TestTools.setShipPlaying(battle, ship);
check.patch(TacticalAIHelpers, "scanArena", () => iarray([
Target.newFromLocation(1, 0),
@ -158,16 +154,10 @@ module TK.SpaceTac.Specs {
maneuver = new Maneuver(ship, engine, Target.newFromLocation(0, 48));
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);
maneuver = new Maneuver(ship, engine, Target.newFromLocation(0, 48));
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 => {
@ -276,7 +266,7 @@ module TK.SpaceTac.Specs {
let battle = TestTools.createBattle();
let ship = battle.fleets[0].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);
let action = new TriggerAction("Test", { range: 100, power: 1 });
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.
*/
@ -151,9 +144,7 @@ module TK.SpaceTac {
static evaluateIdling(ship: Ship, battle: Battle, maneuver: Maneuver): number {
let power_capacity = ship.getAttribute("power_capacity") || 1;
if (maneuver.action instanceof EndTurnAction) {
return -ship.getValue("power") / power_capacity;
} else if (maneuver.action instanceof TriggerAction) {
if (maneuver.action instanceof TriggerAction) {
return 0.5;
} else if (maneuver.action instanceof ToggleAction) {
return ship.actions.isToggled(maneuver.action) ? -0.2 : 0.5;

View File

@ -2,8 +2,9 @@ module TK.SpaceTac.Specs {
testing("DroneDeployedDiff", test => {
test.case("applies and reverts", check => {
let battle = TestTools.createBattle();
let drone1 = new Drone(battle.play_order[0]);
let drone2 = new Drone(battle.play_order[0], "test");
let ship = battle.fleets[0].ships[0];
let drone1 = new Drone(ship);
let drone2 = new Drone(ship, "test");
TestTools.diffChain(check, battle, [
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 => {
test.case("applies and reverts", check => {
let battle = TestTools.createBattle();
let ship = battle.play_order[0];
let ship = battle.fleets[0].ships[0];
let weapon = TestTools.addWeapon(ship);
weapon.configureCooldown(1, 3);
let cooldown = ship.actions.getCooldown(weapon);

View File

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

View File

@ -5,7 +5,6 @@ module TK.SpaceTac.Specs {
let ship1 = battle.fleets[0].addShip();
let ship2 = battle.fleets[0].addShip();
let ship3 = battle.fleets[1].addShip();
battle.play_order = [ship3, ship2, ship1];
TestTools.diffChain(check, battle, [
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(battle.fleets[0].ships, [ship1, ship2], "fleet1");
check.equals(battle.fleets[1].ships, [ship3], "fleet2");
check.equals(battle.play_order, [ship3, ship2, ship1], "in play order");
},
check => {
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(battle.fleets[0].ships, [ship1, ship2], "fleet1");
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)
*
* 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 {
// Index in the play order at which the ship was
play_index: number
constructor(battle: Battle, ship: Ship) {
super(ship);
this.play_index = battle.play_order.indexOf(ship);
}
protected applyOnShip(ship: Ship, battle: Battle): void {
ship.alive = false;
if (this.play_index >= 0) {
battle.removeFromPlayOrder(this.play_index);
}
}
protected revertOnShip(ship: Ship, battle: Battle): void {
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 => {
test.case("applies and reverts", check => {
let battle = TestTools.createBattle();
let ship = battle.play_order[0];
let ship = battle.fleets[0].ships[0];
let effect1 = new BaseEffect("e1");
let effect2 = new BaseEffect("e2");

View File

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

View File

@ -85,10 +85,6 @@ module TK.SpaceTac.UI.Specs {
view.splash = false;
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;
return [view, { player, battle }];

View File

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

View File

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

View File

@ -59,15 +59,13 @@ module TK.SpaceTac.UI {
this.img_action.setAlpha(0.2);
// Hotkey indicator
if (!(action instanceof EndTurnAction)) {
this.shortcut_container = builder.container("shortcut", 0, -47);
builder.in(this.shortcut_container, builder => {
builder.image("battle-actionbar-hotkey", 0, 0, true);
builder.text(`${(position + 1) % 10}`, 0, -4, {
size: 12, color: "#d1d1d1", shadow: true, center: true, vcenter: true
});
this.shortcut_container = builder.container("shortcut", 0, -47);
builder.in(this.shortcut_container, builder => {
builder.image("battle-actionbar-hotkey", 0, 0, true);
builder.text(`${(position + 1) % 10}`, 0, -4, {
size: 12, color: "#d1d1d1", shadow: true, center: true, vcenter: true
});
}
});
// Bottom indicator
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 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);
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[3], "Fire (power 2, range 50km):\n• do 12 damage on target");
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") {
let shortcut = "";
if (action instanceof EndTurnAction) {
shortcut = "[ space ]";
} else if (position == 9) {
if (position == 9) {
shortcut = "[ 0 ]";
} else if (position >= 0 && position < 9) {
shortcut = `[ ${position + 1} ]`;

View File

@ -22,8 +22,8 @@ module TK.SpaceTac.UI {
// Currently hovered ship
private hovered: ArenaShip | null
// Currently playing ship
private playing: ArenaShip | null
// Currently selected ship
private selected: ArenaShip | null
// Layer for particles
container: UIContainer
@ -41,7 +41,7 @@ module TK.SpaceTac.UI {
// Create a graphical arena for ship sprites to fight in a 2D space
constructor(view: BattleView, container?: UIContainer) {
this.view = view;
this.playing = null;
this.selected = null;
this.hovered = null;
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.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> {
if (this.playing) {
this.playing.setPlaying(false);
this.playing = null;
async setSelectedShip(ship: Ship | null, animate = true): Promise<void> {
if (this.selected) {
this.selected.setSelected(false);
this.selected = null;
}
if (ship) {
let arena_ship = this.findShipSprite(ship);
if (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);
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));
check.equals(sprite.effects_messages.list.length, 0);
@ -16,7 +16,7 @@ module TK.SpaceTac.UI.Specs {
test.case("adds sticky effects display", check => {
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));
check.equals(sprite.active_effects_display.list.length, 0);

View File

@ -27,14 +27,9 @@ module TK.SpaceTac.UI {
life_evasion: UIContainer
toggle_hsp: Toggle
// Play order
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
// Frames to indicate the owner, if the ship is selected, and if it is hovered
frame_owner: UIImage
frame_playing: UIImage
frame_selected: UIImage
frame_hover: UIImage
// Effects display
@ -59,8 +54,8 @@ module TK.SpaceTac.UI {
// 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_playing = builder.image(this.enemy ? "battle-hud-ship-enemy-playing" : "battle-hud-ship-own-playing", 0, 0, true);
this.setPlaying(false);
this.frame_selected = builder.image(this.enemy ? "battle-hud-ship-enemy-selected" : "battle-hud-ship-own-selected", 0, 0, true);
this.setSelected(false);
this.frame_hover = builder.image("battle-hud-ship-hover", 0, 0, true);
this.frame_hover.setVisible(false);
@ -83,20 +78,11 @@ module TK.SpaceTac.UI {
this.life_evasion = builder.in(this.hsp).container("evasion");
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
this.active_effects_display = builder.container("active-effects", 0, -44);
this.effects_messages = builder.container("effects-messages");
this.effects_messages_toggle = this.battleview.animations.newVisibilityToggle(this.effects_messages, 500, false);
this.updatePlayOrder();
this.updateHull(this.ship.getValue("hull"));
this.updateShield(this.ship.getValue("shield"));
this.updateEvasion(this.ship.getAttribute("evasion"));
@ -104,7 +90,7 @@ module TK.SpaceTac.UI {
this.updateEffectsRadius();
// 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.moveToArenaLocation(ship.arena_x, ship.arena_y, ship.arena_angle, 1);
} else {
@ -112,7 +98,6 @@ module TK.SpaceTac.UI {
}
// Log processing
this.battleview.log_processor.register(diff => this.processBattleDiff(diff));
this.battleview.log_processor.registerForShip(ship, diff => this.processShipDiff(diff));
}
@ -120,16 +105,6 @@ module TK.SpaceTac.UI {
return `ArenaShip ${this.ship.jasmineToString()}`;
}
/**
* Process a battle diff
*/
private processBattleDiff(diff: BaseBattleDiff) {
if (diff instanceof ShipChangeDiff) {
this.updatePlayOrder();
}
return {};
}
/**
* Process a ship diff
*/
@ -222,16 +197,7 @@ module TK.SpaceTac.UI {
} else if (diff instanceof ShipActionUsedDiff) {
let action = this.ship.actions.getById(diff.action);
if (action) {
if (action instanceof EndTurnAction) {
return {
foreground: async (speed: number) => {
if (speed) {
await this.displayEffect("End turn", true, speed);
await timer.sleep(500 / speed);
}
}
}
} else if (!(action instanceof ToggleAction)) {
if (!(action instanceof ToggleAction)) {
let action_name = action.name;
return {
foreground: async (speed: number) => {
@ -283,32 +249,22 @@ module TK.SpaceTac.UI {
*/
setHovered(hovered: boolean, tactical: boolean) {
let client = tactical ? "tactical" : "hover";
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.toggle_hsp.manipulate(client)(hovered && this.ship.alive);
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
*/
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_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");
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 {
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
*/

View File

@ -6,11 +6,11 @@ module TK.SpaceTac.UI.Specs {
let battleview = testgame.view;
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);
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);
check.same(battleview.ship_hovered, ship, "ship2 hovered");
@ -20,7 +20,7 @@ module TK.SpaceTac.UI.Specs {
battleview.cursorOnShip(ship);
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");
battleview.cursorOffShip(ship);
@ -32,7 +32,7 @@ module TK.SpaceTac.UI.Specs {
check.equals(battleview.targetting.active, false);
battleview.setInteractionEnabled(true);
let ship = nn(battleview.battle.playing_ship);
let ship = battleview.battle.fleets[0].ships[0];
let weapon = TestTools.addWeapon(ship, 10);
battleview.enterTargettingMode(ship, weapon, ActionTargettingMode.SPACE);
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.ship_hovered, null);
ship = battleview.battle.play_order[3];
ship = battleview.battle.fleets[1].ships[0];
battleview.cursorHovered(ship.location, ship);
check.equals(battleview.targetting.target, Target.newFromLocation(ship.arena_x, ship.arena_y));
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 => {
let battleview = testgame.view;
battleview.setInteractionEnabled(true);
battleview.setShipSelected(battleview.battle.fleets[0].ships[0]);
let action_icon = battleview.action_bar.action_icons[0];
check.equals(battleview.targetting.active, false);
@ -79,21 +80,23 @@ module TK.SpaceTac.UI.Specs {
battleview.numberPressed(3);
check.equals(battleview.targetting.active, true);
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);
check.equals(battleview.targetting.active, true);
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 => {
let battleview = testgame.view;
let playing_ship = nn(battleview.battle.playing_ship);
check.equals(battleview.turn_plannings.length, 2);
check.equals(battleview.turn_plannings[0].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));
check.equals(battleview.turn_plannings.length, 2);
check.equals(battleview.turn_plannings[0].collectAllActions(), [

View File

@ -52,7 +52,10 @@ module TK.SpaceTac.UI {
action_bar!: ActionBar
// Currently hovered ship
ship_hovered!: Ship | null
ship_hovered: Ship | null = null
// Currently selected ship
ship_selected: Ship | null = null
// Ship tooltip
ship_tooltip!: ShipTooltip
@ -81,6 +84,7 @@ module TK.SpaceTac.UI {
this.battle = duplicate(data.battle, <any>TK.SpaceTac);
this.turn_plannings = this.battle.fleets.map(fleet => new TurnPlanning(this.battle, fleet.player));
this.ship_hovered = null;
this.ship_selected = null;
this.background = null;
this.multi = new MultiBattle();
@ -198,14 +202,19 @@ module TK.SpaceTac.UI {
return false;
}
let done = false;
this.turn_plannings.forEach(planning => {
if (this.action_bar.ship && this.player.is(planning.player)) {
planning.addAction(this.action_bar.ship, action, target);
const ship = this.ship_selected;
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 {
if (this.interacting) {
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) {
this.targetting.setTarget(Target.newFromShip(ship));
}
@ -312,9 +321,8 @@ module TK.SpaceTac.UI {
cursorClicked(): void {
if (this.targetting.active) {
this.validationPressed();
} else if (this.ship_hovered && this.player.is(this.ship_hovered.fleet.player) && this.interacting) {
this.character_sheet.show(this.ship_hovered);
this.setShipHovered(null);
} else if (this.ship_hovered && this.player.is(this.ship_hovered.getPlayer()) && this.interacting) {
this.setShipSelected(this.ship_hovered);
} else {
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
// Disable interaction when it is the AI turn, or when the current ship can't play
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
*/
@ -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
*/
@ -217,18 +157,8 @@ module TK.SpaceTac.UI {
* Transfer control to the needed player (or not)
*/
private transferControl() {
let player = this.getPlayerNeeded();
if (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);
}
// TODO Should be done by the battleview itself
this.view.setInteractionEnabled(this.view.battle.canPlay(this.view.player));
}
/**
@ -236,7 +166,8 @@ module TK.SpaceTac.UI {
*/
private checkReaction(diff: BaseBattleDiff): LogProcessorDelegate {
if (this.log.isPlaying()) {
let reaction = this.view.session.reactions.check(this.view.player, this.view.battle, this.view.battle.playing_ship, diff);
let playing_ship = null; // FIXME
let reaction = this.view.session.reactions.check(this.view.player, this.view.battle, playing_ship, diff);
if (reaction) {
return {
foreground: async () => {

View File

@ -4,7 +4,7 @@ module TK.SpaceTac.UI.Specs {
test.case("fills ship details", check => {
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);
ship.name = "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-bad");
check.equals(texts, [
"Level 1 Fury", "Plays in 2 turns",
"Level 1 Fury",
"57", "max", "58", "100", "max", "140", "7", "9", "max", "12",
"Weapon", "hull capacity +50", "limit shield capacity to 2 for 3 turns",
"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 });
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, 1, "#2ad8dc", "attribute-shield_capacity", ship.getValue("shield"), ship.getAttribute("shield_capacity"));
ShipTooltip.addValue(builder, 2, "#c1f06b", "attribute-evasion", ship.getAttribute("evasion"));
@ -55,7 +50,7 @@ module TK.SpaceTac.UI {
let iy = 210;
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);
icon.setScale(0.15);
builder.text(action.name, 46, iy + 8);

View File

@ -12,7 +12,7 @@ module TK.SpaceTac.UI.Specs {
test.case("draws simulation parts", check => {
let targetting = newTargetting();
let ship = nn(testgame.view.battle.playing_ship);
let ship = testgame.view.battle.fleets[0].ships[0];
ship.setArenaPosition(10, 20);
let weapon = TestTools.addWeapon(ship);
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 => {
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 action = new TriggerAction("weapon", { range: 50 });
@ -81,7 +81,7 @@ module TK.SpaceTac.UI.Specs {
test.case("updates graphics from simulation", check => {
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 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 => {
let targetting = newTargetting();
let playing_ship = nn(testgame.view.battle.playing_ship);
let action = TestTools.addWeapon(playing_ship);
let ship = testgame.view.battle.fleets[0].ships[0];
let action = TestTools.addWeapon(ship);
let ship1 = testgame.view.battle.play_order[1];
let ship2 = testgame.view.battle.play_order[2];
let ship1 = testgame.view.battle.fleets[1].ships[0];
let ship2 = testgame.view.battle.fleets[1].ships[1];
ship1.setArenaPosition(8000, 50);
ship2.setArenaPosition(8000, 230);
targetting.setAction(playing_ship, action, ActionTargettingMode.SPACE);
targetting.setAction(ship, action, ActionTargettingMode.SPACE);
targetting.setTargetFromLocation({ x: 8000, y: 60 });
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 });
check.equals(targetting.target, Target.newFromShip(ship1), "ship 1");
targetting.setTargetFromLocation({ x: 8100, y: 200 });
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 });
check.equals(targetting.target, new Target(8000, 60, playing_ship), "surroundings 1");
targetting.setTargetFromLocation({ x: playing_ship.arena_x + 10, y: playing_ship.arena_y - 20 });
check.equals(targetting.target, Target.newFromShip(playing_ship), "surroundings 2");
check.equals(targetting.target, new Target(8000, 60, ship), "surroundings 1");
targetting.setTargetFromLocation({ x: ship.arena_x + 10, y: ship.arena_y - 20 });
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 });
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 });
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 => {
let targetting = newTargetting();
let ship = nn(testgame.view.battle.playing_ship);
let ship = testgame.view.battle.fleets[0].ships[0];
ship.setArenaPosition(0, 0);
TestTools.setShipModel(ship, 100, 0, 8);
let move = TestTools.addEngine(ship, 100);

View File

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

View File

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