1
0
Fork 0

Added log when an action cannot be applied

This commit is contained in:
Michaël Lemaire 2017-03-07 20:27:46 +01:00
parent cd7334babf
commit 144eb56537
21 changed files with 181 additions and 128 deletions

2
TODO
View file

@ -1,3 +1,4 @@
* Enable strict null checking in typescript
* Ensure that tweens and particle emitters get destroyed once animation is done (or view changes) * Ensure that tweens and particle emitters get destroyed once animation is done (or view changes)
* Highlight ships that will be included as target of current action * Highlight ships that will be included as target of current action
* Controls: Do not focus on ship while targetting for area effects (dissociate hover and target) * Controls: Do not focus on ship while targetting for area effects (dissociate hover and target)
@ -27,7 +28,6 @@
* TacticalAI: allow to play several moves in the same turn * TacticalAI: allow to play several moves in the same turn
* TacticalAI: add pauses to not play too quickly * TacticalAI: add pauses to not play too quickly
* TacticalAI: replace BullyAI * TacticalAI: replace BullyAI
* AIDuel: fix first AI always winning when two identical AIs are selected
* Add a defeat screen (game over for now) * Add a defeat screen (game over for now)
* Add a victory screen, with loot display * Add a victory screen, with loot display
* Add retreat from battle * Add retreat from battle

View file

@ -89,5 +89,14 @@ module TS.SpaceTac.Specs {
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 18, ship.arena_y, null), ap: 2 } { action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 18, ship.arena_y, null), ap: 2 }
]); ]);
}); });
it("does nothing if trying to move in the same spot", function () {
let [ship, simulator, action] = simpleWeaponCase();
let result = simulator.simulateAction(ship.listEquipment(SlotType.Engine)[0].action, new Target(ship.arena_x, ship.arena_y, null));
expect(result.success).toBe(true);
expect(result.need_move).toBe(false);
expect(result.need_fire).toBe(false);
expect(result.parts).toEqual([]);
});
}); });
} }

View file

@ -59,27 +59,32 @@ module TS.SpaceTac {
* Simulate a given action on a given valid target. * Simulate a given action on a given valid target.
*/ */
simulateAction(action: BaseAction, target: Target): MoveFireResult { simulateAction(action: BaseAction, target: Target): MoveFireResult {
let result = new MoveFireResult();
let dx = target.x - this.ship.arena_x; let dx = target.x - this.ship.arena_x;
let dy = target.y - this.ship.arena_y; let dy = target.y - this.ship.arena_y;
let distance = Math.sqrt(dx * dx + dy * dy); let distance = Math.sqrt(dx * dx + dy * dy);
let result = new MoveFireResult();
let ap = this.ship.values.power.get(); let ap = this.ship.values.power.get();
let action_radius = action.getRangeRadius(this.ship); let action_radius = action.getRangeRadius(this.ship);
if (action instanceof MoveAction || distance > action_radius) { if (action instanceof MoveAction || distance > action_radius) {
result.need_move = true;
let move_distance = action instanceof MoveAction ? distance : distance - action_radius; let move_distance = action instanceof MoveAction ? distance : distance - action_radius;
let move_target = new Target(this.ship.arena_x + dx * move_distance / distance, this.ship.arena_y + dy * move_distance / distance, null); if (move_distance > 0.000001) {
let engine = this.findBestEngine(); result.need_move = true;
if (engine) {
result.total_move_ap = engine.action.getActionPointsUsage(this.ship.getBattle(), this.ship, move_target);
result.can_move = ap > 0;
result.can_end_move = result.total_move_ap <= ap;
result.move_location = move_target;
result.parts.push({ action: engine.action, target: move_target, ap: result.total_move_ap });
ap -= result.total_move_ap; let move_target = new Target(this.ship.arena_x + dx * move_distance / distance, this.ship.arena_y + dy * move_distance / distance, null);
distance -= move_distance; let engine = this.findBestEngine();
if (engine) {
result.total_move_ap = engine.action.getActionPointsUsage(this.ship, move_target);
result.can_move = ap > 0;
result.can_end_move = result.total_move_ap <= ap;
result.move_location = move_target;
result.parts.push({ action: engine.action, target: move_target, ap: result.total_move_ap });
ap -= result.total_move_ap;
distance -= move_distance;
}
} }
} }
@ -87,7 +92,7 @@ module TS.SpaceTac {
result.success = true; result.success = true;
if (!(action instanceof MoveAction)) { if (!(action instanceof MoveAction)) {
result.need_fire = true; result.need_fire = true;
result.total_fire_ap = action.getActionPointsUsage(this.ship.getBattle(), this.ship, target); result.total_fire_ap = action.getActionPointsUsage(this.ship, target);
result.can_fire = result.total_fire_ap <= ap; result.can_fire = result.total_fire_ap <= ap;
result.fire_location = target; result.fire_location = target;
result.parts.push({ action: action, target: target, ap: result.total_fire_ap }); result.parts.push({ action: action, target: target, ap: result.total_fire_ap });

View file

@ -8,22 +8,22 @@ module TS.SpaceTac {
ship.addSlot(SlotType.Hull).attach(equipment); ship.addSlot(SlotType.Hull).attach(equipment);
ship.values.power.setMaximal(10); ship.values.power.setMaximal(10);
expect(action.canBeUsed(null, ship)).toBe(false); expect(action.checkCannotBeApplied(ship)).toBe("not enough power");
ship.values.power.set(5); ship.values.power.set(5);
expect(action.canBeUsed(null, ship)).toBe(true); expect(action.checkCannotBeApplied(ship)).toBe(null);
expect(action.canBeUsed(null, ship, 4)).toBe(true); expect(action.checkCannotBeApplied(ship, 4)).toBe(null);
expect(action.canBeUsed(null, ship, 3)).toBe(true); expect(action.checkCannotBeApplied(ship, 3)).toBe(null);
expect(action.canBeUsed(null, ship, 2)).toBe(false); expect(action.checkCannotBeApplied(ship, 2)).toBe("not enough power");
ship.values.power.set(3); ship.values.power.set(3);
expect(action.canBeUsed(null, ship)).toBe(true); expect(action.checkCannotBeApplied(ship)).toBe(null);
ship.values.power.set(2); ship.values.power.set(2);
expect(action.canBeUsed(null, ship)).toBe(false); expect(action.checkCannotBeApplied(ship)).toBe("not enough power");
}); });
}); });
} }

