2015-02-16 00:00:00 +00:00
|
|
|
/// <reference path="AbstractAI.ts"/>
|
2017-02-09 00:26:04 +00:00
|
|
|
module TS.SpaceTac {
|
2015-03-12 00:00:00 +00:00
|
|
|
// Combination of a move action and a fire action
|
|
|
|
export class BullyManeuver {
|
|
|
|
// Move action to position the ship before firing
|
2015-03-12 00:00:00 +00:00
|
|
|
move: Maneuver;
|
2015-02-17 00:00:00 +00:00
|
|
|
|
2015-03-12 00:00:00 +00:00
|
|
|
// Fire action
|
2015-03-12 00:00:00 +00:00
|
|
|
fire: Maneuver;
|
2015-02-19 00:00:00 +00:00
|
|
|
|
2015-03-12 00:00:00 +00:00
|
|
|
constructor(move: Maneuver = null, fire: Maneuver = null) {
|
2015-03-12 00:00:00 +00:00
|
|
|
this.move = move;
|
|
|
|
this.fire = fire;
|
|
|
|
}
|
2015-02-18 00:00:00 +00:00
|
|
|
|
|
|
|
// Get a sorting score, by distance to another point
|
|
|
|
// Nearest means higher score
|
|
|
|
getScoreByDistance(point: Target): number {
|
2015-03-12 00:00:00 +00:00
|
|
|
return -point.getDistanceTo(this.fire.target);
|
2015-02-18 00:00:00 +00:00
|
|
|
}
|
2015-02-17 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
2015-02-16 00:00:00 +00:00
|
|
|
// Basic Artificial Intelligence, with a tendency to move forward and shoot the nearest enemy
|
|
|
|
export class BullyAI extends AbstractAI {
|
2015-02-20 00:00:00 +00:00
|
|
|
// Safety margin in moves to account for floating-point rounding errors
|
|
|
|
move_margin: number;
|
|
|
|
|
2015-02-16 00:00:00 +00:00
|
|
|
constructor(fleet: Fleet) {
|
|
|
|
super(fleet);
|
2015-02-20 00:00:00 +00:00
|
|
|
|
|
|
|
this.move_margin = 0.1;
|
2015-02-16 00:00:00 +00:00
|
|
|
}
|
2015-02-17 00:00:00 +00:00
|
|
|
|
2015-02-17 00:00:00 +00:00
|
|
|
protected initWork(): void {
|
|
|
|
this.addWorkItem(() => {
|
2015-03-12 00:00:00 +00:00
|
|
|
var maneuvers = this.listAllManeuvers();
|
2015-04-15 00:00:00 +00:00
|
|
|
var maneuver: BullyManeuver;
|
2015-02-17 00:00:00 +00:00
|
|
|
|
2015-03-12 00:00:00 +00:00
|
|
|
if (maneuvers.length > 0) {
|
2015-04-15 00:00:00 +00:00
|
|
|
maneuver = this.pickManeuver(maneuvers);
|
2015-03-12 00:00:00 +00:00
|
|
|
this.applyManeuver(maneuver);
|
2015-02-17 00:00:00 +00:00
|
|
|
|
2015-03-12 00:00:00 +00:00
|
|
|
// Try to make another maneuver
|
2015-02-17 00:00:00 +00:00
|
|
|
this.initWork();
|
2015-04-15 00:00:00 +00:00
|
|
|
} else {
|
|
|
|
// No bullying available, going to fallback move
|
|
|
|
maneuver = this.getFallbackManeuver();
|
|
|
|
this.applyManeuver(maneuver);
|
2015-02-17 00:00:00 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-02-17 00:00:00 +00:00
|
|
|
// List all enemy ships that can be a target
|
|
|
|
listAllEnemies(): Ship[] {
|
|
|
|
var result: Ship[] = [];
|
|
|
|
|
|
|
|
this.fleet.battle.play_order.forEach((ship: Ship) => {
|
2015-02-17 00:00:00 +00:00
|
|
|
if (ship.alive && ship.getPlayer() !== this.ship.getPlayer()) {
|
2015-02-17 00:00:00 +00:00
|
|
|
result.push(ship);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// List all weapons
|
|
|
|
listAllWeapons(): Equipment[] {
|
|
|
|
return this.ship.listEquipment(SlotType.Weapon);
|
|
|
|
}
|
|
|
|
|
2015-03-12 00:00:00 +00:00
|
|
|
// List all available maneuvers for the playing ship
|
|
|
|
listAllManeuvers(): BullyManeuver[] {
|
|
|
|
var result: BullyManeuver[] = [];
|
2015-02-17 00:00:00 +00:00
|
|
|
|
|
|
|
var enemies = this.listAllEnemies();
|
|
|
|
var weapons = this.listAllWeapons();
|
|
|
|
|
|
|
|
enemies.forEach((ship: Ship) => {
|
|
|
|
weapons.forEach((weapon: Equipment) => {
|
2015-03-12 00:00:00 +00:00
|
|
|
var maneuver = this.checkBullyManeuver(ship, weapon);
|
|
|
|
if (maneuver) {
|
|
|
|
result.push(maneuver);
|
2015-02-17 00:00:00 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-04-15 00:00:00 +00:00
|
|
|
// Get an equipped engine to make a move
|
|
|
|
getEngine(): Equipment {
|
|
|
|
var engines = this.ship.listEquipment(SlotType.Engine);
|
|
|
|
if (engines.length === 0) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
return engines[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-17 00:00:00 +00:00
|
|
|
// Check if a weapon can be used against an enemy
|
2015-03-12 00:00:00 +00:00
|
|
|
// Returns the BullyManeuver, or null if impossible to fire
|
|
|
|
checkBullyManeuver(enemy: Ship, weapon: Equipment): BullyManeuver {
|
2015-02-17 00:00:00 +00:00
|
|
|
// Check if enemy in range
|
|
|
|
var target = Target.newFromShip(enemy);
|
|
|
|
var distance = target.getDistanceTo(Target.newFromShip(this.ship));
|
|
|
|
var move: Target;
|
2015-02-19 00:00:00 +00:00
|
|
|
var engine: Equipment;
|
2017-02-07 19:15:21 +00:00
|
|
|
var remaining_ap = this.ship.values.power.get();
|
2015-02-17 00:00:00 +00:00
|
|
|
if (distance <= weapon.distance) {
|
|
|
|
// No need to move
|
|
|
|
move = null;
|
|
|
|
} else {
|
|
|
|
// Move to be in range, using first engine
|
2015-04-15 00:00:00 +00:00
|
|
|
engine = this.getEngine();
|
|
|
|
if (!engine) {
|
2015-02-17 00:00:00 +00:00
|
|
|
// No engine available to move
|
|
|
|
return null;
|
|
|
|
} else {
|
2015-02-20 00:00:00 +00:00
|
|
|
var move_distance = distance - weapon.distance + this.move_margin;
|
2015-02-17 00:00:00 +00:00
|
|
|
var move_ap = engine.ap_usage * move_distance / engine.distance;
|
2015-02-17 00:00:00 +00:00
|
|
|
if (move_ap > remaining_ap) {
|
|
|
|
// Not enough AP to move in range
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
move = target.constraintInRange(this.ship.arena_x, this.ship.arena_y, move_distance);
|
|
|
|
remaining_ap -= move_ap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check fire
|
|
|
|
if (weapon.ap_usage > remaining_ap) {
|
|
|
|
// Not enough AP to fire
|
|
|
|
return null;
|
|
|
|
} else {
|
2015-03-12 00:00:00 +00:00
|
|
|
var result = new BullyManeuver();
|
|
|
|
if (move) {
|
2015-03-12 00:00:00 +00:00
|
|
|
result.move = new Maneuver(this.ship, engine, move);
|
2015-03-12 00:00:00 +00:00
|
|
|
}
|
2015-03-12 00:00:00 +00:00
|
|
|
result.fire = new Maneuver(this.ship, weapon, target);
|
2015-02-17 00:00:00 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
2015-02-17 00:00:00 +00:00
|
|
|
|
2015-04-15 00:00:00 +00:00
|
|
|
// When no bully action is available, pick a random enemy, and go towards it
|
|
|
|
getFallbackManeuver(): BullyManeuver {
|
|
|
|
var enemies = this.listAllEnemies();
|
|
|
|
if (enemies.length === 0) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2015-04-21 20:14:17 +00:00
|
|
|
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);
|
2015-04-15 00:00:00 +00:00
|
|
|
target = engine.action.checkLocationTarget(this.fleet.battle, this.ship, target);
|
|
|
|
return new BullyManeuver(new Maneuver(this.ship, engine, target));
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-12 00:00:00 +00:00
|
|
|
// Pick a maneuver from a list of available ones
|
2015-02-18 00:00:00 +00:00
|
|
|
// By default, it chooses the nearest enemy
|
2015-03-12 00:00:00 +00:00
|
|
|
pickManeuver(available: BullyManeuver[]): BullyManeuver {
|
2015-02-18 00:00:00 +00:00
|
|
|
if (available.length === 0) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort by descending score
|
2015-03-12 00:00:00 +00:00
|
|
|
available.sort((m1: BullyManeuver, m2: BullyManeuver): number => {
|
2015-02-18 00:00:00 +00:00
|
|
|
var point = Target.newFromShip(this.ship);
|
|
|
|
return m1.getScoreByDistance(point) < m2.getScoreByDistance(point) ? 1 : -1;
|
|
|
|
});
|
|
|
|
return available[0];
|
|
|
|
}
|
|
|
|
|
2015-03-12 00:00:00 +00:00
|
|
|
// Effectively apply the chosen maneuver
|
|
|
|
applyManeuver(maneuver: BullyManeuver): void {
|
|
|
|
if (maneuver.move) {
|
2015-02-17 00:00:00 +00:00
|
|
|
this.addWorkItem(() => {
|
2015-03-12 00:00:00 +00:00
|
|
|
maneuver.move.apply();
|
2015-02-20 00:00:00 +00:00
|
|
|
}, 500);
|
2015-02-17 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
2015-04-15 00:00:00 +00:00
|
|
|
if (maneuver.fire) {
|
|
|
|
this.addWorkItem(() => {
|
|
|
|
maneuver.fire.apply();
|
|
|
|
}, 1500);
|
|
|
|
}
|
2015-02-17 00:00:00 +00:00
|
|
|
|
2015-02-20 00:00:00 +00:00
|
|
|
this.addWorkItem(null, 1500);
|
2015-02-17 00:00:00 +00:00
|
|
|
}
|
2015-02-16 00:00:00 +00:00
|
|
|
}
|
|
|
|
}
|