Incomplete WIP
This commit is contained in:
parent
aa640e23f2
commit
0e3e9bc199
|
@ -82,9 +82,9 @@ module TK.SpaceTac.Specs {
|
||||||
ship2.setArenaPosition(1000, 1000);
|
ship2.setArenaPosition(1000, 1000);
|
||||||
TestTools.setShipModel(ship2, 10);
|
TestTools.setShipModel(ship2, 10);
|
||||||
|
|
||||||
let vig1 = ship1.actions.addCustom(new VigilanceAction("Vig1", { radius: 100, filter: ActionTargettingFilter.ENEMIES }, { intruder_effects: [new DamageEffect(1)] }));
|
let vig1 = ship1.actions.addCustom(new VigilanceAction("Vig1", { radius: 100, filter: ActionImpactFilter.ENEMIES }, { intruder_effects: [new DamageEffect(1)] }));
|
||||||
let vig2 = ship1.actions.addCustom(new VigilanceAction("Vig2", { radius: 50, filter: ActionTargettingFilter.ENEMIES }, { intruder_effects: [new DamageEffect(2)] }));
|
let vig2 = ship1.actions.addCustom(new VigilanceAction("Vig2", { radius: 50, filter: ActionImpactFilter.ENEMIES }, { intruder_effects: [new DamageEffect(2)] }));
|
||||||
let vig3 = ship1.actions.addCustom(new VigilanceAction("Vig3", { radius: 100, filter: ActionTargettingFilter.ALLIES }, { intruder_effects: [new DamageEffect(3)] }));
|
let vig3 = ship1.actions.addCustom(new VigilanceAction("Vig3", { radius: 100, filter: ActionImpactFilter.ALLIES }, { intruder_effects: [new DamageEffect(3)] }));
|
||||||
battle.applyOneAction(vig1.id);
|
battle.applyOneAction(vig1.id);
|
||||||
battle.applyOneAction(vig2.id);
|
battle.applyOneAction(vig2.id);
|
||||||
battle.applyOneAction(vig3.id);
|
battle.applyOneAction(vig3.id);
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
module TK.SpaceTac.Specs {
|
|
||||||
testing("Target", test => {
|
|
||||||
test.case("initializes from ship or location", check => {
|
|
||||||
var target: Target;
|
|
||||||
|
|
||||||
target = Target.newFromLocation(2, 3);
|
|
||||||
check.equals(target.x, 2);
|
|
||||||
check.equals(target.y, 3);
|
|
||||||
check.equals(target.ship_id, null);
|
|
||||||
|
|
||||||
var ship = new Ship();
|
|
||||||
ship.arena_x = 4;
|
|
||||||
ship.arena_y = -2.1;
|
|
||||||
target = Target.newFromShip(ship);
|
|
||||||
check.equals(target.x, 4);
|
|
||||||
check.equals(target.y, -2.1);
|
|
||||||
check.equals(target.ship_id, ship.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
test.case("gets distance to another target", check => {
|
|
||||||
var t1 = Target.newFromLocation(5, 1);
|
|
||||||
var t2 = Target.newFromLocation(6, 2);
|
|
||||||
check.nears(t1.getDistanceTo(t2), Math.sqrt(2));
|
|
||||||
});
|
|
||||||
|
|
||||||
test.case("gets angle to another target", check => {
|
|
||||||
var t1 = Target.newFromLocation(2, 3);
|
|
||||||
var t2 = Target.newFromLocation(4, 5);
|
|
||||||
check.nears(t1.getAngleTo(t2), Math.PI / 4);
|
|
||||||
});
|
|
||||||
|
|
||||||
test.case("checks if a target is in range of another", check => {
|
|
||||||
var t1 = Target.newFromLocation(5, 4);
|
|
||||||
check.equals(t1.isInRange(7, 3, 2), false);
|
|
||||||
check.equals(t1.isInRange(7, 3, 3), true);
|
|
||||||
check.equals(t1.isInRange(5, 5, 2), true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test.case("constraints a target to a limited range", check => {
|
|
||||||
var target = Target.newFromLocation(5, 9);
|
|
||||||
check.equals(target.constraintInRange(1, 1, Math.sqrt(80) * 0.5), Target.newFromLocation(3, 5));
|
|
||||||
check.same(target.constraintInRange(1, 1, 70), target);
|
|
||||||
});
|
|
||||||
|
|
||||||
test.case("pushes a target out of a given circle", check => {
|
|
||||||
var target = Target.newFromLocation(5, 5);
|
|
||||||
check.same(target.moveOutOfCircle(0, 0, 3, 0, 0), target);
|
|
||||||
check.equals(target.moveOutOfCircle(6, 6, 3, 0, 0), Target.newFromLocation(3.8786796564403576, 3.8786796564403576));
|
|
||||||
check.equals(target.moveOutOfCircle(4, 4, 3, 10, 10), Target.newFromLocation(6.121320343559642, 6.121320343559642));
|
|
||||||
check.equals(target.moveOutOfCircle(5, 8, 6, 5, 0), Target.newFromLocation(5, 2));
|
|
||||||
check.equals(target.moveOutOfCircle(5, 2, 6, 5, 10), Target.newFromLocation(5, 8));
|
|
||||||
check.equals(target.moveOutOfCircle(8, 5, 6, 0, 5), Target.newFromLocation(2, 5));
|
|
||||||
check.equals(target.moveOutOfCircle(2, 5, 6, 10, 5), Target.newFromLocation(8, 5));
|
|
||||||
});
|
|
||||||
|
|
||||||
test.case("keeps a target inside a rectangle", check => {
|
|
||||||
var target = Target.newFromLocation(5, 5);
|
|
||||||
check.same(target.keepInsideRectangle(0, 0, 10, 10, 0, 0), target);
|
|
||||||
check.equals(target.keepInsideRectangle(8, 0, 13, 10, 10, 5), Target.newFromLocation(8, 5));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,174 +0,0 @@
|
||||||
module TK.SpaceTac {
|
|
||||||
// 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] | null {
|
|
||||||
let a = y2 - y1;
|
|
||||||
let b = -(x2 - x1);
|
|
||||||
let c = -(a * x1 + b * y1);
|
|
||||||
let x0 = -a * c / (a * a + b * b), y0 = -b * c / (a * a + b * b);
|
|
||||||
let EPS = 10e-8;
|
|
||||||
if (c * c > r * r * (a * a + b * b) + EPS) {
|
|
||||||
return null;
|
|
||||||
} else if (Math.abs(c * c - r * r * (a * a + b * b)) < EPS) {
|
|
||||||
return [x0, y0];
|
|
||||||
} else {
|
|
||||||
let d = r * r - c * c / (a * a + b * b);
|
|
||||||
let mult = Math.sqrt(d / (a * a + b * b));
|
|
||||||
let ax, ay, bx, by;
|
|
||||||
ax = x0 + b * mult;
|
|
||||||
bx = x0 - b * mult;
|
|
||||||
ay = y0 - a * mult;
|
|
||||||
by = y0 + a * mult;
|
|
||||||
|
|
||||||
let candidates: [number, number][] = [
|
|
||||||
[x0 + b * mult, y0 - a * mult],
|
|
||||||
[x0 - b * mult, y0 + a * mult]
|
|
||||||
]
|
|
||||||
return minBy(candidates, ([x, y]) => Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Target for a capability
|
|
||||||
// This could be a location in space, or a ship
|
|
||||||
export class Target {
|
|
||||||
// Coordinates of the target
|
|
||||||
x: number
|
|
||||||
y: number
|
|
||||||
|
|
||||||
// If the target is a ship, this attribute will be set
|
|
||||||
ship_id: RObjectId | null
|
|
||||||
|
|
||||||
// Standard constructor
|
|
||||||
constructor(x: number, y: number, ship: Ship | null = null) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.ship_id = ship ? ship.id : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
jasmineToString() {
|
|
||||||
if (this.ship_id) {
|
|
||||||
return `(${this.x},${this.y}) ship_id=${this.ship_id}}`;
|
|
||||||
} else {
|
|
||||||
return `(${this.x},${this.y})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructor to target a single ship
|
|
||||||
static newFromShip(ship: Ship): Target {
|
|
||||||
return new Target(ship.arena_x, ship.arena_y, ship);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructor to target a location in space
|
|
||||||
static newFromLocation(x: number, y: number): Target {
|
|
||||||
return new Target(x, y, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get distance to another target
|
|
||||||
getDistanceTo(other: { x: number, y: number }): number {
|
|
||||||
var dx = other.x - this.x;
|
|
||||||
var dy = other.y - this.y;
|
|
||||||
return Math.sqrt(dx * dx + dy * dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the normalized angle, in radians, to another target
|
|
||||||
getAngleTo(other: { x: number, y: number }): number {
|
|
||||||
var dx = other.x - this.x;
|
|
||||||
var dy = other.y - this.y;
|
|
||||||
return Math.atan2(dy, dx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the target is a ship
|
|
||||||
*/
|
|
||||||
isShip(): boolean {
|
|
||||||
return this.ship_id !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the targetted ship in a battle
|
|
||||||
*/
|
|
||||||
getShip(battle: Battle): Ship | null {
|
|
||||||
if (this.isShip()) {
|
|
||||||
return battle.getShip(this.ship_id);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a target is in range from a specific point
|
|
||||||
isInRange(x: number, y: number, radius: number): boolean {
|
|
||||||
var dx = this.x - x;
|
|
||||||
var dy = this.y - y;
|
|
||||||
var length = Math.sqrt(dx * dx + dy * dy);
|
|
||||||
return (length <= radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constraint a target, to be in a given range from a specific point
|
|
||||||
// May return the original target if it's already in radius
|
|
||||||
constraintInRange(x: number, y: number, radius: number): Target {
|
|
||||||
var dx = this.x - x;
|
|
||||||
var dy = this.y - y;
|
|
||||||
var length = Math.sqrt(dx * dx + dy * dy);
|
|
||||||
if (length <= radius) {
|
|
||||||
return this;
|
|
||||||
} else {
|
|
||||||
var factor = radius / length;
|
|
||||||
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, dx, dy, radius);
|
|
||||||
if (res) {
|
|
||||||
return Target.newFromLocation(res[0] + circlex, res[1] + circley);
|
|
||||||
} else {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keep the target inside a rectangle
|
|
||||||
*
|
|
||||||
* May return the original target if it's already inside the rectangle
|
|
||||||
*/
|
|
||||||
keepInsideRectangle(xmin: number, ymin: number, xmax: number, ymax: number, sourcex: number, sourcey: number): Target {
|
|
||||||
let length = this.getDistanceTo({ x: sourcex, y: sourcey });
|
|
||||||
let result: Target = this;
|
|
||||||
if (result.x < xmin) {
|
|
||||||
let factor = (xmin - sourcex) / (result.x - sourcex);
|
|
||||||
length *= factor;
|
|
||||||
result = result.constraintInRange(sourcex, sourcey, length);
|
|
||||||
}
|
|
||||||
if (result.x > xmax) {
|
|
||||||
let factor = (xmax - sourcex) / (result.x - sourcex);
|
|
||||||
length *= factor;
|
|
||||||
result = result.constraintInRange(sourcex, sourcey, length);
|
|
||||||
}
|
|
||||||
if (result.y < ymin) {
|
|
||||||
let factor = (ymin - sourcey) / (result.y - sourcey);
|
|
||||||
length *= factor;
|
|
||||||
result = result.constraintInRange(sourcex, sourcey, length);
|
|
||||||
}
|
|
||||||
if (result.y > ymax) {
|
|
||||||
let factor = (ymax - sourcey) / (result.y - sourcey);
|
|
||||||
length *= factor;
|
|
||||||
result = result.constraintInRange(sourcex, sourcey, length);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -68,15 +68,15 @@ module TK.SpaceTac.Specs {
|
||||||
let ship2b = fleet2.addShip();
|
let ship2b = fleet2.addShip();
|
||||||
let ships = [ship1a, ship1b, ship2a, ship2b];
|
let ships = [ship1a, ship1b, ship2a, ship2b];
|
||||||
|
|
||||||
check.equals(BaseAction.filterTargets(ship1a, ships, ActionTargettingFilter.ALL),
|
check.equals(BaseAction.filterTargets(ship1a, ships, ActionImpactFilter.ALL),
|
||||||
[ship1a, ship1b, ship2a, ship2b], "ALL");
|
[ship1a, ship1b, ship2a, ship2b], "ALL");
|
||||||
check.equals(BaseAction.filterTargets(ship1a, ships, ActionTargettingFilter.ALL_BUT_SELF),
|
check.equals(BaseAction.filterTargets(ship1a, ships, ActionImpactFilter.ALL_BUT_SELF),
|
||||||
[ship1b, ship2a, ship2b], "ALL_BUT_SELF");
|
[ship1b, ship2a, ship2b], "ALL_BUT_SELF");
|
||||||
check.equals(BaseAction.filterTargets(ship1a, ships, ActionTargettingFilter.ALLIES),
|
check.equals(BaseAction.filterTargets(ship1a, ships, ActionImpactFilter.ALLIES),
|
||||||
[ship1a, ship1b], "ALLIES");
|
[ship1a, ship1b], "ALLIES");
|
||||||
check.equals(BaseAction.filterTargets(ship1a, ships, ActionTargettingFilter.ALLIES_BUT_SELF),
|
check.equals(BaseAction.filterTargets(ship1a, ships, ActionImpactFilter.ALLIES_BUT_SELF),
|
||||||
[ship1b], "ALLIES_BUT_SELF");
|
[ship1b], "ALLIES_BUT_SELF");
|
||||||
check.equals(BaseAction.filterTargets(ship1a, ships, ActionTargettingFilter.ENEMIES),
|
check.equals(BaseAction.filterTargets(ship1a, ships, ActionImpactFilter.ENEMIES),
|
||||||
[ship2a, ship2b], "ENEMIES");
|
[ship2a, ship2b], "ENEMIES");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,33 +5,34 @@ module TK.SpaceTac {
|
||||||
export enum ActionCategory {
|
export enum ActionCategory {
|
||||||
MOVE,
|
MOVE,
|
||||||
PASSIVE,
|
PASSIVE,
|
||||||
ACTIVE
|
ACTIVE,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Targetting mode for an action.
|
* Target for an action
|
||||||
*
|
|
||||||
* This is a hint as to what type of target is required for this action.
|
|
||||||
*/
|
*/
|
||||||
export enum ActionTargettingMode {
|
export type ActionTarget = Readonly<{
|
||||||
// Apply immediately on the ship owning the action, without confirmation
|
distance?: number
|
||||||
|
angle?: number
|
||||||
|
}>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target for an action, as applied from an hypothetical location
|
||||||
|
*/
|
||||||
|
export type ActionTargetFrom = Readonly<{
|
||||||
|
location: IArenaLocation
|
||||||
|
distance?: number
|
||||||
|
angle?: number
|
||||||
|
}>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Impact filter for an action.
|
||||||
|
*
|
||||||
|
* This will filter ships inside the targetted area, to determine which ones will receive the action effects.
|
||||||
|
*/
|
||||||
|
export enum ActionImpactFilter {
|
||||||
|
// Apply only on casting ship
|
||||||
SELF,
|
SELF,
|
||||||
// Apply on the ship owning the action, with a confirmation
|
|
||||||
SELF_CONFIRM,
|
|
||||||
// Apply on one selected ship
|
|
||||||
SHIP,
|
|
||||||
// Apply on a space area
|
|
||||||
SPACE,
|
|
||||||
// Apply on the ship owning the action, but has an effect on surroundings
|
|
||||||
SURROUNDINGS
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Targetting filter for an action.
|
|
||||||
*
|
|
||||||
* This will filter ships inside the targetted area, to determine which will receive the action effects.
|
|
||||||
*/
|
|
||||||
export enum ActionTargettingFilter {
|
|
||||||
// Apply on all ships
|
// Apply on all ships
|
||||||
ALL,
|
ALL,
|
||||||
// Apply on all ships except the actor
|
// Apply on all ships except the actor
|
||||||
|
@ -41,7 +42,49 @@ module TK.SpaceTac {
|
||||||
// Apply on all allies, except the actor
|
// Apply on all allies, except the actor
|
||||||
ALLIES_BUT_SELF,
|
ALLIES_BUT_SELF,
|
||||||
// Apply on all enemies
|
// Apply on all enemies
|
||||||
ENEMIES
|
ENEMIES,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Priority of ships in the target area.
|
||||||
|
*
|
||||||
|
* If there are more ships that the action limit, the most prioritized ones will be affected.
|
||||||
|
*/
|
||||||
|
export enum ActionImpactPriority {
|
||||||
|
// Nearest ships are priority
|
||||||
|
NEAREST,
|
||||||
|
// Farthest ships are priority
|
||||||
|
FARTHEST,
|
||||||
|
// Ships with the most hull are priority
|
||||||
|
TOUGHEST,
|
||||||
|
// Ships with the less hull are priority
|
||||||
|
WEAKEST,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for the target of an action
|
||||||
|
*/
|
||||||
|
export interface BaseActionConfig {
|
||||||
|
// Identifying code (for assets and effects)
|
||||||
|
code: string
|
||||||
|
// Human-friendly name
|
||||||
|
name: string
|
||||||
|
// Power cost
|
||||||
|
power_cost?: number
|
||||||
|
// Maximal distance the target can be set
|
||||||
|
min_distance?: number
|
||||||
|
// Minimal distance the target can be set
|
||||||
|
max_distance?: number
|
||||||
|
// Angle that will span the affected area (around the target direction)
|
||||||
|
angular_span?: number
|
||||||
|
// Radius of the affected area (around the target location)
|
||||||
|
radius?: number
|
||||||
|
// Filtering of ships in the affected area
|
||||||
|
ship_filter?: ActionImpactFilter
|
||||||
|
// Maximal number of ships impacted in the affected area
|
||||||
|
ship_limit?: number
|
||||||
|
// Priority of ships in affected area
|
||||||
|
ship_priority?: ActionImpactPriority
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,22 +108,28 @@ module TK.SpaceTac {
|
||||||
*
|
*
|
||||||
* An action should be the only way to modify a battle state.
|
* An action should be the only way to modify a battle state.
|
||||||
*/
|
*/
|
||||||
export class BaseAction extends RObject {
|
export class BaseAction extends RObject implements BaseActionConfig {
|
||||||
// Identifier code for the type of action
|
code = "nothing"
|
||||||
readonly code: string
|
name = "Nothing"
|
||||||
|
power_cost = 0
|
||||||
// Full name of the action
|
min_distance = 0
|
||||||
readonly name: string
|
max_distance = Infinity
|
||||||
|
angular_span = undefined
|
||||||
|
radius = undefined
|
||||||
|
ship_filter = ActionImpactFilter.ALL
|
||||||
|
ship_limit = undefined
|
||||||
|
ship_priority = ActionImpactPriority.NEAREST
|
||||||
|
|
||||||
// Cooldown configuration
|
// Cooldown configuration
|
||||||
private cooldown = new Cooldown()
|
private cooldown = new Cooldown()
|
||||||
|
|
||||||
// Create the action
|
// Create the action
|
||||||
constructor(name = "Nothing", code?: string) {
|
constructor(config?: Partial<Readonly<BaseActionConfig>>) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.code = code ? code : name.toLowerCase().replace(" ", "");
|
if (config) {
|
||||||
this.name = name;
|
this.configure(config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,17 +154,17 @@ module TK.SpaceTac {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the targetting mode
|
* Get a default target for this action
|
||||||
*/
|
*/
|
||||||
getTargettingMode(ship: Ship): ActionTargettingMode {
|
getDefaultTarget(ship: Ship): ActionTargetFrom {
|
||||||
return ActionTargettingMode.SELF;
|
return { location: ship.location };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a default target for this action
|
* Configure the base settings for this action
|
||||||
*/
|
*/
|
||||||
getDefaultTarget(ship: Ship): Target {
|
configure(config: Partial<Readonly<BaseActionConfig>>): void {
|
||||||
return Target.newFromShip(ship);
|
copyfields(config, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,13 +183,13 @@ module TK.SpaceTac {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check basic conditions to know if the ship can use this action at all
|
* Check basic conditions to know if the ship can use this action at all, not accounting for power
|
||||||
*
|
*
|
||||||
* Method to extend to set conditions
|
* Method to extend to set conditions
|
||||||
*
|
*
|
||||||
* Returns an unavalability reason, null otherwise
|
* Returns an unavalability reason, null otherwise
|
||||||
*/
|
*/
|
||||||
checkCannotBeApplied(ship: Ship, remaining_ap: number | null = null): ActionUnavailability | null {
|
checkCannotBeApplied(ship: Ship): ActionUnavailability | null {
|
||||||
if (!ship.actions.getById(this.id)) {
|
if (!ship.actions.getById(this.id)) {
|
||||||
return ActionUnavailability.NO_SUCH_ACTION;
|
return ActionUnavailability.NO_SUCH_ACTION;
|
||||||
}
|
}
|
||||||
|
@ -154,17 +203,10 @@ module TK.SpaceTac {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the power cost of this action
|
* Get the power cost of this action for the current state
|
||||||
*/
|
*/
|
||||||
getPowerUsage(ship: Ship): number {
|
getPowerUsage(ship: Ship): number {
|
||||||
return 0;
|
return this.power_cost;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the range of this action, for targetting purpose
|
|
||||||
*/
|
|
||||||
getRangeRadius(ship: Ship): number {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -172,36 +214,20 @@ module TK.SpaceTac {
|
||||||
*
|
*
|
||||||
* This may be used as an indicator for helping the player in targetting, or to effectively apply the effects
|
* This may be used as an indicator for helping the player in targetting, or to effectively apply the effects
|
||||||
*/
|
*/
|
||||||
filterImpactedShips(ship: Ship, source: IArenaLocation, target: Target, ships: Ship[]): Ship[] {
|
filterImpactedShips(ships: readonly Ship[], source: Ship, target: ActionTargetFrom): readonly Ship[] {
|
||||||
return [];
|
// TODO Allow to work with ghosts (location instead of ships?)
|
||||||
}
|
// TODO Apply radius and angle
|
||||||
|
// TODO Apply limit and priority
|
||||||
/**
|
|
||||||
* Get a list of ships impacted by this action
|
|
||||||
*/
|
|
||||||
getImpactedShips(ship: Ship, target: Target, source: IArenaLocation = ship.location): Ship[] {
|
|
||||||
let battle = ship.getBattle();
|
|
||||||
if (battle) {
|
|
||||||
return this.filterImpactedShips(ship, source, target, imaterialize(battle.iships(true)));
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to apply a targetting filter on a list of ships, to determine which ones are impacted
|
|
||||||
*/
|
|
||||||
static filterTargets(source: Ship, ships: Ship[], filter: ActionTargettingFilter): Ship[] {
|
|
||||||
return ships.filter(ship => {
|
return ships.filter(ship => {
|
||||||
if (filter == ActionTargettingFilter.ALL) {
|
if (this.ship_filter == ActionImpactFilter.ALL) {
|
||||||
return true;
|
return true;
|
||||||
} else if (filter == ActionTargettingFilter.ALL_BUT_SELF) {
|
} else if (this.ship_filter == ActionImpactFilter.ALL_BUT_SELF) {
|
||||||
return !ship.is(source);
|
return !ship.is(source);
|
||||||
} else if (filter == ActionTargettingFilter.ALLIES) {
|
} else if (this.ship_filter == ActionImpactFilter.ALLIES) {
|
||||||
return ship.fleet.player.is(source.fleet.player);
|
return ship.fleet.player.is(source.fleet.player);
|
||||||
} else if (filter == ActionTargettingFilter.ALLIES_BUT_SELF) {
|
} else if (this.ship_filter == ActionImpactFilter.ALLIES_BUT_SELF) {
|
||||||
return ship.fleet.player.is(source.fleet.player) && !ship.is(source);
|
return ship.fleet.player.is(source.fleet.player) && !ship.is(source);
|
||||||
} else if (filter == ActionTargettingFilter.ENEMIES) {
|
} else if (this.ship_filter == ActionImpactFilter.ENEMIES) {
|
||||||
return !ship.fleet.player.is(source.fleet.player);
|
return !ship.fleet.player.is(source.fleet.player);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -212,16 +238,17 @@ module TK.SpaceTac {
|
||||||
/**
|
/**
|
||||||
* Get a name to represent the group of ships specified by a target filter
|
* Get a name to represent the group of ships specified by a target filter
|
||||||
*/
|
*/
|
||||||
static getFilterDesc(filter: ActionTargettingFilter, plural = true): string {
|
static getFilterDesc(filter: ActionImpactFilter, plural = true): string {
|
||||||
if (filter == ActionTargettingFilter.ALL) {
|
// TODO limit and priority
|
||||||
|
if (filter == ActionImpactFilter.ALL) {
|
||||||
return plural ? "ships" : "ship";
|
return plural ? "ships" : "ship";
|
||||||
} else if (filter == ActionTargettingFilter.ALL_BUT_SELF) {
|
} else if (filter == ActionImpactFilter.ALL_BUT_SELF) {
|
||||||
return plural ? "other ships" : "other ship";
|
return plural ? "other ships" : "other ship";
|
||||||
} else if (filter == ActionTargettingFilter.ALLIES) {
|
} else if (filter == ActionImpactFilter.ALLIES) {
|
||||||
return plural ? "team members" : "team member";
|
return plural ? "team members" : "team member";
|
||||||
} else if (filter == ActionTargettingFilter.ALLIES_BUT_SELF) {
|
} else if (filter == ActionImpactFilter.ALLIES_BUT_SELF) {
|
||||||
return plural ? "teammates" : "teammates";
|
return plural ? "teammates" : "teammates";
|
||||||
} else if (filter == ActionTargettingFilter.ENEMIES) {
|
} else if (filter == ActionImpactFilter.ENEMIES) {
|
||||||
return plural ? "enemies" : "enemy";
|
return plural ? "enemies" : "enemy";
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
|
@ -231,9 +258,9 @@ module TK.SpaceTac {
|
||||||
/**
|
/**
|
||||||
* Check if a target is suitable for this action
|
* Check if a target is suitable for this action
|
||||||
*
|
*
|
||||||
* Will call checkLocationTarget or checkShipTarget by default
|
* Returns a suggested fixed target (may be the same as the input)
|
||||||
*/
|
*/
|
||||||
checkTarget(ship: Ship, target: Target): Target | null {
|
checkTarget(ship: Ship, target: ActionTargetFrom): ActionTargetFrom | null {
|
||||||
if (this.checkCannotBeApplied(ship)) {
|
if (this.checkCannotBeApplied(ship)) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
|
@ -245,24 +272,12 @@ module TK.SpaceTac {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to reimplement to check if a space target is suitable
|
|
||||||
// Must return null if the target can't be applied, an altered target, or the original target
|
|
||||||
protected checkLocationTarget(ship: Ship, target: Target): Target | null {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to reimplement to check if a ship target is suitable
|
|
||||||
// Must return null if the target can't be applied, an altered target, or the original target
|
|
||||||
protected checkShipTarget(ship: Ship, target: Target): Target | null {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the full list of diffs caused by applying this action
|
* Get the full list of diffs caused by applying this action
|
||||||
*
|
*
|
||||||
* This does not perform any check, and assumes the action is doable
|
* This does not perform any check, and assumes the action is doable
|
||||||
*/
|
*/
|
||||||
getDiffs(ship: Ship, battle: Battle, target = this.getDefaultTarget(ship)): BaseBattleDiff[] {
|
getDiffs(ship: Ship, battle: Battle, target: ActionTargetFrom): BaseBattleDiff[] {
|
||||||
let result: BaseBattleDiff[] = [];
|
let result: BaseBattleDiff[] = [];
|
||||||
|
|
||||||
// Action usage
|
// Action usage
|
||||||
|
@ -277,7 +292,7 @@ module TK.SpaceTac {
|
||||||
/**
|
/**
|
||||||
* Method to reimplement to return the diffs specific to this action
|
* Method to reimplement to return the diffs specific to this action
|
||||||
*/
|
*/
|
||||||
protected getSpecificDiffs(ship: Ship, battle: Battle, target: Target): BaseBattleDiff[] {
|
protected getSpecificDiffs(ship: Ship, battle: Battle, target: ActionTargetFrom): BaseBattleDiff[] {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,7 +301,7 @@ module TK.SpaceTac {
|
||||||
*
|
*
|
||||||
* This will first check that the action can be done, then get the battle diffs and apply them.
|
* This will first check that the action can be done, then get the battle diffs and apply them.
|
||||||
*/
|
*/
|
||||||
apply(battle: Battle, ship: Ship, target = this.getDefaultTarget(ship)): boolean {
|
apply(battle: Battle, ship: Ship, target: ActionTarget): boolean {
|
||||||
let reject = this.checkCannotBeApplied(ship);
|
let reject = this.checkCannotBeApplied(ship);
|
||||||
if (reject) {
|
if (reject) {
|
||||||
console.warn(`Action rejected - ${reject}`, ship, this, target);
|
console.warn(`Action rejected - ${reject}`, ship, this, target);
|
||||||
|
|
|
@ -12,7 +12,7 @@ module TK.SpaceTac {
|
||||||
// Effects applied
|
// Effects applied
|
||||||
effects: BaseEffect[]
|
effects: BaseEffect[]
|
||||||
// Filtering ships that will receive the effects
|
// Filtering ships that will receive the effects
|
||||||
filter: ActionTargettingFilter
|
filter: ActionImpactFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,7 +24,7 @@ module TK.SpaceTac {
|
||||||
power = 1
|
power = 1
|
||||||
radius = 0
|
radius = 0
|
||||||
effects: BaseEffect[] = []
|
effects: BaseEffect[] = []
|
||||||
filter = ActionTargettingFilter.ALL
|
filter = ActionImpactFilter.ALL
|
||||||
|
|
||||||
constructor(name: string, config?: Partial<ToggleActionConfig>, code?: string) {
|
constructor(name: string, config?: Partial<ToggleActionConfig>, code?: string) {
|
||||||
super(name, code);
|
super(name, code);
|
||||||
|
|
|
@ -38,7 +38,7 @@ module TK.SpaceTac.Specs {
|
||||||
});
|
});
|
||||||
check.equals(action.getEffectsDescription(), "Watch a 120km area (power usage 2):\n• hull -1 on the first 3 incoming ships");
|
check.equals(action.getEffectsDescription(), "Watch a 120km area (power usage 2):\n• hull -1 on the first 3 incoming ships");
|
||||||
|
|
||||||
action = new VigilanceAction("Reactive Fire", { power: 2, radius: 120, filter: ActionTargettingFilter.ALLIES }, {
|
action = new VigilanceAction("Reactive Fire", { power: 2, radius: 120, filter: ActionImpactFilter.ALLIES }, {
|
||||||
intruder_count: 3,
|
intruder_count: 3,
|
||||||
intruder_effects: [new ValueEffect("hull", -1)]
|
intruder_effects: [new ValueEffect("hull", -1)]
|
||||||
});
|
});
|
||||||
|
@ -61,7 +61,7 @@ module TK.SpaceTac.Specs {
|
||||||
TestTools.setShipModel(ship2b, 10, 0, 5);
|
TestTools.setShipModel(ship2b, 10, 0, 5);
|
||||||
let engine = ship2b.actions.addCustom(new MoveAction("Move", { max_distance: 1000 }));
|
let engine = ship2b.actions.addCustom(new MoveAction("Move", { max_distance: 1000 }));
|
||||||
|
|
||||||
let action = ship1a.actions.addCustom(new VigilanceAction("Reactive Shot", { radius: 1000, filter: ActionTargettingFilter.ENEMIES }, {
|
let action = ship1a.actions.addCustom(new VigilanceAction("Reactive Shot", { radius: 1000, filter: ActionImpactFilter.ENEMIES }, {
|
||||||
intruder_effects: [new DamageEffect(1)]
|
intruder_effects: [new DamageEffect(1)]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ module TK.SpaceTac {
|
||||||
}, "prokhorovlaser");
|
}, "prokhorovlaser");
|
||||||
laser.configureCooldown(3, 1);
|
laser.configureCooldown(3, 1);
|
||||||
|
|
||||||
let interceptors = new VigilanceAction("Interceptors Field", { radius: 400, power: 3, filter: ActionTargettingFilter.ENEMIES }, {
|
let interceptors = new VigilanceAction("Interceptors Field", { radius: 400, power: 3, filter: ActionImpactFilter.ENEMIES }, {
|
||||||
intruder_count: 1,
|
intruder_count: 1,
|
||||||
intruder_effects: [new DamageEffect(4, DamageEffectMode.SHIELD_THEN_HULL)]
|
intruder_effects: [new DamageEffect(4, DamageEffectMode.SHIELD_THEN_HULL)]
|
||||||
}, "interceptors");
|
}, "interceptors");
|
||||||
|
|
|
@ -30,7 +30,7 @@ module TK.SpaceTac {
|
||||||
}, "gravitshield");
|
}, "gravitshield");
|
||||||
repulse.configureCooldown(1, 1);
|
repulse.configureCooldown(1, 1);
|
||||||
|
|
||||||
let repairdrone = new DeployDroneAction("Repair Drone", { power: 3, filter: ActionTargettingFilter.ALLIES }, {
|
let repairdrone = new DeployDroneAction("Repair Drone", { power: 3, filter: ActionImpactFilter.ALLIES }, {
|
||||||
deploy_distance: 600,
|
deploy_distance: 600,
|
||||||
drone_radius: 300,
|
drone_radius: 300,
|
||||||
drone_effects: [
|
drone_effects: [
|
||||||
|
|
|
@ -21,7 +21,7 @@ module TK.SpaceTac {
|
||||||
power: 4,
|
power: 4,
|
||||||
radius: 400,
|
radius: 400,
|
||||||
effects: [new AttributeEffect("evasion", 1)],
|
effects: [new AttributeEffect("evasion", 1)],
|
||||||
filter: ActionTargettingFilter.ALLIES
|
filter: ActionImpactFilter.ALLIES
|
||||||
});
|
});
|
||||||
|
|
||||||
let depleter = new TriggerAction("Power Depleter", {
|
let depleter = new TriggerAction("Power Depleter", {
|
||||||
|
|
Loading…
Reference in a new issue