View file

@ -21,12 +21,18 @@ module TS.SpaceTac {
this.equipment = equipment; this.equipment = equipment;
} }
// Check basic conditions to know if the ship can use this action at all /**
// Method to reimplement to set conditions * Check basic conditions to know if the ship can use this action at all
canBeUsed(battle: Battle, ship: Ship, remaining_ap: number = null): boolean { *
* Method to extend to set conditions
*
* Returns an informative message indicating why the action cannot be used, null otherwise
*/
checkCannotBeApplied(ship: Ship, remaining_ap: number = null): string | null {
let battle = ship.getBattle();
if (battle && battle.playing_ship !== ship) { if (battle && battle.playing_ship !== ship) {
// Ship is not playing // Ship is not playing
return false; return "ship not playing";
} }
// Check AP usage // Check AP usage
@ -34,11 +40,15 @@ module TS.SpaceTac {
remaining_ap = ship.values.power.get(); remaining_ap = ship.values.power.get();
} }
var ap_usage = this.equipment ? this.equipment.ap_usage : 0; var ap_usage = this.equipment ? this.equipment.ap_usage : 0;
return remaining_ap >= ap_usage; if (remaining_ap >= ap_usage) {
return null;
} else {
return "not enough power";
}
} }
// Get the number of action points the action applied to a target would use // Get the number of action points the action applied to a target would use
getActionPointsUsage(battle: Battle, ship: Ship, target: Target): number { getActionPointsUsage(ship: Ship, target: Target): number {
if (this.equipment) { if (this.equipment) {
return this.equipment.ap_usage; return this.equipment.ap_usage;
} else { } else {
@ -66,14 +76,14 @@ module TS.SpaceTac {
// Method to check if a target is applicable for this action // Method to check if a target is applicable for this action
// Will call checkLocationTarget or checkShipTarget by default // Will call checkLocationTarget or checkShipTarget by default
checkTarget(battle: Battle, ship: Ship, target: Target): Target { checkTarget(ship: Ship, target: Target): Target {
if (!this.canBeUsed(battle, ship)) { if (this.checkCannotBeApplied(ship)) {
return null; return null;
} else if (target) { } else if (target) {
if (target.ship) { if (target.ship) {
return this.checkShipTarget(battle, ship, target); return this.checkShipTarget(ship, target);
} else { } else {
return this.checkLocationTarget(battle, ship, target); return this.checkLocationTarget(ship, target);
} }
} else { } else {
return null; return null;
@ -82,38 +92,42 @@ module TS.SpaceTac {
// Method to reimplement to check if a space target is applicable // Method to reimplement to check if a space target is applicable
// Must return null if the target can't be applied, an altered target, or the original target // Must return null if the target can't be applied, an altered target, or the original target
checkLocationTarget(battle: Battle, ship: Ship, target: Target): Target { checkLocationTarget(ship: Ship, target: Target): Target {
return null; return null;
} }
// Method to reimplement to check if a ship target is applicable // Method to reimplement to check if a ship target is applicable
// Must return null if the target can't be applied, an altered target, or the original target // Must return null if the target can't be applied, an altered target, or the original target
checkShipTarget(battle: Battle, ship: Ship, target: Target): Target { checkShipTarget(ship: Ship, target: Target): Target {
return null; return null;
} }
// Apply an action, returning true if it was successful // Apply an action, returning true if it was successful
apply(battle: Battle, ship: Ship, target: Target): boolean { apply(ship: Ship, target: Target): boolean {
if (this.canBeUsed(battle, ship)) { let reject = this.checkCannotBeApplied(ship);
target = this.checkTarget(battle, ship, target); if (reject == null) {
target = this.checkTarget(ship, target);
if (!target && this.needs_target) { if (!target && this.needs_target) {
console.warn("Action rejected - no target selected", ship, this, target);
return false; return false;
} }
let cost = this.getActionPointsUsage(battle, ship, target); let cost = this.getActionPointsUsage(ship, target);
if (!ship.useActionPoints(cost)) { if (!ship.useActionPoints(cost)) {
console.warn("Action rejected - not enough power", ship, this, target);
return false; return false;
} }
this.customApply(battle, ship, target); this.customApply(ship, target);
return true; return true;
} else { } else {
console.warn(`Action rejected - ${reject}`, ship, this, target);
return false; return false;
} }
} }
// Method to reimplement to apply a action // Method to reimplement to apply a action
protected customApply(battle: Battle, ship: Ship, target: Target) { protected customApply(ship: Ship, target: Target) {
} }
} }
} }

View file

@ -20,18 +20,18 @@ module TS.SpaceTac {
equipment.ap_usage = 0; equipment.ap_usage = 0;
let action = new DeployDroneAction(equipment); let action = new DeployDroneAction(equipment);
expect(action.checkTarget(null, ship, new Target(8, 0, null))).toEqual(new Target(8, 0, null)); expect(action.checkTarget(ship, new Target(8, 0, null))).toEqual(new Target(8, 0, null));
expect(action.checkTarget(null, ship, new Target(12, 0, null))).toEqual(new Target(8, 0, null)); expect(action.checkTarget(ship, new Target(12, 0, null))).toEqual(new Target(8, 0, null));
let other = new Ship(); let other = new Ship();
other.setArenaPosition(8, 0); other.setArenaPosition(8, 0);
expect(action.checkTarget(null, ship, new Target(8, 0, other))).toBeNull(); expect(action.checkTarget(ship, new Target(8, 0, other))).toBeNull();
}); });
it("deploys a new drone", function () { it("deploys a new drone", function () {
let ship = new Ship();
ship.setArenaPosition(0, 0);
let battle = new Battle(); let battle = new Battle();
let ship = battle.fleets[0].addShip();
ship.setArenaPosition(0, 0);
battle.playing_ship = ship; battle.playing_ship = ship;
TestTools.setShipAP(ship, 3); TestTools.setShipAP(ship, 3);
let equipment = new Equipment(); let equipment = new Equipment();
@ -43,7 +43,9 @@ module TS.SpaceTac {
equipment.target_effects.push(new DamageEffect(50)); equipment.target_effects.push(new DamageEffect(50));
let action = new DeployDroneAction(equipment); let action = new DeployDroneAction(equipment);
let result = action.apply(battle, ship, new Target(5, 0, null)); battle.log.clear();
battle.log.addFilter("value");
let result = action.apply(ship, new Target(5, 0, null));
expect(result).toBe(true); expect(result).toBe(true);
expect(battle.drones.length).toBe(1); expect(battle.drones.length).toBe(1);

View file

@ -9,20 +9,24 @@ module TS.SpaceTac {
super("deploy-" + equipment.code, "Deploy", true, equipment); super("deploy-" + equipment.code, "Deploy", true, equipment);
} }
checkLocationTarget(battle: Battle, ship: Ship, target: Target): Target { checkLocationTarget(ship: Ship, target: Target): Target {
// TODO Not too close to other ships and drones // TODO Not too close to other ships and drones
target = target.constraintInRange(ship.arena_x, ship.arena_y, this.equipment.distance); target = target.constraintInRange(ship.arena_x, ship.arena_y, this.equipment.distance);
return target; return target;
} }
protected customApply(battle: Battle, ship: Ship, target: Target) { protected customApply(ship: Ship, target: Target) {
let drone = new Drone(ship, this.equipment.code); let drone = new Drone(ship, this.equipment.code);
drone.x = target.x; drone.x = target.x;
drone.y = target.y; drone.y = target.y;
drone.radius = this.equipment.blast; drone.radius = this.equipment.blast;
drone.effects = this.equipment.target_effects; drone.effects = this.equipment.target_effects;
drone.duration = this.equipment.duration; drone.duration = this.equipment.duration;
battle.addDrone(drone);
let battle = ship.getBattle();
if (battle) {
battle.addDrone(drone);
}
} }
} }
} }

View file

@ -1,14 +1,18 @@
module TS.SpaceTac.Specs { module TS.SpaceTac.Specs {
describe("EndTurnAction", () => { describe("EndTurnAction", () => {
it("can't be applied to non-playing ship", () => { it("can't be applied to non-playing ship", () => {
spyOn(console, "warn").and.stub();
var battle = Battle.newQuickRandom(); var battle = Battle.newQuickRandom();
var action = new EndTurnAction(); var action = new EndTurnAction();
expect(action.canBeUsed(battle, battle.play_order[0])).toBe(true); expect(action.checkCannotBeApplied(battle.play_order[0])).toBe(null);
expect(action.canBeUsed(battle, battle.play_order[1])).toBe(false); expect(action.checkCannotBeApplied(battle.play_order[1])).toBe("ship not playing");
var result = action.apply(battle, battle.play_order[1], null); var result = action.apply(battle.play_order[1], null);
expect(result).toBe(false); expect(result).toBe(false);
expect(console.warn).toHaveBeenCalledWith("Action rejected - ship not playing", battle.play_order[1], action, null);
}); });
it("ends turn when applied", () => { it("ends turn when applied", () => {
@ -17,7 +21,7 @@ module TS.SpaceTac.Specs {
expect(battle.playing_ship_index).toBe(0); expect(battle.playing_ship_index).toBe(0);
var result = action.apply(battle, battle.play_order[0], null); var result = action.apply(battle.play_order[0], null);
expect(result).toBe(true); expect(result).toBe(true);
expect(battle.playing_ship_index).toBe(1); expect(battle.playing_ship_index).toBe(1);
}); });

View file

@ -5,9 +5,13 @@ module TS.SpaceTac {
super("endturn", "End ship's turn", false); super("endturn", "End ship's turn", false);
} }
protected customApply(battle: Battle, ship: Ship, target: Target) { protected customApply(ship: Ship, target: Target) {
ship.endTurn(); ship.endTurn();
battle.advanceToNextShip();
let battle = ship.getBattle();
if (battle) {
battle.advanceToNextShip();
}
} }
} }
} }

View file

@ -39,7 +39,7 @@ module TS.SpaceTac {
battle.playing_ship = ship; battle.playing_ship = ship;
fleet.setBattle(battle); fleet.setBattle(battle);
action.apply(battle, ship, Target.newFromLocation(50, 50)); action.apply(ship, Target.newFromLocation(50, 50));
expect(mock_apply).toHaveBeenCalledTimes(1); expect(mock_apply).toHaveBeenCalledTimes(1);
expect(mock_apply).toHaveBeenCalledWith(ship2); expect(mock_apply).toHaveBeenCalledWith(ship2);
}); });

View file

@ -12,7 +12,7 @@ module TS.SpaceTac {
this.can_target_space = can_target_space; this.can_target_space = can_target_space;
} }
checkLocationTarget(battle: Battle, ship: Ship, target: Target): Target { checkLocationTarget(ship: Ship, target: Target): Target {
if (this.can_target_space) { if (this.can_target_space) {
target = target.constraintInRange(ship.arena_x, ship.arena_y, this.equipment.distance); target = target.constraintInRange(ship.arena_x, ship.arena_y, this.equipment.distance);
return target; return target;
@ -21,7 +21,7 @@ module TS.SpaceTac {
} }
} }
checkShipTarget(battle: Battle, ship: Ship, target: Target): Target { checkShipTarget(ship: Ship, target: Target): Target {
if (ship.getPlayer() === target.ship.getPlayer()) { if (ship.getPlayer() === target.ship.getPlayer()) {
// No friendly fire // No friendly fire
return null; return null;
@ -48,7 +48,7 @@ module TS.SpaceTac {
return result; return result;
} }
protected customApply(battle: Battle, ship: Ship, target: Target) { protected customApply(ship: Ship, target: Target) {
// Face the target // Face the target
ship.rotate(Target.newFromShip(ship).getAngleTo(target)); ship.rotate(Target.newFromShip(ship).getAngleTo(target));
@ -56,7 +56,7 @@ module TS.SpaceTac {
ship.addBattleEvent(new FireEvent(ship, this.equipment, target)); ship.addBattleEvent(new FireEvent(ship, this.equipment, target));
// Apply effects // Apply effects
let effects = this.getEffects(battle, ship, target); let effects = this.getEffects(ship.getBattle(), ship, target);
effects.forEach(([ship, effect]) => effect.applyOnShip(ship)); effects.forEach(([ship, effect]) => effect.applyOnShip(ship));
} }
} }

View file

@ -15,14 +15,14 @@ module TS.SpaceTac {
expect(action.getDistanceByActionPoint(ship)).toBe(0.5); expect(action.getDistanceByActionPoint(ship)).toBe(0.5);
var result = action.checkTarget(battle, ship, Target.newFromLocation(0, 2)); var result = action.checkTarget(ship, Target.newFromLocation(0, 2));
expect(result).toEqual(Target.newFromLocation(0, 2)); expect(result).toEqual(Target.newFromLocation(0, 2));
result = action.checkTarget(battle, ship, Target.newFromLocation(0, 8)); result = action.checkTarget(ship, Target.newFromLocation(0, 8));
expect(result).toEqual(Target.newFromLocation(0, 3)); expect(result).toEqual(Target.newFromLocation(0, 3));
ship.values.power.set(0); ship.values.power.set(0);
result = action.checkTarget(battle, ship, Target.newFromLocation(0, 8)); result = action.checkTarget(ship, Target.newFromLocation(0, 8));
expect(result).toBeNull(); expect(result).toBeNull();
}); });
@ -31,10 +31,10 @@ module TS.SpaceTac {
var ship2 = new Ship(null, "Test2"); var ship2 = new Ship(null, "Test2");
var action = new MoveAction(null); var action = new MoveAction(null);
var result = action.checkTarget(null, ship1, Target.newFromShip(ship1)); var result = action.checkTarget(ship1, Target.newFromShip(ship1));
expect(result).toBeNull(); expect(result).toBeNull();
result = action.checkTarget(null, ship1, Target.newFromShip(ship2)); result = action.checkTarget(ship1, Target.newFromShip(ship2));
expect(result).toBeNull(); expect(result).toBeNull();
}); });
@ -51,13 +51,15 @@ module TS.SpaceTac {
var action = new MoveAction(engine); var action = new MoveAction(engine);
battle.playing_ship = ship; battle.playing_ship = ship;
var result = action.apply(battle, ship, Target.newFromLocation(10, 10)); spyOn(console, "warn").and.stub();
var result = action.apply(ship, Target.newFromLocation(10, 10));
expect(result).toBe(true); expect(result).toBe(true);
expect(ship.arena_x).toBeCloseTo(3.535533, 0.00001); expect(ship.arena_x).toBeCloseTo(3.535533, 0.00001);
expect(ship.arena_y).toBeCloseTo(3.535533, 0.00001); expect(ship.arena_y).toBeCloseTo(3.535533, 0.00001);
expect(ship.values.power.get()).toEqual(0); expect(ship.values.power.get()).toEqual(0);
result = action.apply(battle, ship, Target.newFromLocation(10, 10)); result = action.apply(ship, Target.newFromLocation(10, 10));
expect(result).toBe(false); expect(result).toBe(false);
expect(ship.arena_x).toBeCloseTo(3.535533, 0.00001); expect(ship.arena_x).toBeCloseTo(3.535533, 0.00001);
expect(ship.arena_y).toBeCloseTo(3.535533, 0.00001); expect(ship.arena_y).toBeCloseTo(3.535533, 0.00001);
@ -89,19 +91,19 @@ module TS.SpaceTac {
var action = new MoveAction(engine); var action = new MoveAction(engine);
action.safety_distance = 2; action.safety_distance = 2;
var result = action.checkLocationTarget(battle, ship, Target.newFromLocation(7, 5)); var result = action.checkLocationTarget(ship, Target.newFromLocation(7, 5));
expect(result).toEqual(Target.newFromLocation(7, 5)); expect(result).toEqual(Target.newFromLocation(7, 5));
result = action.checkLocationTarget(battle, ship, Target.newFromLocation(8, 5)); result = action.checkLocationTarget(ship, Target.newFromLocation(8, 5));
expect(result).toEqual(Target.newFromLocation(8, 5)); expect(result).toEqual(Target.newFromLocation(8, 5));
result = action.checkLocationTarget(battle, ship, Target.newFromLocation(9, 5)); result = action.checkLocationTarget(ship, Target.newFromLocation(9, 5));
expect(result).toEqual(Target.newFromLocation(8, 5)); expect(result).toEqual(Target.newFromLocation(8, 5));
result = action.checkLocationTarget(battle, ship, Target.newFromLocation(10, 5)); result = action.checkLocationTarget(ship, Target.newFromLocation(10, 5));
expect(result).toEqual(Target.newFromLocation(8, 5)); expect(result).toEqual(Target.newFromLocation(8, 5));
result = action.checkLocationTarget(battle, ship, Target.newFromLocation(12, 5)); result = action.checkLocationTarget(ship, Target.newFromLocation(12, 5));
expect(result).toEqual(Target.newFromLocation(12, 5)); expect(result).toEqual(Target.newFromLocation(12, 5));
}); });
}); });

