From 4e624dc9db9b82e51653ef91ad43bc0061a9e1f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Wed, 13 Jun 2018 23:52:36 +0200 Subject: [PATCH] Display simulated ship displacement effects while targetting --- TODO.md | 2 +- src/core/Battle.spec.ts | 2 +- src/core/Battle.ts | 6 ++- src/core/BattleCheats.spec.ts | 4 +- src/core/BattleChecks.ts | 6 ++- src/core/BattleOutcome.ts | 6 +-- src/core/Fleet.ts | 4 +- src/core/MoveFireSimulator.spec.ts | 31 +++++++++++++ src/core/MoveFireSimulator.ts | 20 +++++++++ src/core/StarLocation.ts | 2 +- src/core/diffs/EndBattleDiff.spec.ts | 2 +- src/core/missions/MissionPartEscort.spec.ts | 2 +- src/ui/battle/OutcomeDialog.ts | 2 +- src/ui/battle/Targetting.ts | 48 +++++++++++++++------ 14 files changed, 108 insertions(+), 29 deletions(-) diff --git a/TODO.md b/TODO.md index ee3d8fe..9dfb96c 100644 --- a/TODO.md +++ b/TODO.md @@ -43,7 +43,7 @@ Battle * Improve arena ships layering (sometimes information is displayed behind other sprites) * In the ship tooltip, show power cost, toggled and overheat states * Display shield (and its (dis)appearance) -* Display estimated damage and displacement in targetting mode +* Display estimated damage in targetting mode * Add a voluntary retreat option * Toggle bar/text display in power section of action bar * Show a cooldown indicator on move action icon, if the simulation would cause the engine to overheat diff --git a/src/core/Battle.spec.ts b/src/core/Battle.spec.ts index c3f1dda..0bf08c9 100644 --- a/src/core/Battle.spec.ts +++ b/src/core/Battle.spec.ts @@ -148,7 +148,7 @@ module TK.SpaceTac { let diff = battle.log.get(battle.log.count() - 1); if (diff instanceof EndBattleDiff) { check.notequals(diff.outcome.winner, null); - check.same(diff.outcome.winner, fleet2); + check.same(diff.outcome.winner, fleet2.id); } else { check.fail("Not an EndBattleDiff"); } diff --git a/src/core/Battle.ts b/src/core/Battle.ts index 682951c..67c88c8 100644 --- a/src/core/Battle.ts +++ b/src/core/Battle.ts @@ -349,10 +349,12 @@ module TK.SpaceTac { /** * Perform all battle checks to ensure the state is consistent + * + * Returns all applied diffs */ - performChecks(): void { + performChecks(): BaseBattleDiff[] { let checks = new BattleChecks(this); - checks.apply(); + return checks.apply(); } /** diff --git a/src/core/BattleCheats.spec.ts b/src/core/BattleCheats.spec.ts index 1b40c32..3fe3655 100644 --- a/src/core/BattleCheats.spec.ts +++ b/src/core/BattleCheats.spec.ts @@ -7,7 +7,7 @@ module TK.SpaceTac.Specs { cheats.win(); check.equals(battle.ended, true, "ended"); - check.same(nn(battle.outcome).winner, battle.fleets[0], "winner"); + check.same(nn(battle.outcome).winner, battle.fleets[0].id, "winner"); check.equals(any(battle.fleets[1].ships, ship => ship.alive), false, "all enemies dead"); }) @@ -18,7 +18,7 @@ module TK.SpaceTac.Specs { cheats.lose(); check.equals(battle.ended, true, "ended"); - check.same(nn(battle.outcome).winner, battle.fleets[1], "winner"); + check.same(nn(battle.outcome).winner, battle.fleets[1].id, "winner"); check.equals(any(battle.fleets[0].ships, ship => ship.alive), false, "all allies dead"); }) }) diff --git a/src/core/BattleChecks.ts b/src/core/BattleChecks.ts index d404dc6..5fc5ca8 100644 --- a/src/core/BattleChecks.ts +++ b/src/core/BattleChecks.ts @@ -13,7 +13,8 @@ module TK.SpaceTac { /** * Apply all the checks */ - apply(): void { + apply(): BaseBattleDiff[] { + let all: BaseBattleDiff[] = []; let diffs: BaseBattleDiff[]; let loops = 0; @@ -23,6 +24,7 @@ module TK.SpaceTac { if (diffs.length > 0) { //console.log("Battle checks diffs", diffs); this.battle.applyDiffs(diffs); + all = all.concat(diffs); } loops += 1; @@ -31,6 +33,8 @@ module TK.SpaceTac { break; } } while (diffs.length > 0); + + return all; } /** diff --git a/src/core/BattleOutcome.ts b/src/core/BattleOutcome.ts index b303888..bbde516 100644 --- a/src/core/BattleOutcome.ts +++ b/src/core/BattleOutcome.ts @@ -9,10 +9,10 @@ module TK.SpaceTac { draw: boolean // Victorious fleet - winner: Fleet | null + winner: RObjectId | null constructor(winner: Fleet | null) { - this.winner = winner; + this.winner = winner ? winner.id : null; this.draw = winner ? false : true; } @@ -21,7 +21,7 @@ module TK.SpaceTac { */ grantExperience(fleets: Fleet[]) { fleets.forEach(fleet => { - let winfactor = (fleet == this.winner) ? 0.03 : (this.draw ? 0.01 : 0.005); + let winfactor = (fleet.is(this.winner)) ? 0.03 : (this.draw ? 0.01 : 0.005); let enemies = flatten(fleets.filter(f => f !== fleet).map(f => f.ships)); let difficulty = sum(enemies.map(enemy => 100 + enemy.level.getExperience())); fleet.ships.forEach(ship => { diff --git a/src/core/Fleet.ts b/src/core/Fleet.ts index bce1616..c8a86ec 100644 --- a/src/core/Fleet.ts +++ b/src/core/Fleet.ts @@ -2,7 +2,7 @@ module TK.SpaceTac { /** * A fleet of ships, all belonging to the same player */ - export class Fleet { + export class Fleet extends RObject { // Fleet owner player: Player @@ -26,6 +26,8 @@ module TK.SpaceTac { // Create a fleet, bound to a player constructor(player = new Player()) { + super(); + this.player = player; this.name = player ? player.name : "Fleet"; this.ships = []; diff --git a/src/core/MoveFireSimulator.spec.ts b/src/core/MoveFireSimulator.spec.ts index a2565b0..d34152c 100644 --- a/src/core/MoveFireSimulator.spec.ts +++ b/src/core/MoveFireSimulator.spec.ts @@ -172,5 +172,36 @@ module TK.SpaceTac.Specs { check.equals(result.need_move, true); check.equals(result.move_location, new Target(ship.arena_x + 6, ship.arena_y)); }); + + test.case("simulates the results on a fake battle, to provide a list of expected diffs", check => { + let battle = TestTools.createBattle(); + let ship = battle.fleets[0].ships[0]; + let enemy = battle.fleets[1].ships[0]; + ship.setArenaPosition(100, 100); + enemy.setArenaPosition(300, 100); + TestTools.setShipModel(ship, 1, 1, 3); + TestTools.setShipModel(enemy, 2, 1); + let engine = TestTools.addEngine(ship, 80); + let weapon = TestTools.addWeapon(ship, 5, 1, 150); + let simulator = new MoveFireSimulator(ship); + let result = simulator.simulateAction(weapon, Target.newFromShip(enemy), 5); + let diffs = simulator.getExpectedDiffs(nn(ship.getBattle()), result); + check.equals(diffs, [ + new ShipActionUsedDiff(ship, engine, Target.newFromLocation(155, 100)), + new ShipValueDiff(ship, "power", -1), + new ShipMoveDiff(ship, ship.location, new ArenaLocationAngle(155, 100), engine), + new ShipActionUsedDiff(ship, weapon, Target.newFromShip(enemy)), + new ShipValueDiff(ship, "power", -1), + new ProjectileFiredDiff(ship, weapon, Target.newFromShip(enemy)), + new ShipDamageDiff(enemy, 2, 1, 0, 5), + new ShipValueDiff(enemy, "shield", -1), + new ShipValueDiff(enemy, "hull", -2), + new ShipDeathDiff(battle, enemy), + new EndBattleDiff(battle.fleets[0], 0) + ]); + + check.equals(enemy.getValue("hull"), 2); + check.equals(enemy.getValue("hull"), 2); + }); }); } diff --git a/src/core/MoveFireSimulator.ts b/src/core/MoveFireSimulator.ts index 2d01322..4c7d3bc 100644 --- a/src/core/MoveFireSimulator.ts +++ b/src/core/MoveFireSimulator.ts @@ -187,5 +187,25 @@ module TK.SpaceTac { return result; } + + /** + * Apply a move-fire simulation result, and predict the diffs it will apply on a battle + * + * The original battle passed as parameter will be duplicated, and not altered + */ + getExpectedDiffs(battle: Battle, simulation: MoveFireResult): BaseBattleDiff[] { + let sim_battle = duplicate(battle, TK.SpaceTac); + let sim_ship = nn(sim_battle.getShip(this.ship.id)); + let results: BaseBattleDiff[] = []; + simulation.parts.forEach(part => { + let diffs = part.action.getDiffs(sim_ship, battle, part.target); + results = results.concat(diffs); + sim_battle.applyDiffs(diffs); + + diffs = sim_battle.performChecks(); + results = results.concat(diffs); + }); + return results; + } } } diff --git a/src/core/StarLocation.ts b/src/core/StarLocation.ts index 5902d3f..a100ca2 100644 --- a/src/core/StarLocation.ts +++ b/src/core/StarLocation.ts @@ -173,7 +173,7 @@ module TK.SpaceTac { * Resolves the encounter from a battle outcome */ resolveEncounter(outcome: BattleOutcome) { - if (this.encounter && outcome.winner && outcome.winner != this.encounter) { + if (this.encounter && outcome.winner && !this.encounter.is(outcome.winner)) { this.clearEncounter(); } } diff --git a/src/core/diffs/EndBattleDiff.spec.ts b/src/core/diffs/EndBattleDiff.spec.ts index a2ae94c..0188543 100644 --- a/src/core/diffs/EndBattleDiff.spec.ts +++ b/src/core/diffs/EndBattleDiff.spec.ts @@ -17,7 +17,7 @@ module TK.SpaceTac.Specs { }, check => { check.equals(battle.ended, true, "battle is ended"); - check.same(nn(battle.outcome).winner, battle.fleets[1], "battle has an outcome"); + check.same(nn(battle.outcome).winner, battle.fleets[1].id, "battle has an outcome"); }, ]); }); diff --git a/src/core/missions/MissionPartEscort.spec.ts b/src/core/missions/MissionPartEscort.spec.ts index 436ffd5..1a9af8f 100644 --- a/src/core/missions/MissionPartEscort.spec.ts +++ b/src/core/missions/MissionPartEscort.spec.ts @@ -62,7 +62,7 @@ module TK.SpaceTac.Specs { ship.setDead(); battle.performChecks(); check.equals(battle.ended, true); - check.notsame(nn(battle.outcome).winner, fleet); + check.notsame(nn(battle.outcome).winner, fleet.id); }) }) } diff --git a/src/ui/battle/OutcomeDialog.ts b/src/ui/battle/OutcomeDialog.ts index 02ae47e..ea822a9 100644 --- a/src/ui/battle/OutcomeDialog.ts +++ b/src/ui/battle/OutcomeDialog.ts @@ -38,7 +38,7 @@ module TK.SpaceTac.UI { refreshContent(): void { let parent = this.battleview; let outcome = this.outcome; - let victory = outcome.winner && this.player.is(outcome.winner.player); + let victory = outcome.winner && this.player.fleet.is(outcome.winner); this.content.clear(); diff --git a/src/ui/battle/Targetting.ts b/src/ui/battle/Targetting.ts index 644b9f2..5e8f20f 100644 --- a/src/ui/battle/Targetting.ts +++ b/src/ui/battle/Targetting.ts @@ -16,7 +16,7 @@ module TK.SpaceTac.UI { // Simulated result simulation = new MoveFireResult() - effects: BaseBattleDiff[] = [] + simulated_diffs: BaseBattleDiff[] = [] // Move and fire lines drawn_info: UIGraphics @@ -27,6 +27,9 @@ module TK.SpaceTac.UI { impact_area: UIGraphics impact_indicators: UIContainer + // Diffs display + diffs_move: UIGraphics + // Collaborators to update actionbar: ActionBar range_hint: RangeHint @@ -46,18 +49,16 @@ module TK.SpaceTac.UI { builder = builder.in(this.container); // Visual effects - this.impact_area = builder.graphics("impact-area"); - this.impact_area.setVisible(false); - this.drawn_info = builder.graphics("lines"); - this.drawn_info.setVisible(false); + this.impact_area = builder.graphics("impact-area", 0, 0, false); + this.diffs_move = builder.graphics("effects-move"); + this.drawn_info = builder.graphics("lines", 0, 0, false); this.move_ghost = builder.image("common-transparent", 0, 0, true); this.move_ghost.setAlpha(0.8); this.move_ghost.setVisible(false); this.fire_arrow = builder.image("battle-hud-simulator-ok"); this.fire_arrow.setOrigin(1, 0.5); this.fire_arrow.setVisible(false); - this.impact_indicators = builder.container("impact-indicators"); - this.impact_indicators.setVisible(false); + this.impact_indicators = builder.container("impact-indicators", 0, 0, false); } /** @@ -200,6 +201,25 @@ module TK.SpaceTac.UI { } } + /** + * Update information about simulated diffs + */ + updateDiffsDisplay(): void { + this.diffs_move.clear(); + + this.simulated_diffs.forEach(diff => { + if (diff instanceof ShipMoveDiff) { + this.diffs_move.addLine({ + start: diff.start, + end: diff.end, + width: 4, + color: 0xFFFFFF, + alpha: 0.5 + }); + } + }); + } + /** * Update visual effects to show the simulation of current action/target */ @@ -259,6 +279,9 @@ module TK.SpaceTac.UI { this.fire_arrow.visible = true; this.impact_area.visible = false; } + + this.updateDiffsDisplay(); + this.container.visible = true; } else { this.container.visible = false; @@ -305,16 +328,13 @@ module TK.SpaceTac.UI { */ simulate(): void { if (this.ship && this.action && this.target) { - let ship = this.ship; - let simulator = new MoveFireSimulator(ship); + let battle = nn(this.ship.getBattle()); + let simulator = new MoveFireSimulator(this.ship); this.simulation = simulator.simulateAction(this.action, this.target, 1); - - this.effects = flatten(this.simulation.parts.map(part => - part.action.getDiffs(ship, nn(ship.getBattle()), part.target) - )); + this.simulated_diffs = simulator.getExpectedDiffs(battle, this.simulation); } else { this.simulation = new MoveFireResult(); - this.effects = []; + this.simulated_diffs = []; } }