From 0b71a531cce3598dab2961efac0bf496fce2bc39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Mon, 20 May 2019 00:32:15 +0200 Subject: [PATCH] Allow to select any ship to plan its turn --- ...my-playing.png => ship-enemy-selected.png} | Bin ...-own-playing.png => ship-own-selected.png} | Bin src/core/Battle.spec.ts | 175 ++------------- src/core/Battle.ts | 154 ++------------ src/core/BattleChecks.spec.ts | 14 +- src/core/BattleChecks.ts | 4 +- src/core/Drone.spec.ts | 2 +- src/core/GameSession.spec.ts | 9 +- src/core/Ship.spec.ts | 2 +- src/core/Ship.ts | 9 +- src/core/TestTools.ts | 20 +- src/core/actions/ActionList.spec.ts | 8 +- src/core/actions/ActionList.ts | 2 +- src/core/actions/BaseAction.spec.ts | 9 +- src/core/actions/BaseAction.ts | 6 - src/core/actions/DeployDroneAction.spec.ts | 2 +- src/core/actions/EndTurnAction.spec.ts | 199 ------------------ src/core/actions/EndTurnAction.ts | 74 ------- src/core/actions/MoveAction.spec.ts | 7 +- src/core/actions/TriggerAction.spec.ts | 4 +- src/core/actions/VigilanceAction.spec.ts | 1 - src/core/ai/AIWorker.ts | 2 +- src/core/ai/AbstractAI.ts | 14 -- src/core/ai/Maneuver.ts | 23 +- src/core/ai/TacticalAI.spec.ts | 2 - src/core/ai/TacticalAI.ts | 5 - src/core/ai/TacticalAIHelpers.spec.ts | 12 +- src/core/ai/TacticalAIHelpers.ts | 11 +- src/core/diffs/DroneDeployedDiff.spec.ts | 5 +- src/core/diffs/ShipChangeDiff.spec.ts | 36 ---- src/core/diffs/ShipChangeDiff.ts | 39 ---- src/core/diffs/ShipCooldownDiff.spec.ts | 2 +- src/core/diffs/ShipDamageDiff.spec.ts | 2 +- src/core/diffs/ShipDeathDiff.spec.ts | 3 - src/core/diffs/ShipDeathDiff.ts | 13 +- src/core/diffs/ShipEffectAddedDiff.spec.ts | 2 +- src/core/diffs/ShipEffectChangedDiff.spec.ts | 4 +- src/ui/TestGame.ts | 4 - src/ui/battle/ActionBar.spec.ts | 9 +- src/ui/battle/ActionBar.ts | 13 -- src/ui/battle/ActionIcon.ts | 14 +- src/ui/battle/ActionTooltip.spec.ts | 7 - src/ui/battle/ActionTooltip.ts | 4 +- src/ui/battle/Arena.ts | 27 +-- src/ui/battle/ArenaShip.spec.ts | 4 +- src/ui/battle/ArenaShip.ts | 82 ++------ src/ui/battle/BattleView.spec.ts | 21 +- src/ui/battle/BattleView.ts | 41 +++- src/ui/battle/LogProcessor.ts | 77 +------ src/ui/battle/ShipTooltip.spec.ts | 4 +- src/ui/battle/ShipTooltip.ts | 7 +- src/ui/battle/Targetting.spec.ts | 34 +-- src/ui/battle/WeaponEffect.spec.ts | 4 +- src/ui/character/CharacterSheet.ts | 2 +- 54 files changed, 180 insertions(+), 1050 deletions(-) rename data/stage2/image/battle/hud/{ship-enemy-playing.png => ship-enemy-selected.png} (100%) rename data/stage2/image/battle/hud/{ship-own-playing.png => ship-own-selected.png} (100%) delete mode 100644 src/core/actions/EndTurnAction.spec.ts delete mode 100644 src/core/actions/EndTurnAction.ts delete mode 100644 src/core/diffs/ShipChangeDiff.spec.ts delete mode 100644 src/core/diffs/ShipChangeDiff.ts diff --git a/data/stage2/image/battle/hud/ship-enemy-playing.png b/data/stage2/image/battle/hud/ship-enemy-selected.png similarity index 100% rename from data/stage2/image/battle/hud/ship-enemy-playing.png rename to data/stage2/image/battle/hud/ship-enemy-selected.png diff --git a/data/stage2/image/battle/hud/ship-own-playing.png b/data/stage2/image/battle/hud/ship-own-selected.png similarity index 100% rename from data/stage2/image/battle/hud/ship-own-playing.png rename to data/stage2/image/battle/hud/ship-own-selected.png diff --git a/src/core/Battle.spec.ts b/src/core/Battle.spec.ts index 0bf08c9..f34ac5c 100644 --- a/src/core/Battle.spec.ts +++ b/src/core/Battle.spec.ts @@ -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"); }); diff --git a/src/core/Battle.ts b/src/core/Battle.ts index 04a916b..9917694 100644 --- a/src/core/Battle.ts +++ b/src/core/Battle.ts @@ -18,11 +18,7 @@ module TK.SpaceTac { // Container of all engaged ships ships: RObjectContainer - // 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()) { diff --git a/src/core/BattleChecks.spec.ts b/src/core/BattleChecks.spec.ts index 8777bb7..f7adacb 100644 --- a/src/core/BattleChecks.spec.ts +++ b/src/core/BattleChecks.spec.ts @@ -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)] })); diff --git a/src/core/BattleChecks.ts b/src/core/BattleChecks.ts index 5fc5ca8..db7144c 100644 --- a/src/core/BattleChecks.ts +++ b/src/core/BattleChecks.ts @@ -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); diff --git a/src/core/Drone.spec.ts b/src/core/Drone.spec.ts index b922da3..bfb1966 100644 --- a/src/core/Drone.spec.ts +++ b/src/core/Drone.spec.ts @@ -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); diff --git a/src/core/GameSession.spec.ts b/src/core/GameSession.spec.ts index ab52e1b..21de993 100644 --- a/src/core/GameSession.spec.ts +++ b/src/core/GameSession.spec.ts @@ -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); diff --git a/src/core/Ship.spec.ts b/src/core/Ship.spec.ts index 6315976..d0b0f10 100644 --- a/src/core/Ship.spec.ts +++ b/src/core/Ship.spec.ts @@ -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), diff --git a/src/core/Ship.ts b/src/core/Ship.ts index 1de7755..fe481ad 100644 --- a/src/core/Ship.ts +++ b/src/core/Ship.ts @@ -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(); } /** diff --git a/src/core/TestTools.ts b/src/core/TestTools.ts index ee7e7af..24597e2 100644 --- a/src/core/TestTools.ts +++ b/src/core/TestTools.ts @@ -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`)); } } diff --git a/src/core/actions/ActionList.spec.ts b/src/core/actions/ActionList.spec.ts index 3482064..3af38fe 100644 --- a/src/core/actions/ActionList.spec.ts +++ b/src/core/actions/ActionList.spec.ts @@ -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"]]]); }) diff --git a/src/core/actions/ActionList.ts b/src/core/actions/ActionList.ts index 52a5879..7afe10d 100644 --- a/src/core/actions/ActionList.ts +++ b/src/core/actions/ActionList.ts @@ -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); } /** diff --git a/src/core/actions/BaseAction.spec.ts b/src/core/actions/BaseAction.spec.ts index 09f45ba..2b5db44 100644 --- a/src/core/actions/BaseAction.spec.ts +++ b/src/core/actions/BaseAction.spec.ts @@ -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"); - }, ]); }) diff --git a/src/core/actions/BaseAction.ts b/src/core/actions/BaseAction.ts index 10dec2b..162810e 100644 --- a/src/core/actions/BaseAction.ts +++ b/src/core/actions/BaseAction.ts @@ -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; } diff --git a/src/core/actions/DeployDroneAction.spec.ts b/src/core/actions/DeployDroneAction.spec.ts index 7937cb5..32fb556 100644 --- a/src/core/actions/DeployDroneAction.spec.ts +++ b/src/core/actions/DeployDroneAction.spec.ts @@ -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); diff --git a/src/core/actions/EndTurnAction.spec.ts b/src/core/actions/EndTurnAction.spec.ts deleted file mode 100644 index de9a3b8..0000000 --- a/src/core/actions/EndTurnAction.spec.ts +++ /dev/null @@ -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((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((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"); - } - ]); - }); - }); -} diff --git a/src/core/actions/EndTurnAction.ts b/src/core/actions/EndTurnAction.ts deleted file mode 100644 index 6e2d0b0..0000000 --- a/src/core/actions/EndTurnAction.ts +++ /dev/null @@ -1,74 +0,0 @@ -/// - -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."; - } - } -} diff --git a/src/core/actions/MoveAction.spec.ts b/src/core/actions/MoveAction.spec.ts index dde11b8..83db856 100644 --- a/src/core/actions/MoveAction.spec.ts +++ b/src/core/actions/MoveAction.spec.ts @@ -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); diff --git a/src/core/actions/TriggerAction.spec.ts b/src/core/actions/TriggerAction.spec.ts index 05e4a18..25d732d 100644 --- a/src/core/actions/TriggerAction.spec.ts +++ b/src/core/actions/TriggerAction.spec.ts @@ -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); diff --git a/src/core/actions/VigilanceAction.spec.ts b/src/core/actions/VigilanceAction.spec.ts index 1577239..ec418c0 100644 --- a/src/core/actions/VigilanceAction.spec.ts +++ b/src/core/actions/VigilanceAction.spec.ts @@ -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(), []); diff --git a/src/core/ai/AIWorker.ts b/src/core/ai/AIWorker.ts index d818fdb..0af8f6e 100644 --- a/src/core/ai/AIWorker.ts +++ b/src/core/ai/AIWorker.ts @@ -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; } diff --git a/src/core/ai/AbstractAI.ts b/src/core/ai/AbstractAI.ts index 5a3f21b..e5efaa8 100644 --- a/src/core/ai/AbstractAI.ts +++ b/src/core/ai/AbstractAI.ts @@ -45,20 +45,11 @@ module TK.SpaceTac { async play(): Promise { 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))); - } } /** diff --git a/src/core/ai/Maneuver.ts b/src/core/ai/Maneuver.ts index 73a58eb..2783939 100644 --- a/src/core/ai/Maneuver.ts +++ b/src/core/ai/Maneuver.ts @@ -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; } } } diff --git a/src/core/ai/TacticalAI.spec.ts b/src/core/ai/TacticalAI.spec.ts index 4d880af..1167407 100644 --- a/src/core/ai/TacticalAI.spec.ts +++ b/src/core/ai/TacticalAI.spec.ts @@ -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) { diff --git a/src/core/ai/TacticalAI.ts b/src/core/ai/TacticalAI.ts index 4eca9c3..3631300 100644 --- a/src/core/ai/TacticalAI.ts +++ b/src/core/ai/TacticalAI.ts @@ -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, diff --git a/src/core/ai/TacticalAIHelpers.spec.ts b/src/core/ai/TacticalAIHelpers.spec.ts index 8680e87..43cb718 100644 --- a/src/core/ai/TacticalAIHelpers.spec.ts +++ b/src/core/ai/TacticalAIHelpers.spec.ts @@ -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); diff --git a/src/core/ai/TacticalAIHelpers.ts b/src/core/ai/TacticalAIHelpers.ts index ac56688..c81fc0a 100644 --- a/src/core/ai/TacticalAIHelpers.ts +++ b/src/core/ai/TacticalAIHelpers.ts @@ -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; diff --git a/src/core/diffs/DroneDeployedDiff.spec.ts b/src/core/diffs/DroneDeployedDiff.spec.ts index 02a0546..5adbe70 100644 --- a/src/core/diffs/DroneDeployedDiff.spec.ts +++ b/src/core/diffs/DroneDeployedDiff.spec.ts @@ -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), diff --git a/src/core/diffs/ShipChangeDiff.spec.ts b/src/core/diffs/ShipChangeDiff.spec.ts deleted file mode 100644 index 19f36fd..0000000 --- a/src/core/diffs/ShipChangeDiff.spec.ts +++ /dev/null @@ -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"); - }, - ]); - }); - }); -} \ No newline at end of file diff --git a/src/core/diffs/ShipChangeDiff.ts b/src/core/diffs/ShipChangeDiff.ts deleted file mode 100644 index 1a24b60..0000000 --- a/src/core/diffs/ShipChangeDiff.ts +++ /dev/null @@ -1,39 +0,0 @@ -/// - -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); - } - } -} diff --git a/src/core/diffs/ShipCooldownDiff.spec.ts b/src/core/diffs/ShipCooldownDiff.spec.ts index a310ee8..6ab9f65 100644 --- a/src/core/diffs/ShipCooldownDiff.spec.ts +++ b/src/core/diffs/ShipCooldownDiff.spec.ts @@ -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); diff --git a/src/core/diffs/ShipDamageDiff.spec.ts b/src/core/diffs/ShipDamageDiff.spec.ts index a6a4888..53c9210 100644 --- a/src/core/diffs/ShipDamageDiff.spec.ts +++ b/src/core/diffs/ShipDamageDiff.spec.ts @@ -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, [ diff --git a/src/core/diffs/ShipDeathDiff.spec.ts b/src/core/diffs/ShipDeathDiff.spec.ts index fb335cd..1d92de3 100644 --- a/src/core/diffs/ShipDeathDiff.spec.ts +++ b/src/core/diffs/ShipDeathDiff.spec.ts @@ -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"); }, ]); }); diff --git a/src/core/diffs/ShipDeathDiff.ts b/src/core/diffs/ShipDeathDiff.ts index 4233a25..1732ec1 100644 --- a/src/core/diffs/ShipDeathDiff.ts +++ b/src/core/diffs/ShipDeathDiff.ts @@ -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); - } } } } diff --git a/src/core/diffs/ShipEffectAddedDiff.spec.ts b/src/core/diffs/ShipEffectAddedDiff.spec.ts index c84c337..8437d8f 100644 --- a/src/core/diffs/ShipEffectAddedDiff.spec.ts +++ b/src/core/diffs/ShipEffectAddedDiff.spec.ts @@ -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"); diff --git a/src/core/diffs/ShipEffectChangedDiff.spec.ts b/src/core/diffs/ShipEffectChangedDiff.spec.ts index c46d126..88b23d0 100644 --- a/src/core/diffs/ShipEffectChangedDiff.spec.ts +++ b/src/core/diffs/ShipEffectChangedDiff.spec.ts @@ -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); diff --git a/src/ui/TestGame.ts b/src/ui/TestGame.ts index 2a4bd62..bf19672 100644 --- a/src/ui/TestGame.ts +++ b/src/ui/TestGame.ts @@ -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 }]; diff --git a/src/ui/battle/ActionBar.spec.ts b/src/ui/battle/ActionBar.spec.ts index 43fe0cb..5c75a96 100644 --- a/src/ui/battle/ActionBar.spec.ts +++ b/src/ui/battle/ActionBar.spec.ts @@ -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"); }); diff --git a/src/ui/battle/ActionBar.ts b/src/ui/battle/ActionBar.ts index 0ff6e09..1796f23 100644 --- a/src/ui/battle/ActionBar.ts +++ b/src/ui/battle/ActionBar.ts @@ -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); } diff --git a/src/ui/battle/ActionIcon.ts b/src/ui/battle/ActionIcon.ts index a9cd662..8b1259d 100644 --- a/src/ui/battle/ActionIcon.ts +++ b/src/ui/battle/ActionIcon.ts @@ -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); diff --git a/src/ui/battle/ActionTooltip.spec.ts b/src/ui/battle/ActionTooltip.spec.ts index 1832546..8643eb3 100644 --- a/src/ui/battle/ActionTooltip.spec.ts +++ b/src/ui/battle/ActionTooltip.spec.ts @@ -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 ]"); }); }); } diff --git a/src/ui/battle/ActionTooltip.ts b/src/ui/battle/ActionTooltip.ts index fce461e..5931ec1 100644 --- a/src/ui/battle/ActionTooltip.ts +++ b/src/ui/battle/ActionTooltip.ts @@ -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} ]`; diff --git a/src/ui/battle/Arena.ts b/src/ui/battle/Arena.ts index 75ad9de..5cfb301 100644 --- a/src/ui/battle/Arena.ts +++ b/src/ui/battle/Arena.ts @@ -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 { - if (this.playing) { - this.playing.setPlaying(false); - this.playing = null; + async setSelectedShip(ship: Ship | null, animate = true): Promise { + 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; } } diff --git a/src/ui/battle/ArenaShip.spec.ts b/src/ui/battle/ArenaShip.spec.ts index fb8de62..b8d2e1f 100644 --- a/src/ui/battle/ArenaShip.spec.ts +++ b/src/ui/battle/ArenaShip.spec.ts @@ -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); diff --git a/src/ui/battle/ArenaShip.ts b/src/ui/battle/ArenaShip.ts index e7c02db..5e5f7d7 100644 --- a/src/ui/battle/ArenaShip.ts +++ b/src/ui/battle/ArenaShip.ts @@ -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 { + async setSelected(selected: boolean, animate = true): Promise { 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 */ diff --git a/src/ui/battle/BattleView.spec.ts b/src/ui/battle/BattleView.spec.ts index 736978e..2f28ff6 100644 --- a/src/ui/battle/BattleView.spec.ts +++ b/src/ui/battle/BattleView.spec.ts @@ -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(), [ diff --git a/src/ui/battle/BattleView.ts b/src/ui/battle/BattleView.ts index 8c06352..a00801b 100644 --- a/src/ui/battle/BattleView.ts +++ b/src/ui/battle/BattleView.ts @@ -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, 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 { diff --git a/src/ui/battle/LogProcessor.ts b/src/ui/battle/LogProcessor.ts index 96785e2..a823af7 100644 --- a/src/ui/battle/LogProcessor.ts +++ b/src/ui/battle/LogProcessor.ts @@ -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 () => { diff --git a/src/ui/battle/ShipTooltip.spec.ts b/src/ui/battle/ShipTooltip.spec.ts index e2979f3..e36af1e 100644 --- a/src/ui/battle/ShipTooltip.spec.ts +++ b/src/ui/battle/ShipTooltip.spec.ts @@ -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 !" diff --git a/src/ui/battle/ShipTooltip.ts b/src/ui/battle/ShipTooltip.ts index a99bd8b..903051b 100644 --- a/src/ui/battle/ShipTooltip.ts +++ b/src/ui/battle/ShipTooltip.ts @@ -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); diff --git a/src/ui/battle/Targetting.spec.ts b/src/ui/battle/Targetting.spec.ts index 9d08b12..2c27409 100644 --- a/src/ui/battle/Targetting.spec.ts +++ b/src/ui/battle/Targetting.spec.ts @@ -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); diff --git a/src/ui/battle/WeaponEffect.spec.ts b/src/ui/battle/WeaponEffect.spec.ts index b9a4525..2dca29a 100644 --- a/src/ui/battle/WeaponEffect.spec.ts +++ b/src/ui/battle/WeaponEffect.spec.ts @@ -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 }); diff --git a/src/ui/character/CharacterSheet.ts b/src/ui/character/CharacterSheet.ts index 3219e4d..3d763b8 100644 --- a/src/ui/character/CharacterSheet.ts +++ b/src/ui/character/CharacterSheet.ts @@ -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);