Added collision prevention on move actions
This commit is contained in:
parent
df9091b64a
commit
cb278959a9
3
TODO
Normal file
3
TODO
Normal file
|
@ -0,0 +1,3 @@
|
|||
* Add a cheat system, to use for development
|
||||
* Add a defeat screen (game over for now)
|
||||
* Add a victory screen, with looting
|
|
@ -3,6 +3,22 @@
|
|||
module SpaceTac.Game {
|
||||
"use strict";
|
||||
|
||||
// Find the nearest intersection between a line and a circle
|
||||
// Circle is supposed to be centered at (0,0)
|
||||
// Nearest intersection to (x1,y1) is returned
|
||||
function intersectLineCircle(x1: number, y1: number, x2: number, y2: number, r: number): [number, number] {
|
||||
// See http://mathworld.wolfram.com/Circle-LineIntersection.html
|
||||
var dx = x2 - x1;
|
||||
var dy = y2 - y1;
|
||||
var dr = Math.sqrt(dx * dx + dy * dy);
|
||||
var d = x1 * y2 - x2 * y1;
|
||||
var delta = r * r * dr * dr - d * d;
|
||||
|
||||
var rx = (d * dy - dx * Math.sqrt(delta)) / (dr * dr);
|
||||
var ry = (-d * dx - dy * Math.sqrt(delta)) / (dr * dr);
|
||||
return [rx, ry];
|
||||
}
|
||||
|
||||
// Target for a capability
|
||||
// This could be a location in space, or a ship
|
||||
export class Target extends Serializable {
|
||||
|
@ -67,5 +83,24 @@ module SpaceTac.Game {
|
|||
return Target.newFromLocation(x + dx * factor, y + dy * factor);
|
||||
}
|
||||
}
|
||||
|
||||
// Force a target to stay out of a given circle
|
||||
// If the target is in the circle, it will be moved to the nearest intersection between targetting line
|
||||
// and the circle
|
||||
// May return the original target if it's already out of the circle
|
||||
moveOutOfCircle(circlex: number, circley: number, radius: number, sourcex: number, sourcey: number): Target {
|
||||
var dx = this.x - circlex;
|
||||
var dy = this.y - circley;
|
||||
var length = Math.sqrt(dx * dx + dy * dy);
|
||||
if (length >= radius) {
|
||||
// Already out of circle
|
||||
return this;
|
||||
} else {
|
||||
// Find nearest intersection with circle
|
||||
var res = intersectLineCircle(sourcex - circlex, sourcey - circley,
|
||||
this.x - circlex, this.y - circley, radius);
|
||||
return Target.newFromLocation(res[0] + circlex, res[1] + circley);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,10 +37,11 @@ module SpaceTac.Game {
|
|||
}
|
||||
|
||||
// Add an engine, allowing a ship to move *distance*, for each action points
|
||||
static addEngine(ship: Ship, distance: number): void {
|
||||
static addEngine(ship: Ship, distance: number): Equipment {
|
||||
var equipment = this.getOrGenEquipment(ship, SlotType.Engine, new Equipments.ConventionalEngine());
|
||||
equipment.ap_usage = 1;
|
||||
equipment.distance = distance;
|
||||
return equipment;
|
||||
}
|
||||
|
||||
// Set a ship action points, adding/updating an equipment if needed
|
||||
|
|
|
@ -3,8 +3,14 @@ module SpaceTac.Game {
|
|||
|
||||
// Action to move to a given location
|
||||
export class MoveAction extends BaseAction {
|
||||
|
||||
// Safety distance from other ships
|
||||
safety_distance: number;
|
||||
|
||||
constructor(equipment: Equipment) {
|
||||
super("move", true, equipment);
|
||||
|
||||
this.safety_distance = 50;
|
||||
}
|
||||
|
||||
canBeUsed(battle: Battle, ship: Ship, remaining_ap: number = null): boolean {
|
||||
|
@ -33,9 +39,19 @@ module SpaceTac.Game {
|
|||
}
|
||||
|
||||
checkLocationTarget(battle: Battle, ship: Ship, target: Target): Target {
|
||||
// TODO Should forbid to move too much near another ship
|
||||
// Apply maximal distance
|
||||
var max_distance = this.equipment.distance * ship.ap_current.current / this.equipment.ap_usage;
|
||||
return target.constraintInRange(ship.arena_x, ship.arena_y, max_distance);
|
||||
target = target.constraintInRange(ship.arena_x, ship.arena_y, max_distance);
|
||||
|
||||
// Apply collision prevention
|
||||
battle.play_order.forEach((iship: Ship) => {
|
||||
if (iship !== ship) {
|
||||
target = target.moveOutOfCircle(iship.arena_x, iship.arena_y, this.safety_distance,
|
||||
ship.arena_x, ship.arena_y);
|
||||
}
|
||||
});
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
protected customApply(battle: Battle, ship: Ship, target: Target): boolean {
|
||||
|
|
|
@ -151,15 +151,16 @@ module SpaceTac.Game.AI {
|
|||
return null;
|
||||
}
|
||||
|
||||
var MIN_DISTANCE = 20;
|
||||
var APPROACH_FACTOR = 0.5;
|
||||
|
||||
var picked = this.random.choice(enemies);
|
||||
var target = Target.newFromShip(picked);
|
||||
var distance = target.getDistanceTo(Target.newFromShip(this.ship));
|
||||
var engine = this.getEngine();
|
||||
if (distance > MIN_DISTANCE) { // Don't move too close
|
||||
target = target.constraintInRange(this.ship.arena_x, this.ship.arena_y, (distance - MIN_DISTANCE) * APPROACH_FACTOR);
|
||||
var safety_distance = (<MoveAction>engine.action).safety_distance;
|
||||
if (distance > safety_distance) { // Don't move too close
|
||||
target = target.constraintInRange(this.ship.arena_x, this.ship.arena_y,
|
||||
(distance - safety_distance) * APPROACH_FACTOR);
|
||||
target = engine.action.checkLocationTarget(this.fleet.battle, this.ship, target);
|
||||
return new BullyManeuver(new Maneuver(this.ship, engine, target));
|
||||
} else {
|
||||
|
|
|
@ -170,7 +170,8 @@ module SpaceTac.Game.AI.Specs {
|
|||
ai.ship = battle.fleets[0].ships[0];
|
||||
|
||||
TestTools.setShipAP(ai.ship, 5);
|
||||
TestTools.addEngine(ai.ship, 100);
|
||||
var engine = TestTools.addEngine(ai.ship, 100);
|
||||
(<MoveAction>engine.action).safety_distance = 20;
|
||||
|
||||
var maneuver: BullyManeuver;
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@ module SpaceTac.Game {
|
|||
|
||||
describe("MoveAction", function () {
|
||||
it("checks movement against remaining AP", function () {
|
||||
var ship = new Ship(null, "Test");
|
||||
var ship = new Ship();
|
||||
var battle = new Battle(ship.fleet);
|
||||
battle.playing_ship = ship;
|
||||
ship.ap_current.setMaximal(20);
|
||||
ship.ap_current.set(6);
|
||||
ship.arena_x = 0;
|
||||
|
@ -15,14 +17,14 @@ module SpaceTac.Game {
|
|||
engine.ap_usage = 2;
|
||||
var action = new MoveAction(engine);
|
||||
|
||||
var result = action.checkTarget(null, ship, Target.newFromLocation(0, 2));
|
||||
var result = action.checkTarget(battle, ship, Target.newFromLocation(0, 2));
|
||||
expect(result).toEqual(Target.newFromLocation(0, 2));
|
||||
|
||||
result = action.checkTarget(null, ship, Target.newFromLocation(0, 8));
|
||||
result = action.checkTarget(battle, ship, Target.newFromLocation(0, 8));
|
||||
expect(result).toEqual(Target.newFromLocation(0, 3));
|
||||
|
||||
ship.ap_current.set(0);
|
||||
result = action.checkTarget(null, ship, Target.newFromLocation(0, 8));
|
||||
result = action.checkTarget(battle, ship, Target.newFromLocation(0, 8));
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
|
@ -76,5 +78,33 @@ module SpaceTac.Game {
|
|||
expect((<AttributeChangeEvent>battle.log.events[1]).attribute).toEqual(
|
||||
new Attribute(AttributeCode.AP, 0, 20));
|
||||
});
|
||||
|
||||
it("can't move too much near another ship", function () {
|
||||
var battle = TestTools.createBattle(1, 1);
|
||||
var ship = battle.fleets[0].ships[0];
|
||||
var enemy = battle.fleets[1].ships[0];
|
||||
var engine = TestTools.addEngine(ship, 10);
|
||||
TestTools.setShipAP(ship, 100);
|
||||
ship.setArenaPosition(5, 5);
|
||||
enemy.setArenaPosition(10, 5);
|
||||
|
||||
var action = new MoveAction(engine);
|
||||
action.safety_distance = 2;
|
||||
|
||||
var result = action.checkLocationTarget(battle, ship, Target.newFromLocation(7, 5));
|
||||
expect(result).toEqual(Target.newFromLocation(7, 5));
|
||||
|
||||
result = action.checkLocationTarget(battle, ship, Target.newFromLocation(8, 5));
|
||||
expect(result).toEqual(Target.newFromLocation(8, 5));
|
||||
|
||||
result = action.checkLocationTarget(battle, ship, Target.newFromLocation(9, 5));
|
||||
expect(result).toEqual(Target.newFromLocation(8, 5));
|
||||
|
||||
result = action.checkLocationTarget(battle, ship, Target.newFromLocation(10, 5));
|
||||
expect(result).toEqual(Target.newFromLocation(8, 5));
|
||||
|
||||
result = action.checkLocationTarget(battle, ship, Target.newFromLocation(12, 5));
|
||||
expect(result).toEqual(Target.newFromLocation(12, 5));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -39,5 +39,16 @@ module SpaceTac.Game.Specs {
|
|||
expect(target.constraintInRange(1, 1, Math.sqrt(80) * 0.5)).toEqual(Target.newFromLocation(3, 5));
|
||||
expect(target.constraintInRange(1, 1, 70)).toBe(target);
|
||||
});
|
||||
|
||||
it("pushes a target out of a given circle", () => {
|
||||
var target = Target.newFromLocation(5, 5);
|
||||
expect(target.moveOutOfCircle(0, 0, 3, 0, 0)).toBe(target);
|
||||
expect(target.moveOutOfCircle(6, 6, 3, 0, 0)).toEqual(Target.newFromLocation(3.8786796564403576, 3.8786796564403576));
|
||||
expect(target.moveOutOfCircle(4, 4, 3, 10, 10)).toEqual(Target.newFromLocation(6.121320343559642, 6.121320343559642));
|
||||
expect(target.moveOutOfCircle(5, 8, 6, 5, 0)).toEqual(Target.newFromLocation(5, 2));
|
||||
expect(target.moveOutOfCircle(5, 2, 6, 5, 10)).toEqual(Target.newFromLocation(5, 8));
|
||||
expect(target.moveOutOfCircle(8, 5, 6, 0, 5)).toEqual(Target.newFromLocation(2, 5));
|
||||
expect(target.moveOutOfCircle(2, 5, 6, 10, 5)).toEqual(Target.newFromLocation(8, 5));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue