From bb67458180a146c796cc9287a84833ca6de94a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Mon, 9 Jul 2018 12:28:18 +0200 Subject: [PATCH] WIP --- TODO.md | 1 + src/core/ArenaLocation.spec.ts | 16 ++++++ src/core/ArenaLocation.ts | 7 +++ src/core/MoveFireSimulator.ts | 10 +--- src/core/Target.ts | 5 +- src/core/TestTools.ts | 1 + src/core/actions/BaseAction.ts | 31 +++++------ src/core/actions/DeployDroneAction.spec.ts | 7 +-- src/core/actions/DeployDroneAction.ts | 9 ++-- src/core/actions/EndTurnAction.ts | 4 +- src/core/actions/MoveAction.spec.ts | 62 ++++++---------------- src/core/actions/MoveAction.ts | 4 +- src/core/actions/ToggleAction.ts | 4 +- src/core/actions/TriggerAction.spec.ts | 26 +-------- src/core/actions/TriggerAction.ts | 15 +++--- 15 files changed, 81 insertions(+), 121 deletions(-) diff --git a/TODO.md b/TODO.md index b2576b5..f8b9b81 100644 --- a/TODO.md +++ b/TODO.md @@ -49,6 +49,7 @@ Battle * 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 * [WIP] Add an hexagonal grid + * Use the grid for "border" exclusion areas * Use distances in units of this grid (add a multiplier to be compatible with no grid mode) * Fix repel effect to snap to grid (beware of two ships being moved to the same location!) * Fix engine targetting producing out-of-grid coordinates because of exclusion areas diff --git a/src/core/ArenaLocation.spec.ts b/src/core/ArenaLocation.spec.ts index b821e91..15a95ae 100644 --- a/src/core/ArenaLocation.spec.ts +++ b/src/core/ArenaLocation.spec.ts @@ -5,6 +5,22 @@ module TK.SpaceTac.Specs { check.nears(arenaAngle({ x: 0, y: 0 }, { x: 1, y: 1 }), Math.PI / 4); }) + test.case("checks if a location is in range of another", check => { + check.in("first order", check => { + check.equals(arenaInRange({ x: 0, y: 0 }, { x: 4, y: 4 }, 5), false, "<5"); + check.equals(arenaInRange({ x: 0, y: 0 }, { x: 4, y: 4 }, 8), true, "<8"); + }); + + check.in("second order", check => { + check.equals(arenaInRange({ x: 4, y: 4 }, { x: 0, y: 0 }, 5), false, "<5"); + check.equals(arenaInRange({ x: 4, y: 4 }, { x: 0, y: 0 }, 8), true, "<8"); + }); + + check.equals(arenaInRange({ x: 0, y: 0 }, { x: 0.99999999999999, y: 0 }, 1), true, "0.99999999999999"); + check.equals(arenaInRange({ x: 0, y: 0 }, { x: 1.00000000000001, y: 0 }, 1), true, "1.00000000000001"); + check.equals(arenaInRange({ x: 0, y: 0 }, { x: 1.000001, y: 0 }, 1), false, "1.000001"); + }) + test.case("computes an angular difference", check => { check.equals(angularDifference(0.5, 1.5), 1.0); check.nears(angularDifference(0.5, 1.5 + Math.PI * 6), 1.0); diff --git a/src/core/ArenaLocation.ts b/src/core/ArenaLocation.ts index 59eed53..9b3c05b 100644 --- a/src/core/ArenaLocation.ts +++ b/src/core/ArenaLocation.ts @@ -75,6 +75,13 @@ module TK.SpaceTac { return Math.sqrt(dx * dx + dy * dy); } + /** + * Check if a location is in range of another (accounting for rounding errors) + */ + export function arenaInRange(loc1: IArenaLocation, loc2: IArenaLocation, range: number): boolean { + return arenaDistance(loc1, loc2) - range < 1e-8; + } + /** * Check if a location is inside an area */ diff --git a/src/core/MoveFireSimulator.ts b/src/core/MoveFireSimulator.ts index c2c2ba4..11cdb88 100644 --- a/src/core/MoveFireSimulator.ts +++ b/src/core/MoveFireSimulator.ts @@ -63,14 +63,6 @@ module TK.SpaceTac { return first(actions, action => this.ship.actions.getCooldown(action).canUse()); } - /** - * Check that a move action can reach a given destination - */ - canMoveTo(action: MoveAction, target: Target): boolean { - let checked = action.checkLocationTarget(this.ship, target); - return checked != null && checked.x == target.x && checked.y == target.y; - } - /** * Get an iterator for scanning a circle */ @@ -104,7 +96,7 @@ module TK.SpaceTac { let candidates: [number, Target][] = []; iforeach(this.scanCircle(target.x, target.y, radius), candidate => { - if (this.canMoveTo(action, candidate)) { + if (action.checkLocationTarget(this.ship, candidate)) { candidates.push([candidate.getDistanceTo(this.ship.location), candidate]); } }); diff --git a/src/core/Target.ts b/src/core/Target.ts index 75a12ba..971a5c7 100644 --- a/src/core/Target.ts +++ b/src/core/Target.ts @@ -110,10 +110,7 @@ module TK.SpaceTac { // Check if a target is in range from a specific point isInRange(x: number, y: number, radius: number): boolean { - var dx = this.x - x; - var dy = this.y - y; - var length = Math.sqrt(dx * dx + dy * dy); - return (length <= radius); + return arenaInRange(this, new ArenaLocation(x, y), radius); } // Constraint a target, to be in a given range from a specific point diff --git a/src/core/TestTools.ts b/src/core/TestTools.ts index e26cb09..b4a2477 100644 --- a/src/core/TestTools.ts +++ b/src/core/TestTools.ts @@ -17,6 +17,7 @@ module TK.SpaceTac { var battle = new Battle(fleet1, fleet2); if (!hexgrid) { battle.grid = new PixelGrid(); + battle.placeShips(); } battle.ships.list().forEach(ship => TestTools.setShipModel(ship, 1, 0)); battle.play_order = fleet1.ships.concat(fleet2.ships); diff --git a/src/core/actions/BaseAction.ts b/src/core/actions/BaseAction.ts index 68c17fa..94424e9 100644 --- a/src/core/actions/BaseAction.ts +++ b/src/core/actions/BaseAction.ts @@ -236,9 +236,9 @@ module TK.SpaceTac { * * Will call checkLocationTarget or checkShipTarget by default */ - checkTarget(ship: Ship, target: Target): Target | null { + checkTarget(ship: Ship, target: Target): boolean { if (this.checkCannotBeApplied(ship)) { - return null; + return false; } else { if (target.isShip()) { return this.checkShipTarget(ship, target); @@ -248,16 +248,18 @@ module TK.SpaceTac { } } - // Method to reimplement to check if a space target is suitable - // Must return null if the target can't be applied, an altered target, or the original target - protected checkLocationTarget(ship: Ship, target: Target): Target | null { - return null; + /** + * Method to reimplement to check if a space target is suitable + */ + protected checkLocationTarget(ship: Ship, target: Target): boolean { + return false; } - // Method to reimplement to check if a ship target is suitable - // Must return null if the target can't be applied, an altered target, or the original target - protected checkShipTarget(ship: Ship, target: Target): Target | null { - return null; + /** + * Method to reimplement to check if a ship target is suitable + */ + protected checkShipTarget(ship: Ship, target: Target): boolean { + return false; } /** @@ -302,19 +304,18 @@ module TK.SpaceTac { return false; } - let checked_target = this.checkTarget(ship, target); - if (!checked_target) { + if (!this.checkTarget(ship, target)) { console.warn("Action rejected - invalid target", ship, this, target); return false; } - let cost = this.getPowerUsage(ship, checked_target); + let cost = this.getPowerUsage(ship, target); if (ship.getValue("power") < cost) { - console.warn("Action rejected - not enough power", ship, this, checked_target); + console.warn("Action rejected - not enough power", ship, this, target); return false; } - let diffs = this.getDiffs(ship, battle, checked_target); + let diffs = this.getDiffs(ship, battle, target); if (diffs.length) { battle.applyDiffs(diffs); return true; diff --git a/src/core/actions/DeployDroneAction.spec.ts b/src/core/actions/DeployDroneAction.spec.ts index 7937cb5..1347542 100644 --- a/src/core/actions/DeployDroneAction.spec.ts +++ b/src/core/actions/DeployDroneAction.spec.ts @@ -18,12 +18,13 @@ module TK.SpaceTac.Specs { let action = new DeployDroneAction("testdrone", { power: 0 }, { deploy_distance: 8 }); ship.actions.addCustom(action); - check.equals(action.checkTarget(ship, new Target(8, 0, null)), new Target(8, 0, null)); - check.equals(action.checkTarget(ship, new Target(12, 0, null)), new Target(8, 0, null)); + check.equals(action.checkTarget(ship, new Target(8, 0, null)), true); + check.equals(action.checkTarget(ship, new Target(12, 0, null)), false); + check.equals(action.checkTarget(ship, Target.newFromShip(ship)), false); let other = new Ship(); other.setArenaPosition(8, 0); - check.equals(action.checkTarget(ship, new Target(8, 0, other)), null); + check.equals(action.checkTarget(ship, new Target(8, 0, other)), false); }); test.case("deploys a new drone", check => { diff --git a/src/core/actions/DeployDroneAction.ts b/src/core/actions/DeployDroneAction.ts index 15ca893..d88adc7 100644 --- a/src/core/actions/DeployDroneAction.ts +++ b/src/core/actions/DeployDroneAction.ts @@ -67,9 +67,12 @@ module TK.SpaceTac { return result; } - checkLocationTarget(ship: Ship, target: Target): Target { - target = target.constraintInRange(ship.arena_x, ship.arena_y, this.deploy_distance); - return target; + checkShipTarget(ship: Ship, target: Target): boolean { + return false; + } + + checkLocationTarget(ship: Ship, target: Target): boolean { + return arenaInRange(ship.location, target, this.deploy_distance); } getSpecificDiffs(ship: Ship, battle: Battle, target: Target): BaseBattleDiff[] { diff --git a/src/core/actions/EndTurnAction.ts b/src/core/actions/EndTurnAction.ts index 6e2d0b0..f6b8f98 100644 --- a/src/core/actions/EndTurnAction.ts +++ b/src/core/actions/EndTurnAction.ts @@ -59,8 +59,8 @@ module TK.SpaceTac { } } - protected checkShipTarget(ship: Ship, target: Target): Target | null { - return ship.is(target.ship_id) ? target : null; + protected checkShipTarget(ship: Ship, target: Target): boolean { + return ship.is(target.ship_id); } getTargettingMode(ship: Ship): ActionTargettingMode { diff --git a/src/core/actions/MoveAction.spec.ts b/src/core/actions/MoveAction.spec.ts index 216407e..b386dae 100644 --- a/src/core/actions/MoveAction.spec.ts +++ b/src/core/actions/MoveAction.spec.ts @@ -3,6 +3,7 @@ module TK.SpaceTac.Specs { test.case("checks movement against remaining AP", check => { var ship = new Ship(); var battle = new Battle(ship.fleet); + battle.grid = new PixelGrid(); TestTools.setShipPlaying(battle, ship); ship.setValue("power", 6); ship.arena_x = 0; @@ -11,14 +12,13 @@ module TK.SpaceTac.Specs { ship.actions.addCustom(action); var result = action.checkTarget(ship, Target.newFromLocation(0, 20)); - check.equals(result, Target.newFromLocation(0, 20)); + check.equals(result, true); result = action.checkTarget(ship, Target.newFromLocation(0, 80)); - check.nears(nn(result).y, 59.9); + check.equals(result, false); - ship.setValue("power", 0); - result = action.checkTarget(ship, Target.newFromLocation(0, 80)); - check.equals(result, null); + result = action.checkTarget(ship, Target.newFromLocation(0, 0)); + check.equals(result, false); }); test.case("forbids targetting a ship", check => { @@ -37,11 +37,11 @@ module TK.SpaceTac.Specs { test.case("applies and reverts", check => { let battle = TestTools.createBattle(); let ship = battle.play_order[0]; - ship.setArenaPosition(500, 600) + ship.setArenaPosition(500, 600); TestTools.setShipModel(ship, 100, 0, 20); ship.setValue("power", 5); - let action = new MoveAction("Engine", { distance_per_power: 1 }); + let action = new MoveAction("Engine", { distance_per_power: 4 }); ship.actions.addCustom(action); TestTools.actionChain(check, battle, [ @@ -53,9 +53,9 @@ module TK.SpaceTac.Specs { check.equals(ship.getValue("power"), 5, "power"); }, check => { - check.nears(ship.arena_x, 504.382693, 5, "ship X"); - check.nears(ship.arena_y, 602.191346, 5, "ship Y"); - check.equals(ship.getValue("power"), 0, "power"); + check.equals(ship.arena_x, 510, "ship X"); + check.equals(ship.arena_y, 605, "ship Y"); + check.equals(ship.getValue("power"), 2, "power"); } ]); }); @@ -71,51 +71,19 @@ module TK.SpaceTac.Specs { var action = new MoveAction("Engine", { distance_per_power: 1000, safety_distance: 200 }); var result = action.checkLocationTarget(ship, Target.newFromLocation(700, 500)); - check.equals(result, Target.newFromLocation(700, 500)); + check.equals(result, true); result = action.checkLocationTarget(ship, Target.newFromLocation(800, 500)); - check.equals(result, Target.newFromLocation(800, 500)); + check.equals(result, true); result = action.checkLocationTarget(ship, Target.newFromLocation(900, 500)); - check.equals(result, Target.newFromLocation(800, 500)); + check.equals(result, false); result = action.checkLocationTarget(ship, Target.newFromLocation(1000, 500)); - check.equals(result, Target.newFromLocation(800, 500)); + check.equals(result, false); result = action.checkLocationTarget(ship, Target.newFromLocation(1200, 500)); - check.equals(result, Target.newFromLocation(1200, 500)); - }); - - test.case("exclusion radius is applied correctly over two ships", check => { - var battle = TestTools.createBattle(1, 2); - var ship = battle.fleets[0].ships[0]; - var enemy1 = battle.fleets[1].ships[0]; - var enemy2 = battle.fleets[1].ships[1]; - TestTools.setShipModel(ship, 100, 0, 100); - enemy1.setArenaPosition(0, 800); - enemy2.setArenaPosition(0, 1000); - - var action = new MoveAction("Engine", { distance_per_power: 1000, safety_distance: 150 }); - - var result = action.checkLocationTarget(ship, Target.newFromLocation(0, 1100)); - check.equals(result, Target.newFromLocation(0, 650)); - }); - - test.case("exclusion radius does not make the ship go back", check => { - var battle = TestTools.createBattle(1, 2); - var ship = battle.fleets[0].ships[0]; - var enemy1 = battle.fleets[1].ships[0]; - var enemy2 = battle.fleets[1].ships[1]; - TestTools.setShipModel(ship, 100, 0, 100); - enemy1.setArenaPosition(0, 500); - enemy2.setArenaPosition(0, 800); - - var action = new MoveAction("Engine", { distance_per_power: 1000, safety_distance: 600 }); - - let result = action.checkLocationTarget(ship, Target.newFromLocation(0, 1000)); - check.equals(result, null); - result = action.checkLocationTarget(ship, Target.newFromLocation(0, 1400)); - check.equals(result, Target.newFromLocation(0, 1400)); + check.equals(result, true); }); test.case("builds a textual description", check => { diff --git a/src/core/actions/MoveAction.ts b/src/core/actions/MoveAction.ts index 55bf9eb..27813ae 100644 --- a/src/core/actions/MoveAction.ts +++ b/src/core/actions/MoveAction.ts @@ -124,9 +124,9 @@ module TK.SpaceTac { return target.constraintInRange(ship.arena_x, ship.arena_y, max_distance); } - checkLocationTarget(ship: Ship, target: Target): Target | null { + checkLocationTarget(ship: Ship, target: Target): boolean { let fixed_target = this.applyExclusion(ship, this.applyReachableRange(ship, target)); - return fixed_target.getDistanceTo(target) < 1e-8 ? target : null; + return fixed_target.getDistanceTo(target) < 1e-8; } protected getSpecificDiffs(ship: Ship, battle: Battle, target: Target): BaseBattleDiff[] { diff --git a/src/core/actions/ToggleAction.ts b/src/core/actions/ToggleAction.ts index 2637536..5fc2695 100644 --- a/src/core/actions/ToggleAction.ts +++ b/src/core/actions/ToggleAction.ts @@ -67,8 +67,8 @@ module TK.SpaceTac { return result; } - checkShipTarget(ship: Ship, target: Target): Target | null { - return ship.is(target.ship_id) ? target : null; + checkShipTarget(ship: Ship, target: Target): boolean { + return ship.is(target.ship_id); } getSpecificDiffs(ship: Ship, battle: Battle, target: Target, apply_effects = true): BaseBattleDiff[] { diff --git a/src/core/actions/TriggerAction.spec.ts b/src/core/actions/TriggerAction.spec.ts index 05e4a18..4262814 100644 --- a/src/core/actions/TriggerAction.spec.ts +++ b/src/core/actions/TriggerAction.spec.ts @@ -38,30 +38,6 @@ module TK.SpaceTac.Specs { ]); }) - test.case("transforms ship target in location target, when the weapon has blast radius", check => { - let ship1 = new Ship(); - ship1.setArenaPosition(50, 10); - let ship2 = new Ship(); - ship2.setArenaPosition(150, 10); - let action = TestTools.addWeapon(ship1, 1, 0, 100, 30); - - let target = action.checkTarget(ship1, new Target(150, 10)); - check.equals(target, new Target(150, 10)); - - target = action.checkTarget(ship1, Target.newFromShip(ship2)); - check.equals(target, new Target(150, 10)); - - ship1.setArenaPosition(30, 10); - - target = action.checkTarget(ship1, Target.newFromShip(ship2)); - check.equals(target, new Target(130, 10)); - - ship1.setArenaPosition(0, 10); - - target = action.checkTarget(ship1, Target.newFromShip(ship2)); - check.equals(target, new Target(100, 10)); - }) - test.case("lists impacted ships", check => { let ship1 = new Ship(null, "S1"); ship1.setArenaPosition(10, 50); @@ -104,7 +80,7 @@ module TK.SpaceTac.Specs { let battle = TestTools.createBattle(); let ship = battle.play_order[0]; let action = TestTools.addWeapon(ship, 1, 0, 100, 30); - check.patch(action, "checkTarget", (ship: Ship, target: Target) => target); + check.patch(action, "checkTarget", (ship: Ship, target: Target) => true); check.equals(ship.arena_angle, 0); let result = action.apply(battle, ship, Target.newFromLocation(10, 20)); diff --git a/src/core/actions/TriggerAction.ts b/src/core/actions/TriggerAction.ts index b375518..555a2b8 100644 --- a/src/core/actions/TriggerAction.ts +++ b/src/core/actions/TriggerAction.ts @@ -124,27 +124,24 @@ module TK.SpaceTac { } } - checkLocationTarget(ship: Ship, target: Target): Target | null { + checkLocationTarget(ship: Ship, target: Target): boolean { if (target && (this.blast > 0 || this.angle > 0)) { - target = target.constraintInRange(ship.arena_x, ship.arena_y, this.range); - return target; + return arenaInRange(ship.location, target, this.range); } else { - return null; + return false; } } - checkShipTarget(ship: Ship, target: Target): Target | null { + checkShipTarget(ship: Ship, target: Target): boolean { if (this.range > 0 && ship.is(target.ship_id)) { // No self fire - return null; + return false; } else { // Check if target is in range if (this.blast > 0 || this.angle > 0) { return this.checkLocationTarget(ship, new Target(target.x, target.y)); - } else if (target.isInRange(ship.arena_x, ship.arena_y, this.range)) { - return target; } else { - return null; + return arenaInRange(ship.location, target, this.range); } } }