From b4302da8555bacd8a8964d151ffe734aaf27ac1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Mon, 9 Jul 2018 11:38:42 +0200 Subject: [PATCH] WIP --- TODO.md | 10 ++++++++- src/core/ArenaGrid.ts | 11 ++++++++++ src/core/Battle.spec.ts | 1 + src/core/Battle.ts | 6 ++---- src/core/MoveFireSimulator.spec.ts | 31 ++++++++++++++------------- src/core/MoveFireSimulator.ts | 22 +++++++++---------- src/core/Target.ts | 4 ++-- src/core/TestTools.ts | 5 ++++- src/core/actions/MoveAction.ts | 7 +++--- src/core/ai/Maneuver.spec.ts | 1 + src/core/ai/TacticalAIHelpers.spec.ts | 5 +++-- src/ui/TestGame.ts | 2 ++ src/ui/battle/Targetting.ts | 2 +- 13 files changed, 66 insertions(+), 41 deletions(-) diff --git a/TODO.md b/TODO.md index 7db6056..b2576b5 100644 --- a/TODO.md +++ b/TODO.md @@ -33,12 +33,14 @@ Character sheet --------------- * Improve attribute tooltips +* Fix action tooltips showing battle information ("not enough power"...) * Implement sliders for personality traits * Center the portraits when there are less than 5 Battle ------ +* Fix tactical information being hidden when changing selected action * 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) @@ -46,7 +48,13 @@ Battle * 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 -* Add an hexagonal grid (optional, may be enforced only on mobile) and work in units of this grid +* [WIP] Add an hexagonal grid + * 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 + * Display the grid in targetting mode, with colors (replaces range hint) + * Fix move-fire simulator's scanCircle + * Fix tactical AI scanArea * Add engine trail effect, and sound * Find incentives to move from starting position (permanent drones or anomalies?) * Mark targetting in error when target is refused by the action (there is already an arrow for this) diff --git a/src/core/ArenaGrid.ts b/src/core/ArenaGrid.ts index b9a6aef..e074871 100644 --- a/src/core/ArenaGrid.ts +++ b/src/core/ArenaGrid.ts @@ -8,6 +8,17 @@ module TK.SpaceTac { snap(loc: IArenaLocation): IArenaLocation; } + /** + * Pixel grid + * + * This will only round the coordinates to the pixels + */ + export class PixelGrid implements IArenaGrid { + snap(loc: IArenaLocation): IArenaLocation { + return new ArenaLocation(Math.round(loc.x), Math.round(loc.y)); + } + } + /** * Hexagonal unbounded arena grid * diff --git a/src/core/Battle.spec.ts b/src/core/Battle.spec.ts index 0bf08c9..aa0e7f6 100644 --- a/src/core/Battle.spec.ts +++ b/src/core/Battle.spec.ts @@ -36,6 +36,7 @@ module TK.SpaceTac { var ship5 = new Ship(fleet2, "F2S2"); var battle = new Battle(fleet1, fleet2, 1000, 500); + battle.grid = new PixelGrid(); battle.placeShips(); check.nears(ship1.arena_x, 250); diff --git a/src/core/Battle.ts b/src/core/Battle.ts index 4463914..bfb930b 100644 --- a/src/core/Battle.ts +++ b/src/core/Battle.ts @@ -4,7 +4,7 @@ module TK.SpaceTac { */ export class Battle { // Grid for the arena - grid?: IArenaGrid + grid: IArenaGrid // Battle outcome, if the battle has ended outcome: BattleOutcome | null = null @@ -309,9 +309,7 @@ module TK.SpaceTac { y -= dy * total_length * 0.5; for (var i = 0; i < fleet.ships.length; i++) { let location = new ArenaLocation(x + i * dx * spacing, y + i * dy * spacing); - if (this.grid) { - location = this.grid.snap(location); - } + location = this.grid.snap(location); fleet.ships[i].setArenaPosition(location.x, location.y); fleet.ships[i].setArenaFacingAngle(facing_angle); } diff --git a/src/core/MoveFireSimulator.spec.ts b/src/core/MoveFireSimulator.spec.ts index d34152c..28c3d1a 100644 --- a/src/core/MoveFireSimulator.spec.ts +++ b/src/core/MoveFireSimulator.spec.ts @@ -6,13 +6,13 @@ module TK.SpaceTac.Specs { TestTools.setShipModel(ship, 100, 0, ship_ap); TestTools.addEngine(ship, engine_distance); let action = new TriggerAction("weapon", { power: weapon_ap, range: distance }); - let simulator = new MoveFireSimulator(ship); + let simulator = new MoveFireSimulator(ship, new PixelGrid()); return [ship, simulator, action]; } test.case("finds a suitable engine to make an approach", check => { let ship = new Ship(); - let simulator = new MoveFireSimulator(ship); + let simulator = new MoveFireSimulator(ship, new PixelGrid()); check.equals(simulator.findEngine(), null, "no engine"); let engine1 = TestTools.addEngine(ship, 100); engine1.configureCooldown(1, 1); @@ -44,11 +44,11 @@ module TK.SpaceTac.Specs { test.case("can't fire when in range, but not enough AP", check => { let [ship, simulator, action] = simpleWeaponCase(10, 2, 3); let result = simulator.simulateAction(action, new Target(ship.arena_x + 5, ship.arena_y, null)); - check.same(result.success, true, 'success'); - check.same(result.need_move, false, 'need_move'); - check.same(result.need_fire, true, 'need_fire'); - check.same(result.can_fire, false, 'can_fire'); - check.same(result.total_fire_ap, 3, 'total_fire_ap'); + check.equals(result.success, true, 'success'); + check.equals(result.need_move, false, 'need_move'); + check.equals(result.need_fire, true, 'need_fire'); + check.equals(result.can_fire, false, 'can_fire'); + check.equals(result.total_fire_ap, 3, 'total_fire_ap'); check.equals(result.parts, [ { action: action, target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 3, possible: false } @@ -58,14 +58,15 @@ module TK.SpaceTac.Specs { test.case("moves straight to get within range", check => { let [ship, simulator, action] = simpleWeaponCase(); let result = simulator.simulateAction(action, new Target(ship.arena_x + 15, ship.arena_y, null)); - check.same(result.success, true, 'success'); - check.same(result.need_move, true, 'need_move'); - check.same(result.can_end_move, true, 'can_end_move'); + check.equals(result.success, true, 'success'); + check.equals(result.need_move, true, 'need_move'); + check.equals(result.can_move, true, 'can_move'); + check.equals(result.can_end_move, true, 'can_end_move'); check.equals(result.move_location, new Target(ship.arena_x + 5, ship.arena_y, null)); check.equals(result.total_move_ap, 1); - check.same(result.need_fire, true, 'need_fire'); - check.same(result.can_fire, true, 'can_fire'); - check.same(result.total_fire_ap, 3, 'total_fire_ap'); + check.equals(result.need_fire, true, 'need_fire'); + check.equals(result.can_fire, true, 'can_fire'); + check.equals(result.total_fire_ap, 3, 'total_fire_ap'); let move_action = ship.actions.listAll().filter(action => action instanceof MoveAction)[0]; check.equals(result.parts, [ @@ -75,7 +76,7 @@ module TK.SpaceTac.Specs { }); test.case("scans a circle for move targets", check => { - let simulator = new MoveFireSimulator(new Ship()); + let simulator = new MoveFireSimulator(new Ship(), new PixelGrid()); let result = simulator.scanCircle(50, 30, 10, 1, 1); check.equals(imaterialize(result), [ @@ -183,7 +184,7 @@ module TK.SpaceTac.Specs { 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 simulator = new MoveFireSimulator(ship, new PixelGrid()); let result = simulator.simulateAction(weapon, Target.newFromShip(enemy), 5); let diffs = simulator.getExpectedDiffs(nn(ship.getBattle()), result); check.equals(diffs, [ diff --git a/src/core/MoveFireSimulator.ts b/src/core/MoveFireSimulator.ts index d6399f4..c2c2ba4 100644 --- a/src/core/MoveFireSimulator.ts +++ b/src/core/MoveFireSimulator.ts @@ -50,7 +50,7 @@ module TK.SpaceTac { // Playing ship private ship: Ship, // Coordinates grid - private grid?: IArenaGrid + private grid: IArenaGrid ) { } /** @@ -129,12 +129,8 @@ module TK.SpaceTac { let move_action: MoveAction | null = null; result.move_location = Target.newFromShip(this.ship); if (action instanceof MoveAction) { - let corrected_target = action.applyReachableRange(this.ship, target, move_margin); - corrected_target = action.applyExclusion(this.ship, corrected_target); - if (corrected_target) { - result.need_move = target.getDistanceTo(this.ship.location) > 0; - move_target = corrected_target; - } + result.need_move = true; + move_target = target; move_action = action; } else { move_action = this.findEngine(); @@ -152,15 +148,19 @@ module TK.SpaceTac { } } } - if (move_target && arenaDistance(move_target, this.ship.location) < 0.000001) { - result.need_move = false; + if (move_target && move_action && result.need_move) { + if (arenaDistance(move_target, this.ship.location) < 1e-8) { + result.need_move = false; + } else { + result.can_move = bool(move_action.checkTarget(this.ship, move_target)); + } } // Check move AP if (result.need_move && move_target && move_action) { result.total_move_ap = move_action.getPowerUsage(this.ship, move_target); - result.can_move = ap > 0; - result.can_end_move = result.total_move_ap <= ap; + result.can_move = result.can_move && (ap > 0); + result.can_end_move = result.can_move && (result.total_move_ap <= ap); result.move_location = move_target; // TODO Split in "this turn" part and "next turn" part if needed result.parts.push({ action: move_action, target: move_target, ap: result.total_move_ap, possible: result.can_move }); diff --git a/src/core/Target.ts b/src/core/Target.ts index 73736d5..75a12ba 100644 --- a/src/core/Target.ts +++ b/src/core/Target.ts @@ -67,8 +67,8 @@ module TK.SpaceTac { /** * Snap to battle grid */ - snap(grid?: IArenaGrid): Target { - if (!grid || this.ship_id) { + snap(grid: IArenaGrid): Target { + if (this.ship_id) { return this; } else { let location = grid.snap(this); diff --git a/src/core/TestTools.ts b/src/core/TestTools.ts index ee7e7af..e26cb09 100644 --- a/src/core/TestTools.ts +++ b/src/core/TestTools.ts @@ -3,7 +3,7 @@ module TK.SpaceTac { export class TestTools { // Create a battle between two fleets, with a fixed play order (owned ships, then enemy ships) - static createBattle(own_ships = 1, enemy_ships = 1): Battle { + static createBattle(own_ships = 1, enemy_ships = 1, hexgrid = false): Battle { var fleet1 = new Fleet(new Player("Attacker")); var fleet2 = new Fleet(new Player("Defender")); @@ -15,6 +15,9 @@ module TK.SpaceTac { } var battle = new Battle(fleet1, fleet2); + if (!hexgrid) { + battle.grid = new PixelGrid(); + } battle.ships.list().forEach(ship => TestTools.setShipModel(ship, 1, 0)); battle.play_order = fleet1.ships.concat(fleet2.ships); battle.setPlayingShip(battle.play_order[0]); diff --git a/src/core/actions/MoveAction.ts b/src/core/actions/MoveAction.ts index 5598041..55bf9eb 100644 --- a/src/core/actions/MoveAction.ts +++ b/src/core/actions/MoveAction.ts @@ -125,13 +125,12 @@ module TK.SpaceTac { } checkLocationTarget(ship: Ship, target: Target): Target | null { - target = this.applyReachableRange(ship, target); - target = this.applyExclusion(ship, target); - return target.getDistanceTo(ship.location) > 0 ? target : null; + let fixed_target = this.applyExclusion(ship, this.applyReachableRange(ship, target)); + return fixed_target.getDistanceTo(target) < 1e-8 ? target : null; } protected getSpecificDiffs(ship: Ship, battle: Battle, target: Target): BaseBattleDiff[] { - let angle = (arenaDistance(target, ship.location) < 0.00001) ? ship.arena_angle : arenaAngle(ship.location, target); + let angle = (arenaDistance(target, ship.location) < 1e-8) ? ship.arena_angle : arenaAngle(ship.location, target); let destination = new ArenaLocationAngle(target.x, target.y, angle); return [new ShipMoveDiff(ship, ship.location, destination, this)]; } diff --git a/src/core/ai/Maneuver.spec.ts b/src/core/ai/Maneuver.spec.ts index 55d2903..584531b 100644 --- a/src/core/ai/Maneuver.spec.ts +++ b/src/core/ai/Maneuver.spec.ts @@ -2,6 +2,7 @@ module TK.SpaceTac.Specs { testing("Maneuver", test => { test.case("uses move-fire simulation to build a list of battle diffs", check => { let battle = new Battle(); + battle.grid = new PixelGrid(); let ship1 = battle.fleets[0].addShip(); let ship2 = battle.fleets[1].addShip(); let ship3 = battle.fleets[1].addShip(); diff --git a/src/core/ai/TacticalAIHelpers.spec.ts b/src/core/ai/TacticalAIHelpers.spec.ts index 8680e87..7121e0c 100644 --- a/src/core/ai/TacticalAIHelpers.spec.ts +++ b/src/core/ai/TacticalAIHelpers.spec.ts @@ -108,6 +108,7 @@ module TK.SpaceTac.Specs { test.case("evaluates turn cost", check => { let battle = new Battle(); + battle.grid = new PixelGrid(); let ship = battle.fleets[0].addShip(); let weapon = TestTools.addWeapon(ship, 50, 5, 100); let action = weapon; @@ -202,8 +203,8 @@ module TK.SpaceTac.Specs { TestTools.addEngine(ship, 100); let weapon = TestTools.addWeapon(ship, 100, 1, 100, 10); - let maneuver = new Maneuver(ship, weapon, Target.newFromLocation(200, 0), 0.5); - check.nears(maneuver.simulation.move_location.x, 100.5, 1); + let maneuver = new Maneuver(ship, weapon, Target.newFromLocation(200, 0)); + check.equals(maneuver.simulation.move_location.x, 100); check.equals(maneuver.simulation.move_location.y, 0); check.equals(TacticalAIHelpers.evaluateClustering(ship, battle, maneuver), 0); diff --git a/src/ui/TestGame.ts b/src/ui/TestGame.ts index 4654337..8c72dc5 100644 --- a/src/ui/TestGame.ts +++ b/src/ui/TestGame.ts @@ -82,6 +82,8 @@ module TK.SpaceTac.UI.Specs { view.splash = false; let battle = Battle.newQuickRandom(); + battle.grid = new PixelGrid(); + battle.placeShips(); let player = new Player(); nn(battle.playing_ship).fleet.setPlayer(player); diff --git a/src/ui/battle/Targetting.ts b/src/ui/battle/Targetting.ts index 50b5e43..891dccb 100644 --- a/src/ui/battle/Targetting.ts +++ b/src/ui/battle/Targetting.ts @@ -300,7 +300,7 @@ module TK.SpaceTac.UI { move_action = last_move.action; } } else { - let engine = new MoveFireSimulator(this.ship).findEngine(); + let engine = new MoveFireSimulator(this.ship, new PixelGrid()).findEngine(); if (engine) { move_action = engine; }