View file

@ -11,19 +11,24 @@ module TS.SpaceTac {
this.safety_distance = 50; this.safety_distance = 50;
} }
canBeUsed(battle: Battle, ship: Ship, remaining_ap: number = null): boolean { checkCannotBeApplied(ship: Ship, remaining_ap: number = null): string | null {
if (battle && battle.playing_ship !== ship) { let base = super.checkCannotBeApplied(ship, Infinity);
return false; if (base) {
return base;
} }
// Check AP usage // Check AP usage
if (remaining_ap === null) { if (remaining_ap === null) {
remaining_ap = ship.values.power.get(); remaining_ap = ship.values.power.get();
} }
return remaining_ap > 0.0001; if (remaining_ap > 0.0001) {
return null
} else {
return "not enough power";
}
} }
getActionPointsUsage(battle: Battle, ship: Ship, target: Target): number { getActionPointsUsage(ship: Ship, target: Target): number {
if (target === null) { if (target === null) {
return 0; return 0;
} }
@ -43,23 +48,26 @@ module TS.SpaceTac {
return this.equipment.distance / this.equipment.ap_usage; return this.equipment.distance / this.equipment.ap_usage;
} }
checkLocationTarget(battle: Battle, ship: Ship, target: Target): Target { checkLocationTarget(ship: Ship, target: Target): Target {
// Apply maximal distance // Apply maximal distance
var max_distance = this.getRangeRadius(ship); var max_distance = this.getRangeRadius(ship);
target = target.constraintInRange(ship.arena_x, ship.arena_y, max_distance); target = target.constraintInRange(ship.arena_x, ship.arena_y, max_distance);
// Apply collision prevention // Apply collision prevention
battle.play_order.forEach((iship: Ship) => { let battle = ship.getBattle();
if (iship !== ship) { if (battle) {
target = target.moveOutOfCircle(iship.arena_x, iship.arena_y, this.safety_distance, battle.play_order.forEach((iship: Ship) => {
ship.arena_x, ship.arena_y); if (iship !== ship) {
} target = target.moveOutOfCircle(iship.arena_x, iship.arena_y, this.safety_distance,
}); ship.arena_x, ship.arena_y);
}
});
}
return target; return target;
} }
protected customApply(battle: Battle, ship: Ship, target: Target) { protected customApply(ship: Ship, target: Target) {
ship.moveTo(target.x, target.y); ship.moveTo(target.x, target.y);
} }
} }

View file

@ -44,14 +44,15 @@ module TS.SpaceTac {
/** /**
* Update the result of a single battle * Update the result of a single battle
*/ */
update(winner: AbstractAI | null) { update(winner: number) {
if (winner) { if (winner >= 0) {
if (winner == this.ai1) { if (winner == 0) {
this.win1 += 1; this.win1 += 1;
console.log(` => Player 1 wins (${this.ai1})`);
} else { } else {
this.win2 += 1; this.win2 += 1;
console.log(` => Player 2 wins (${this.ai2})`);
} }
console.log(` => ${winner.name} wins`);
} else { } else {
this.draw += 1; this.draw += 1;
console.log(" => draw"); console.log(" => draw");
@ -86,9 +87,9 @@ module TS.SpaceTac {
} }
if (battle.ended && !battle.outcome.draw) { if (battle.ended && !battle.outcome.draw) {
this.update(battle.outcome.winner == battle.fleets[0] ? this.ai1 : this.ai2); this.update(battle.fleets.indexOf(battle.outcome.winner));
} else { } else {
this.update(null); this.update(-1);
} }
if (!this.stopped) { if (!this.stopped) {
this.scheduled = Timer.global.schedule(100, () => this.next()); this.scheduled = Timer.global.schedule(100, () => this.next());

View file

@ -153,7 +153,7 @@ module TS.SpaceTac {
if (distance > safety_distance) { // Don't move too close if (distance > safety_distance) { // Don't move too close
target = target.constraintInRange(this.ship.arena_x, this.ship.arena_y, target = target.constraintInRange(this.ship.arena_x, this.ship.arena_y,
(distance - safety_distance) * APPROACH_FACTOR); (distance - safety_distance) * APPROACH_FACTOR);
target = engine.action.checkLocationTarget(this.ship.getBattle(), this.ship, target); target = engine.action.checkLocationTarget(this.ship, target);
return new BullyManeuver(new Maneuver(this.ship, engine, target)); return new BullyManeuver(new Maneuver(this.ship, engine, target));
} else { } else {
return null; return null;

View file

@ -32,8 +32,8 @@ module TS.SpaceTac {
apply(): void { apply(): void {
if (this.simulation.success) { if (this.simulation.success) {
this.simulation.parts.forEach(part => { this.simulation.parts.forEach(part => {
if (!part.action.apply(this.ship.getBattle(), this.ship, part.target)) { if (!part.action.apply(this.ship, part.target)) {
console.error("AI cannot apply maneuver", this); console.error("AI cannot apply maneuver", this, part);
} }
}); });
} }

View file

@ -38,15 +38,15 @@ module TS.SpaceTac.Specs {
weapon.ap_usage = new Range(2); weapon.ap_usage = new Range(2);
var equipment = weapon.generateFixed(0); var equipment = weapon.generateFixed(0);
expect(equipment.action.canBeUsed(null, ship)).toBe(true); expect(equipment.action.checkCannotBeApplied(ship)).toBe(null);
weapon.ap_usage = new Range(3); weapon.ap_usage = new Range(3);
equipment = weapon.generateFixed(0); equipment = weapon.generateFixed(0);
expect(equipment.action.canBeUsed(null, ship)).toBe(true); expect(equipment.action.checkCannotBeApplied(ship)).toBe(null);
weapon.ap_usage = new Range(4); weapon.ap_usage = new Range(4);
equipment = weapon.generateFixed(0); equipment = weapon.generateFixed(0);
expect(equipment.action.canBeUsed(null, ship)).toBe(false); expect(equipment.action.checkCannotBeApplied(ship)).toBe("not enough power");
}); });
it("can't friendly fire", function () { it("can't friendly fire", function () {
@ -60,9 +60,9 @@ module TS.SpaceTac.Specs {
weapon.setRange(10, 10); weapon.setRange(10, 10);
var equipment = weapon.generateFixed(0); var equipment = weapon.generateFixed(0);
expect(equipment.action.checkShipTarget(null, ship1a, Target.newFromShip(ship2a))).toEqual( expect(equipment.action.checkShipTarget(ship1a, Target.newFromShip(ship2a))).toEqual(
Target.newFromShip(ship2a)); Target.newFromShip(ship2a));
expect(equipment.action.checkShipTarget(null, ship1a, Target.newFromShip(ship1b))).toBeNull(); expect(equipment.action.checkShipTarget(ship1a, Target.newFromShip(ship1b))).toBeNull();
}); });
it("can't fire farther than its range", function () { it("can't fire farther than its range", function () {
@ -78,25 +78,25 @@ module TS.SpaceTac.Specs {
var equipment = weapon.generateFixed(0); var equipment = weapon.generateFixed(0);
expect(equipment.distance).toEqual(10); expect(equipment.distance).toEqual(10);
expect(equipment.action.checkLocationTarget(null, ship, Target.newFromLocation(15, 10))).toEqual( expect(equipment.action.checkLocationTarget(ship, Target.newFromLocation(15, 10))).toEqual(
Target.newFromLocation(15, 10)); Target.newFromLocation(15, 10));
expect(equipment.action.checkLocationTarget(null, ship, Target.newFromLocation(30, 10))).toEqual( expect(equipment.action.checkLocationTarget(ship, Target.newFromLocation(30, 10))).toEqual(
Target.newFromLocation(20, 10)); Target.newFromLocation(20, 10));
// Ship targetting // Ship targetting
var ship2 = new Ship(fleet2); var ship2 = new Ship(fleet2);
ship2.setArenaPosition(10, 15); ship2.setArenaPosition(10, 15);
expect(equipment.action.checkShipTarget(null, ship, Target.newFromShip(ship2))).toEqual( expect(equipment.action.checkShipTarget(ship, Target.newFromShip(ship2))).toEqual(
Target.newFromShip(ship2)); Target.newFromShip(ship2));
ship2.setArenaPosition(10, 25); ship2.setArenaPosition(10, 25);
expect(equipment.action.checkShipTarget(null, ship, Target.newFromShip(ship2))).toBeNull(); expect(equipment.action.checkShipTarget(ship, Target.newFromShip(ship2))).toBeNull();
// Forbid targetting in space // Forbid targetting in space
weapon.setRange(10, 10, false); weapon.setRange(10, 10, false);
equipment = weapon.generateFixed(0); equipment = weapon.generateFixed(0);
expect(equipment.action.checkLocationTarget(null, ship, Target.newFromLocation(15, 10))).toBeNull(); expect(equipment.action.checkLocationTarget(ship, Target.newFromLocation(15, 10))).toBeNull();
}); });
it("can target an enemy ship and damage it", function () { it("can target an enemy ship and damage it", function () {
@ -119,17 +119,17 @@ module TS.SpaceTac.Specs {
var equipment = weapon.generateFixed(0); var equipment = weapon.generateFixed(0);
equipment.action.apply(null, ship1, Target.newFromShip(ship2)); equipment.action.apply(ship1, Target.newFromShip(ship2));
expect(ship2.values.hull.get()).toEqual(100); expect(ship2.values.hull.get()).toEqual(100);
expect(ship2.values.shield.get()).toEqual(10); expect(ship2.values.shield.get()).toEqual(10);
expect(ship1.values.power.get()).toEqual(49); expect(ship1.values.power.get()).toEqual(49);
equipment.action.apply(null, ship1, Target.newFromShip(ship2)); equipment.action.apply(ship1, Target.newFromShip(ship2));
expect(ship2.values.hull.get()).toEqual(90); expect(ship2.values.hull.get()).toEqual(90);
expect(ship2.values.shield.get()).toEqual(0); expect(ship2.values.shield.get()).toEqual(0);
expect(ship1.values.power.get()).toEqual(48); expect(ship1.values.power.get()).toEqual(48);
equipment.action.apply(null, ship1, Target.newFromShip(ship2)); equipment.action.apply(ship1, Target.newFromShip(ship2));
expect(ship2.values.hull.get()).toEqual(70); expect(ship2.values.hull.get()).toEqual(70);
expect(ship2.values.shield.get()).toEqual(0); expect(ship2.values.shield.get()).toEqual(0);
expect(ship1.values.power.get()).toEqual(47); expect(ship1.values.power.get()).toEqual(47);

View file

@ -13,7 +13,7 @@ module TS.SpaceTac.Specs {
expect(target.sticky_effects).toEqual([]); expect(target.sticky_effects).toEqual([]);
// Attribute is immediately limited // Attribute is immediately limited
equipment.action.apply(null, ship, Target.newFromShip(target)); equipment.action.apply(ship, Target.newFromShip(target));
expect(target.values.power.get()).toBe(4); expect(target.values.power.get()).toBe(4);
expect(target.sticky_effects).toEqual([ expect(target.sticky_effects).toEqual([

View file

@ -7,10 +7,10 @@ module TS.SpaceTac.Equipments {
expect(equipment.target_effects).toEqual([new ValueEffect("hull", 30)]); expect(equipment.target_effects).toEqual([new ValueEffect("hull", 30)]);
let battle = new Battle(); let battle = new Battle();
let ship = new Ship(); let ship = battle.fleets[0].addShip();
battle.playing_ship = ship; battle.playing_ship = ship;
TestTools.setShipAP(ship, 10); TestTools.setShipAP(ship, 10);
let result = equipment.action.apply(battle, ship, new Target(5, 5, null)); let result = equipment.action.apply(ship, new Target(5, 5, null));
expect(result).toBe(true); expect(result).toBe(true);
expect(battle.drones.length).toBe(1); expect(battle.drones.length).toBe(1);

View file

@ -34,12 +34,12 @@ module TS.SpaceTac.Specs {
battle.log.addFilter("value"); battle.log.addFilter("value");
// Fire at a ship // Fire at a ship
var t = Target.newFromShip(enemy1); var target = Target.newFromShip(enemy1);
expect(equipment.action.canBeUsed(battle, ship)).toBe(true); expect(equipment.action.checkCannotBeApplied(ship)).toBe(null);
equipment.action.apply(battle, ship, t); equipment.action.apply(ship, target);
checkHP(50, 10, 50, 10, 50, 10); checkHP(50, 10, 50, 10, 50, 10);
expect(battle.log.events.length).toBe(4); expect(battle.log.events.length).toBe(4);
expect(battle.log.events[0]).toEqual(new FireEvent(ship, equipment, t)); expect(battle.log.events[0]).toEqual(new FireEvent(ship, equipment, target));
expect(battle.log.events[1]).toEqual(new DamageEvent(ship, 0, 20)); expect(battle.log.events[1]).toEqual(new DamageEvent(ship, 0, 20));
expect(battle.log.events[2]).toEqual(new DamageEvent(enemy1, 0, 20)); expect(battle.log.events[2]).toEqual(new DamageEvent(enemy1, 0, 20));
expect(battle.log.events[3]).toEqual(new DamageEvent(enemy2, 0, 20)); expect(battle.log.events[3]).toEqual(new DamageEvent(enemy2, 0, 20));
@ -47,24 +47,24 @@ module TS.SpaceTac.Specs {
battle.log.clear(); battle.log.clear();
// Fire in space // Fire in space
t = Target.newFromLocation(2.4, 0); target = Target.newFromLocation(2.4, 0);
expect(equipment.action.canBeUsed(battle, ship)).toBe(true); expect(equipment.action.checkCannotBeApplied(ship)).toBe(null);
equipment.action.apply(battle, ship, t); equipment.action.apply(ship, target);
checkHP(50, 10, 40, 0, 40, 0); checkHP(50, 10, 40, 0, 40, 0);
expect(battle.log.events.length).toBe(3); expect(battle.log.events.length).toBe(3);
expect(battle.log.events[0]).toEqual(new FireEvent(ship, equipment, t)); expect(battle.log.events[0]).toEqual(new FireEvent(ship, equipment, target));
expect(battle.log.events[1]).toEqual(new DamageEvent(enemy1, 10, 10)); expect(battle.log.events[1]).toEqual(new DamageEvent(enemy1, 10, 10));
expect(battle.log.events[2]).toEqual(new DamageEvent(enemy2, 10, 10)); expect(battle.log.events[2]).toEqual(new DamageEvent(enemy2, 10, 10));
battle.log.clear(); battle.log.clear();
// Fire far away // Fire far away
t = Target.newFromLocation(5, 0); target = Target.newFromLocation(5, 0);
expect(equipment.action.canBeUsed(battle, ship)).toBe(true); expect(equipment.action.checkCannotBeApplied(ship)).toBe(null);
equipment.action.apply(battle, ship, t); equipment.action.apply(ship, target);
checkHP(50, 10, 40, 0, 40, 0); checkHP(50, 10, 40, 0, 40, 0);
expect(battle.log.events.length).toBe(1); expect(battle.log.events.length).toBe(1);
expect(battle.log.events[0]).toEqual(new FireEvent(ship, equipment, t)); expect(battle.log.events[0]).toEqual(new FireEvent(ship, equipment, target));
}); });
}); });
} }

View file

@ -88,7 +88,7 @@ module TS.SpaceTac.UI {
if (!this.bar.interactive) { if (!this.bar.interactive) {
return; return;
} }
if (!this.action.canBeUsed(this.battleview.battle, this.ship)) { if (this.action.checkCannotBeApplied(this.ship)) {
return; return;
} }
if (this.selected) { if (this.selected) {
@ -104,7 +104,7 @@ module TS.SpaceTac.UI {
this.battleview.arena.range_hint.setPrimary(this.ship, this.action); this.battleview.arena.range_hint.setPrimary(this.ship, this.action);
// Update fading statuses // Update fading statuses
this.bar.updateSelectedActionPower(this.action.getActionPointsUsage(this.battleview.battle, this.ship, null)); this.bar.updateSelectedActionPower(this.action.getActionPointsUsage(this.ship, null));
// Set the selected state // Set the selected state
this.setSelected(true); this.setSelected(true);
@ -127,14 +127,14 @@ module TS.SpaceTac.UI {
// Called when a target is hovered // Called when a target is hovered
// This will check the target against current action and adjust it if needed // This will check the target against current action and adjust it if needed
processHover(target: Target): void { processHover(target: Target): void {
target = this.action.checkTarget(this.battleview.battle, this.ship, target); target = this.action.checkTarget(this.ship, target);
this.targetting.setTarget(target, false, this.action.getBlastRadius(this.ship)); this.targetting.setTarget(target, false, this.action.getBlastRadius(this.ship));
this.bar.updateSelectedActionPower(this.action.getActionPointsUsage(this.battleview.battle, this.ship, target)); this.bar.updateSelectedActionPower(this.action.getActionPointsUsage(this.ship, target));
} }
// Called when a target is selected // Called when a target is selected
processSelection(target: Target): void { processSelection(target: Target): void {
if (this.action.apply(this.battleview.battle, this.ship, target)) { if (this.action.apply(this.ship, target)) {
this.bar.actionEnded(); this.bar.actionEnded();
} }
} }
@ -159,7 +159,7 @@ module TS.SpaceTac.UI {
// Update the active status, from the action canBeUsed result // Update the active status, from the action canBeUsed result
updateActiveStatus(force = false): void { updateActiveStatus(force = false): void {
var old_active = this.active; var old_active = this.active;
this.active = this.action.canBeUsed(this.battleview.battle, this.ship); this.active = !this.action.checkCannotBeApplied(this.ship);
if (force || (this.active != old_active)) { if (force || (this.active != old_active)) {
Animation.setVisibility(this.game, this.layer_active, this.active, 500); Animation.setVisibility(this.game, this.layer_active, this.active, 500);
this.game.tweens.create(this.layer_icon).to({ alpha: this.active ? 1 : 0.3 }, 500).start(); this.game.tweens.create(this.layer_icon).to({ alpha: this.active ? 1 : 0.3 }, 500).start();
@ -170,7 +170,7 @@ module TS.SpaceTac.UI {
// Update the fading status, given an hypothetical remaining AP // Update the fading status, given an hypothetical remaining AP
updateFadingStatus(remaining_ap: number): void { updateFadingStatus(remaining_ap: number): void {
var old_fading = this.fading; var old_fading = this.fading;
this.fading = this.active && !this.action.canBeUsed(this.battleview.battle, this.ship, remaining_ap); this.fading = this.active && (this.action.checkCannotBeApplied(this.ship, remaining_ap) != null);
if (this.fading != old_fading) { if (this.fading != old_fading) {
Animation.setVisibility(this.game, this.layer_active, this.active && !this.fading, 500); Animation.setVisibility(this.game, this.layer_active, this.active && !this.fading, 500);
} }