1
0
Fork 0

Added collision prevention on move actions

This commit is contained in:
Michaël Lemaire 2015-04-21 22:14:17 +02:00
parent df9091b64a
commit cb278959a9
8 changed files with 109 additions and 11 deletions

3
TODO Normal file
View 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

View file

@ -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);
}
}
}
}

View file

@ -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

View file

@ -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 {

View file

@ -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 {

View file

@ -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;

View file

@ -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));
});
});
}

View file

@ -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));
});
});
}