WIP
This commit is contained in:
parent
7e5572c1d9
commit
b4302da855
10
TODO.md
10
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)
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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, [
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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)];
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -300,7 +300,7 @@ module TK.SpaceTac.UI {
|
|||
move_action = <MoveAction>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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue