Started MoveFireSimulator
This commit is contained in:
parent
bd963a07f9
commit
44b870f970
93
src/game/MoveFireSimulator.spec.ts
Normal file
93
src/game/MoveFireSimulator.spec.ts
Normal file
|
@ -0,0 +1,93 @@
|
|||
module TS.SpaceTac.Game.Specs {
|
||||
describe("MoveFireSimulator", function () {
|
||||
|
||||
function simpleWeaponCase(distance = 10, ship_ap = 5, weapon_ap = 3, engine_distance = 5): [Ship, MoveFireSimulator, BaseAction] {
|
||||
let ship = new Ship();
|
||||
TestTools.setShipAP(ship, ship_ap);
|
||||
TestTools.addEngine(ship, engine_distance);
|
||||
let action = new FireWeaponAction(new Equipment(), true);
|
||||
action.equipment.distance = distance;
|
||||
action.equipment.ap_usage = weapon_ap;
|
||||
let simulator = new MoveFireSimulator(ship);
|
||||
return [ship, simulator, action];
|
||||
}
|
||||
|
||||
it("finds the best engine to make a move", function () {
|
||||
let ship = new Ship();
|
||||
let simulator = new MoveFireSimulator(ship);
|
||||
expect(simulator.findBestEngine()).toBe(null);
|
||||
let engine1 = TestTools.addEngine(ship, 100);
|
||||
expect(simulator.findBestEngine()).toBe(engine1);
|
||||
let engine2 = TestTools.addEngine(ship, 120);
|
||||
let engine3 = TestTools.addEngine(ship, 150);
|
||||
let engine4 = TestTools.addEngine(ship, 70);
|
||||
expect(simulator.findBestEngine()).toBe(engine3);
|
||||
expect(simulator.findBestEngine().distance).toBe(150);
|
||||
});
|
||||
|
||||
it("fires directly when in range", function () {
|
||||
let [ship, simulator, action] = simpleWeaponCase();
|
||||
let result = simulator.simulateAction(action, new Target(ship.arena_x + 5, ship.arena_y, null));
|
||||
|
||||
expect(result.success).toBe(true, 'success');
|
||||
expect(result.need_move).toBe(false, 'need_move');
|
||||
expect(result.need_fire).toBe(true, 'need_fire');
|
||||
expect(result.can_fire).toBe(true, 'can_fire');
|
||||
expect(result.total_fire_ap).toBe(3, 'total_fire_ap');
|
||||
|
||||
expect(result.parts).toEqual([
|
||||
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 3 }
|
||||
]);
|
||||
});
|
||||
|
||||
it("can't fire when in range, but not enough AP", function () {
|
||||
let [ship, simulator, action] = simpleWeaponCase(10, 2, 3);
|
||||
let result = simulator.simulateAction(action, new Target(ship.arena_x + 5, ship.arena_y, null));
|
||||
expect(result.success).toBe(true, 'success');
|
||||
expect(result.need_move).toBe(false, 'need_move');
|
||||
expect(result.need_fire).toBe(true, 'need_fire');
|
||||
expect(result.can_fire).toBe(false, 'can_fire');
|
||||
expect(result.total_fire_ap).toBe(3, 'total_fire_ap');
|
||||
|
||||
expect(result.parts).toEqual([
|
||||
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 3 }
|
||||
]);
|
||||
});
|
||||
|
||||
it("moves straight to get within range", function () {
|
||||
let [ship, simulator, action] = simpleWeaponCase();
|
||||
let result = simulator.simulateAction(action, new Target(ship.arena_x + 15, ship.arena_y, null));
|
||||
expect(result.success).toBe(true, 'success');
|
||||
expect(result.need_move).toBe(true, 'need_move');
|
||||
expect(result.can_end_move).toBe(true, 'can_end_move');
|
||||
expect(result.move_location).toEqual(new Target(ship.arena_x + 5, ship.arena_y, null));
|
||||
expect(result.total_move_ap).toBe(1);
|
||||
expect(result.need_fire).toBe(true, 'need_fire');
|
||||
expect(result.can_fire).toBe(true, 'can_fire');
|
||||
expect(result.total_fire_ap).toBe(3, 'total_fire_ap');
|
||||
|
||||
expect(result.parts).toEqual([
|
||||
{ action: jasmine.objectContaining({ code: "move" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 1 },
|
||||
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 15, ship.arena_y, null), ap: 3 }
|
||||
]);
|
||||
});
|
||||
|
||||
it("moves to get in range, even if not enough AP to fire", function () {
|
||||
let [ship, simulator, action] = simpleWeaponCase(8, 3, 2, 5);
|
||||
let result = simulator.simulateAction(action, new Target(ship.arena_x + 18, ship.arena_y, null));
|
||||
expect(result.success).toBe(true, 'success');
|
||||
expect(result.need_move).toBe(true, 'need_move');
|
||||
expect(result.can_end_move).toBe(true, 'can_end_move');
|
||||
expect(result.move_location).toEqual(new Target(ship.arena_x + 10, ship.arena_y, null));
|
||||
expect(result.total_move_ap).toBe(2);
|
||||
expect(result.need_fire).toBe(true, 'need_fire');
|
||||
expect(result.can_fire).toBe(false, 'can_fire');
|
||||
expect(result.total_fire_ap).toBe(2, 'total_fire_ap');
|
||||
|
||||
expect(result.parts).toEqual([
|
||||
{ action: jasmine.objectContaining({ code: "move" }), target: new Target(ship.arena_x + 10, 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 }
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
101
src/game/MoveFireSimulator.ts
Normal file
101
src/game/MoveFireSimulator.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
module TS.SpaceTac.Game {
|
||||
|
||||
/**
|
||||
* A single action in the sequence result from the simulator
|
||||
*/
|
||||
type MoveFirePart = {
|
||||
action: BaseAction
|
||||
target: Target
|
||||
ap: number
|
||||
}
|
||||
|
||||
/**
|
||||
* A simulation result
|
||||
*/
|
||||
class MoveFireResult {
|
||||
// Simulation success, false only if no route can be found
|
||||
success = false
|
||||
// Ideal successive parts to make the full move+fire
|
||||
parts: MoveFirePart[] = []
|
||||
|
||||
need_move = false
|
||||
can_move = false
|
||||
can_end_move = false
|
||||
total_move_ap = 0
|
||||
move_location = new Target(0, 0, null)
|
||||
|
||||
need_fire = false
|
||||
can_fire = false
|
||||
total_fire_ap = 0
|
||||
fire_location = new Target(0, 0, null)
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility to simulate a move+fire action.
|
||||
*
|
||||
* This is also a helper to bring a ship in range to fire a weapon.
|
||||
*/
|
||||
export class MoveFireSimulator {
|
||||
ship: Ship;
|
||||
|
||||
constructor(ship: Ship) {
|
||||
this.ship = ship;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the best available engine for moving
|
||||
*/
|
||||
findBestEngine(): Equipment | null {
|
||||
let engines = this.ship.listEquipment(SlotType.Engine);
|
||||
if (engines.length == 0) {
|
||||
return null;
|
||||
} else {
|
||||
engines.sort((a, b) => cmp(b.distance, a.distance));
|
||||
return engines[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a given action on a given valid target.
|
||||
*/
|
||||
simulateAction(action: BaseAction, target: Target): MoveFireResult {
|
||||
let dx = target.x - this.ship.arena_x;
|
||||
let dy = target.y - this.ship.arena_y;
|
||||
let distance = Math.sqrt(dx * dx + dy * dy);
|
||||
let result = new MoveFireResult();
|
||||
let ap = this.ship.ap_current.current;
|
||||
|
||||
if (distance > action.getRangeRadius(this.ship)) {
|
||||
result.need_move = true;
|
||||
let move_distance = distance - action.getRangeRadius(this.ship);
|
||||
let move_target = new Target(this.ship.arena_x + dx * move_distance / distance, this.ship.arena_y + dy * move_distance / distance, null);
|
||||
let engine = this.findBestEngine();
|
||||
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;
|
||||
distance -= move_distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (distance <= action.getRangeRadius(this.ship)) {
|
||||
result.success = true;
|
||||
if (!(action instanceof MoveAction)) {
|
||||
result.need_fire = true;
|
||||
result.total_fire_ap = action.getActionPointsUsage(this.ship.getBattle(), this.ship, target);
|
||||
result.can_fire = result.total_fire_ap <= ap;
|
||||
result.fire_location = target;
|
||||
result.parts.push({ action: action, target: target, ap: result.total_fire_ap });
|
||||
}
|
||||
} else {
|
||||
result.success = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,10 +21,10 @@ module TS.SpaceTac.Game {
|
|||
}
|
||||
|
||||
// Get or add an equipment of a given slot type
|
||||
static getOrGenEquipment(ship: Ship, slot: SlotType, template: LootTemplate): Equipment {
|
||||
static getOrGenEquipment(ship: Ship, slot: SlotType, template: LootTemplate, force_generate = false): Equipment {
|
||||
var equipped = ship.listEquipment(slot);
|
||||
var equipment: Equipment;
|
||||
if (equipped.length === 0) {
|
||||
if (force_generate || equipped.length === 0) {
|
||||
equipment = template.generateFixed(0);
|
||||
ship.addSlot(slot).attach(equipment);
|
||||
} else {
|
||||
|
@ -36,7 +36,7 @@ module TS.SpaceTac.Game {
|
|||
|
||||
// Add an engine, allowing a ship to move *distance*, for each action points
|
||||
static addEngine(ship: Ship, distance: number): Equipment {
|
||||
var equipment = this.getOrGenEquipment(ship, SlotType.Engine, new Equipments.ConventionalEngine());
|
||||
var equipment = this.getOrGenEquipment(ship, SlotType.Engine, new Equipments.ConventionalEngine(), true);
|
||||
equipment.ap_usage = 1;
|
||||
equipment.distance = distance;
|
||||
return equipment;
|
||||
|
|
|
@ -53,6 +53,10 @@ module TS.SpaceTac.View {
|
|||
this.ship_hovered = null;
|
||||
this.log_processor = null;
|
||||
this.background = null;
|
||||
|
||||
if (typeof window != "undefined") {
|
||||
(<any>window).battle = this.battle;
|
||||
}
|
||||
}
|
||||
|
||||
// Create view graphics
|
||||
|
|
|
@ -128,6 +128,7 @@ module TS.SpaceTac.View {
|
|||
while (this.ap_indicators.length < count) {
|
||||
let indicator = new Phaser.Image(this.battleview.game, 0, 0, "battle-arena-ap-indicator");
|
||||
indicator.anchor.set(0.5, 0.5);
|
||||
indicator.scale.set(0.5, 0.5);
|
||||
this.battleview.arena.addChild(indicator);
|
||||
this.ap_indicators.push(indicator);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue