Removed randomness factors in battle actions
This commit is contained in:
parent
28e2f889bd
commit
3d6bc192d0
Binary file not shown.
Before Width: | Height: | Size: 429 B |
Binary file not shown.
Before Width: | Height: | Size: 358 B |
|
@ -10,8 +10,6 @@ module TK.SpaceTac.Specs {
|
|||
var action = new MoveAction("Engine", { distance_per_power: 10 });
|
||||
ship.actions.addCustom(action);
|
||||
|
||||
check.equals(action.getDistanceByPower(ship), 10);
|
||||
|
||||
var result = action.checkTarget(ship, Target.newFromLocation(0, 20));
|
||||
check.equals(result, Target.newFromLocation(0, 20));
|
||||
|
||||
|
@ -120,35 +118,12 @@ module TK.SpaceTac.Specs {
|
|||
check.equals(result, Target.newFromLocation(0, 1400));
|
||||
});
|
||||
|
||||
test.case("applies ship maneuvrability to determine distance per power point", check => {
|
||||
let ship = new Ship();
|
||||
|
||||
let action = new MoveAction("Engine", { distance_per_power: 100, maneuvrability_factor: 60 });
|
||||
TestTools.setAttribute(ship, "maneuvrability", 0);
|
||||
check.nears(action.getDistanceByPower(ship), 40);
|
||||
TestTools.setAttribute(ship, "maneuvrability", 1);
|
||||
check.nears(action.getDistanceByPower(ship), 60);
|
||||
TestTools.setAttribute(ship, "maneuvrability", 2);
|
||||
check.nears(action.getDistanceByPower(ship), 70);
|
||||
TestTools.setAttribute(ship, "maneuvrability", 10);
|
||||
check.nears(action.getDistanceByPower(ship), 90);
|
||||
|
||||
action = new MoveAction("Engine", { distance_per_power: 100, maneuvrability_factor: 0 });
|
||||
TestTools.setAttribute(ship, "maneuvrability", 0);
|
||||
check.nears(action.getDistanceByPower(ship), 100);
|
||||
TestTools.setAttribute(ship, "maneuvrability", 10);
|
||||
check.nears(action.getDistanceByPower(ship), 100);
|
||||
});
|
||||
|
||||
test.case("builds a textual description", check => {
|
||||
let action = new MoveAction("Engine", { distance_per_power: 58, safety_distance: 0 });
|
||||
check.equals(action.getEffectsDescription(), "Move: 58km per power point");
|
||||
|
||||
action = new MoveAction("Engine", { distance_per_power: 58, safety_distance: 12 });
|
||||
check.equals(action.getEffectsDescription(), "Move: 58km per power point (safety: 12km)");
|
||||
|
||||
action = new MoveAction("Engine", { distance_per_power: 58, safety_distance: 12, maneuvrability_factor: 80 });
|
||||
check.equals(action.getEffectsDescription(), "Move: 12-58km per power point (safety: 12km)");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ module TK.SpaceTac {
|
|||
distance_per_power: number
|
||||
// Safety distance from other ships
|
||||
safety_distance: number
|
||||
// Impact of maneuvrability (in % of distance)
|
||||
maneuvrability_factor: number
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,9 +66,11 @@ module TK.SpaceTac {
|
|||
}
|
||||
|
||||
getPowerUsage(ship: Ship, target: Target | null): number {
|
||||
if (target) {
|
||||
if (this.distance_per_power == 0) {
|
||||
return Infinity;
|
||||
} else if (target) {
|
||||
let distance = Target.newFromShip(ship).getDistanceTo(target);
|
||||
return Math.ceil(distance / this.getDistanceByPower(ship));
|
||||
return Math.ceil(distance / this.distance_per_power);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
@ -84,27 +84,7 @@ module TK.SpaceTac {
|
|||
* Get the distance reachable with a given power
|
||||
*/
|
||||
getRangeRadiusForPower(ship: Ship, power = ship.getValue("power")): number {
|
||||
return power * this.getDistanceByPower(ship);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the distance range that may be traveled with 1 power point
|
||||
*
|
||||
* The actual range will then depend on the ship maneuvrability
|
||||
*/
|
||||
getDistanceRangeByPower(): IntegerRange {
|
||||
let min_distance = Math.ceil(this.distance_per_power * (1 - this.maneuvrability_factor * 0.01));
|
||||
return new IntegerRange(min_distance, this.distance_per_power);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the distance that may be traveled with 1 power point
|
||||
*/
|
||||
getDistanceByPower(ship: Ship): number {
|
||||
let maneuvrability = Math.max(ship.getAttribute("maneuvrability"), 0);
|
||||
let factor = maneuvrability / (maneuvrability + 2);
|
||||
let range = this.getDistanceRangeByPower();
|
||||
return range.getProportional(factor);
|
||||
return power * this.distance_per_power;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -147,9 +127,7 @@ module TK.SpaceTac {
|
|||
}
|
||||
|
||||
getEffectsDescription(): string {
|
||||
let range = this.getDistanceRangeByPower();
|
||||
let rangeinfo = (range.max == range.min) ? `${range.min}` : `${range.min}-${range.max}`;
|
||||
let result = `Move: ${rangeinfo}km per power point`;
|
||||
let result = `Move: ${this.distance_per_power}km per power point`;
|
||||
|
||||
if (this.safety_distance) {
|
||||
result += ` (safety: ${this.safety_distance}km)`;
|
||||
|
|
|
@ -34,7 +34,7 @@ module TK.SpaceTac.Specs {
|
|||
|
||||
action.apply(battle, ship, Target.newFromLocation(50, 50));
|
||||
check.called(mock_apply, [
|
||||
[ship2, ship, 1]
|
||||
[ship2, ship]
|
||||
]);
|
||||
})
|
||||
|
||||
|
@ -82,82 +82,6 @@ module TK.SpaceTac.Specs {
|
|||
check.equals(action.filterImpactedShips({ x: 0, y: 51 }, Target.newFromLocation(30, 50), ships), [ship1, ship2]);
|
||||
})
|
||||
|
||||
test.case("computes a success factor, from precision and maneuvrability", check => {
|
||||
function verify(precision: number, precision_factor: number, maneuvrability: number, maneuvrability_factor: number, result: number) {
|
||||
let ship1 = new Ship();
|
||||
let ship2 = new Ship();
|
||||
|
||||
TestTools.setAttribute(ship1, "precision", precision);
|
||||
TestTools.setAttribute(ship2, "maneuvrability", maneuvrability);
|
||||
|
||||
let action = new TriggerAction("testaction", { aim: precision_factor, evasion: maneuvrability_factor });
|
||||
check.nears(action.getSuccessFactor(ship1, ship2), result, 3,
|
||||
`precision ${precision} (weight ${precision_factor}), maneuvrability ${maneuvrability} (weight ${maneuvrability_factor})`);
|
||||
}
|
||||
|
||||
// no weight => always 100%
|
||||
verify(0, 0, 0, 0, 1);
|
||||
verify(10, 0, 20, 0, 1);
|
||||
verify(40, 0, -5, 0, 1);
|
||||
|
||||
// precision only
|
||||
verify(0, 100, 0, 0, 0);
|
||||
verify(1, 100, 1, 0, 0.5);
|
||||
verify(2, 100, 2, 0, 0.8);
|
||||
verify(10, 100, 10, 0, 0.99);
|
||||
verify(1, 50, 1, 0, 0.75);
|
||||
|
||||
// maneuvrability only
|
||||
verify(0, 0, 0, 100, 1);
|
||||
verify(1, 0, 1, 100, 0.5);
|
||||
verify(2, 0, 2, 100, 0.2);
|
||||
verify(10, 0, 10, 100, 0.01);
|
||||
verify(1, 0, 1, 50, 0.75);
|
||||
|
||||
// precision vs maneuvrability
|
||||
verify(0, 100, 0, 100, 0);
|
||||
verify(4, 50, 4, 50, 0.5);
|
||||
verify(4, 50, 8, 50, 0.283);
|
||||
verify(4, 50, 20, 50, 0.016);
|
||||
verify(4, 50, 4, 50, 0.5);
|
||||
verify(8, 50, 4, 50, 0.717);
|
||||
verify(20, 50, 4, 50, 0.984);
|
||||
|
||||
// complex example
|
||||
verify(7, 20, 5, 40, 0.639);
|
||||
})
|
||||
|
||||
test.case("computes an effective success value, with random element", check => {
|
||||
function verify(success_base: number, luck: number, random: number, expected: number) {
|
||||
let ship1 = new Ship();
|
||||
let ship2 = new Ship();
|
||||
let action = new TriggerAction("testaction", { luck: luck });
|
||||
check.patch(action, "getSuccessFactor", () => success_base);
|
||||
check.nears(action.getEffectiveSuccess(ship1, ship2, new SkewedRandomGenerator([random])), expected, 5,
|
||||
`success ${success_base}, luck ${luck}, random ${random}`);
|
||||
}
|
||||
|
||||
// no luck influence
|
||||
verify(0.3, 0, 0.4, 0.3);
|
||||
verify(0.51, 0, 0.7, 0.51);
|
||||
|
||||
// small luck influence
|
||||
verify(0.5, 5, 0.0, 0);
|
||||
verify(0.5, 5, 0.1, 0.47979);
|
||||
verify(0.5, 5, 0.4, 0.49715);
|
||||
verify(0.5, 5, 0.8, 0.51161);
|
||||
verify(0.5, 5, 1.0, 1.0);
|
||||
verify(0.7, 5, 0.5, 0.69399);
|
||||
|
||||
// large luck influence
|
||||
verify(0.5, 45, 0.0, 0);
|
||||
verify(0.5, 45, 0.1, 0.31336);
|
||||
verify(0.5, 45, 0.4, 0.46864);
|
||||
verify(0.5, 45, 0.8, 0.61679);
|
||||
verify(0.5, 45, 1.0, 1);
|
||||
verify(0.7, 45, 0.5, 0.63485);
|
||||
})
|
||||
|
||||
test.case("guesses targetting mode", check => {
|
||||
let ship = new Ship();
|
||||
let action = new TriggerAction("testaction");
|
||||
|
@ -206,20 +130,11 @@ module TK.SpaceTac.Specs {
|
|||
action.configureTrigger({ effects: effects, power: 2, range: 120 });
|
||||
check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km):\n• precision +20% on target");
|
||||
|
||||
action.configureTrigger({ effects: effects, power: 2, range: 120, aim: 10 });
|
||||
check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km, aim +10%):\n• precision +20% on target");
|
||||
action.configureTrigger({ effects: effects, power: 2, range: 120, angle: 80 });
|
||||
check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km):\n• precision +20% in 80° arc");
|
||||
|
||||
action.configureTrigger({ effects: effects, power: 2, range: 120, aim: 10, evasion: 35 });
|
||||
check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km, aim +10%, evasion -35%):\n• precision +20% on target");
|
||||
|
||||
action.configureTrigger({ effects: effects, power: 2, range: 120, aim: 10, evasion: 35, angle: 80 });
|
||||
check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km, aim +10%, evasion -35%):\n• precision +20% in 80° arc");
|
||||
|
||||
action.configureTrigger({ effects: effects, power: 2, range: 120, aim: 10, evasion: 35, blast: 100, angle: 80 });
|
||||
check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km, aim +10%, evasion -35%):\n• precision +20% in 100km radius");
|
||||
|
||||
action.configureTrigger({ effects: effects, power: 2, range: 120, aim: 10, evasion: 35, blast: 100, angle: 80, luck: 15 });
|
||||
check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km, aim +10%, evasion -35%, luck ±15%):\n• precision +20% in 100km radius");
|
||||
action.configureTrigger({ effects: effects, power: 2, range: 120, blast: 100, angle: 80 });
|
||||
check.equals(action.getEffectsDescription(), "Fire (power 2, range 120km):\n• precision +20% in 100km radius");
|
||||
})
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,12 +15,6 @@ module TK.SpaceTac {
|
|||
blast: number
|
||||
// Angle of the area between the source and the target that will be impacted
|
||||
angle: number
|
||||
// Influence of "precision" of firing ship (0..100)
|
||||
aim: number
|
||||
// Influence of "maneuvrability" of impacted ship (0..100)
|
||||
evasion: number
|
||||
// Influence of luck (0..100)
|
||||
luck: number
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -34,9 +28,6 @@ module TK.SpaceTac {
|
|||
range = 0
|
||||
blast = 0
|
||||
angle = 0
|
||||
aim = 0
|
||||
evasion = 0
|
||||
luck = 0
|
||||
|
||||
constructor(name?: string, config?: Partial<TriggerActionConfig>, code?: string) {
|
||||
super(name, code);
|
||||
|
@ -99,42 +90,6 @@ module TK.SpaceTac {
|
|||
return this.range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the success factor [0-1] for this action applied from a ship to another
|
||||
*
|
||||
* This is a predictible formula, not including random elements.
|
||||
*/
|
||||
getSuccessFactor(from: Ship, to: Ship): number {
|
||||
let aim = this.aim * 0.01;
|
||||
let evasion = this.evasion * 0.01;
|
||||
let f1 = (x: number) => (x < 0 ? -1 : 1) * (1 - 1 / (x * x + 1));
|
||||
let prec = from.getAttribute("precision");
|
||||
let man = to.getAttribute("maneuvrability");
|
||||
let delta = Math.min(aim, evasion) * f1(0.2 * (prec - man));
|
||||
return clamp(1 - aim * (1 - f1(prec)) - evasion * f1(man) + delta, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the effective success of the action [0-1].
|
||||
*
|
||||
* Result has more chance to be near the success factor, but may be in the whole [0-1] range.
|
||||
*/
|
||||
getEffectiveSuccess(from: Ship, to: Ship, random = RandomGenerator.global): number {
|
||||
let p = this.getSuccessFactor(from, to);
|
||||
let s = this.luck * 0.01;
|
||||
if (s) {
|
||||
let c = (2 / (2 - s)) - 1;
|
||||
let x = random.random();
|
||||
if (x <= p) {
|
||||
return Math.pow(x, c) / Math.pow(p, c - 1);
|
||||
} else {
|
||||
return 1 - Math.pow(1 - x, c) / Math.pow(1 - p, c - 1);
|
||||
}
|
||||
} else {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
filterImpactedShips(source: ArenaLocation, target: Target, ships: Ship[]): Ship[] {
|
||||
if (this.blast) {
|
||||
return ships.filter(ship => arenaDistance(ship.location, target) <= this.blast);
|
||||
|
@ -181,15 +136,12 @@ module TK.SpaceTac {
|
|||
|
||||
/**
|
||||
* Collect the effects applied by this action
|
||||
*
|
||||
* If *luck* is specified, an effective success factor is used, instead of an estimated one
|
||||
*/
|
||||
getEffects(ship: Ship, target: Target, source = ship.location, luck?: RandomGenerator): [Ship, BaseEffect, number][] {
|
||||
let result: [Ship, BaseEffect, number][] = [];
|
||||
getEffects(ship: Ship, target: Target, source = ship.location): [Ship, BaseEffect][] {
|
||||
let result: [Ship, BaseEffect][] = [];
|
||||
let ships = this.getImpactedShips(ship, target, source);
|
||||
ships.forEach(iship => {
|
||||
let success = luck ? this.getEffectiveSuccess(ship, iship, luck) : this.getSuccessFactor(ship, iship);
|
||||
this.effects.forEach(effect => result.push([iship, effect, success]));
|
||||
this.effects.forEach(effect => result.push([iship, effect]));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
@ -213,9 +165,9 @@ module TK.SpaceTac {
|
|||
}
|
||||
|
||||
// Apply effects
|
||||
let effects = this.getEffects(ship, target, undefined, RandomGenerator.global);
|
||||
effects.forEach(([ship_target, effect, success]) => {
|
||||
let diffs = effect.getOnDiffs(ship_target, ship, success);
|
||||
let effects = this.getEffects(ship, target);
|
||||
effects.forEach(([ship_target, effect]) => {
|
||||
let diffs = effect.getOnDiffs(ship_target, ship);
|
||||
result = result.concat(diffs);
|
||||
});
|
||||
|
||||
|
@ -234,15 +186,6 @@ module TK.SpaceTac {
|
|||
if (this.range) {
|
||||
info.push(`range ${this.range}km`);
|
||||
}
|
||||
if (this.aim) {
|
||||
info.push(`aim +${this.aim}%`);
|
||||
}
|
||||
if (this.evasion) {
|
||||
info.push(`evasion -${this.evasion}%`);
|
||||
}
|
||||
if (this.luck) {
|
||||
info.push(`luck ±${this.luck}%`);
|
||||
}
|
||||
|
||||
let desc = (info.length) ? `${this.getVerb()} (${info.join(", ")})` : this.getVerb();
|
||||
let effects = this.effects.map(effect => {
|
||||
|
|
|
@ -150,8 +150,7 @@ module TK.SpaceTac.Specs {
|
|||
test.case("evaluates damage to enemies", check => {
|
||||
let battle = new Battle();
|
||||
let ship = battle.fleets[0].addShip();
|
||||
let weapon = TestTools.addWeapon(ship, 50, 5, 500, 100);
|
||||
let action = weapon;
|
||||
let action = TestTools.addWeapon(ship, 50, 5, 500, 100);
|
||||
|
||||
let enemy1 = battle.fleets[1].addShip();
|
||||
enemy1.setArenaPosition(250, 0);
|
||||
|
@ -162,6 +161,7 @@ module TK.SpaceTac.Specs {
|
|||
|
||||
// no enemies hurt
|
||||
let maneuver = new Maneuver(ship, action, Target.newFromLocation(100, 0));
|
||||
console.log(maneuver)
|
||||
check.nears(TacticalAIHelpers.evaluateEnemyHealth(ship, battle, maneuver), 0, 8);
|
||||
|
||||
// one enemy loses half-life
|
||||
|
|
|
@ -6,11 +6,11 @@ module TK.SpaceTac {
|
|||
check.equals(ship.getAttribute("maneuvrability"), 0, "initial");
|
||||
|
||||
let effect1 = new AttributeEffect("maneuvrability", 20);
|
||||
battle.applyDiffs(effect1.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect1.getOnDiffs(ship, ship));
|
||||
check.equals(ship.getAttribute("maneuvrability"), 20, "applied 1");
|
||||
|
||||
let effect2 = new AttributeEffect("maneuvrability", 10);
|
||||
battle.applyDiffs(effect2.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect2.getOnDiffs(ship, ship));
|
||||
check.equals(ship.getAttribute("maneuvrability"), 30, "applied 2");
|
||||
|
||||
battle.applyDiffs(effect1.getOffDiffs(ship));
|
||||
|
|
|
@ -20,7 +20,7 @@ module TK.SpaceTac {
|
|||
this.value = value;
|
||||
}
|
||||
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] {
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
return [
|
||||
new ShipAttributeDiff(ship, this.attrcode, { cumulative: this.value }, {}),
|
||||
];
|
||||
|
|
|
@ -7,11 +7,11 @@ module TK.SpaceTac {
|
|||
check.equals(ship.getAttribute("precision"), 12, "initial");
|
||||
|
||||
let effect1 = new AttributeLimitEffect("precision", 5);
|
||||
battle.applyDiffs(effect1.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect1.getOnDiffs(ship, ship));
|
||||
check.equals(ship.getAttribute("precision"), 5, "applied 1");
|
||||
|
||||
let effect2 = new AttributeLimitEffect("precision", 3);
|
||||
battle.applyDiffs(effect2.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect2.getOnDiffs(ship, ship));
|
||||
check.equals(ship.getAttribute("precision"), 3, "applied 2");
|
||||
|
||||
battle.applyDiffs(effect1.getOffDiffs(ship));
|
||||
|
|
|
@ -20,7 +20,7 @@ module TK.SpaceTac {
|
|||
this.value = value;
|
||||
}
|
||||
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] {
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
return [
|
||||
new ShipAttributeDiff(ship, this.attrcode, { limit: this.value }, {}),
|
||||
];
|
||||
|
|
|
@ -7,11 +7,11 @@ module TK.SpaceTac {
|
|||
check.equals(ship.getAttribute("hull_capacity"), 100, "initial");
|
||||
|
||||
let effect1 = new AttributeMultiplyEffect("hull_capacity", 30);
|
||||
battle.applyDiffs(effect1.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect1.getOnDiffs(ship, ship));
|
||||
check.equals(ship.getAttribute("hull_capacity"), 130, "applied 1");
|
||||
|
||||
let effect2 = new AttributeMultiplyEffect("hull_capacity", -10);
|
||||
battle.applyDiffs(effect2.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect2.getOnDiffs(ship, ship));
|
||||
check.equals(ship.getAttribute("hull_capacity"), 120, "applied 2");
|
||||
|
||||
battle.applyDiffs(effect1.getOffDiffs(ship));
|
||||
|
|
|
@ -21,7 +21,7 @@ module TK.SpaceTac {
|
|||
this.value = value;
|
||||
}
|
||||
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] {
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
return [
|
||||
new ShipAttributeDiff(ship, this.attrcode, { multiplier: this.value }, {}),
|
||||
];
|
||||
|
|
|
@ -19,7 +19,7 @@ module TK.SpaceTac {
|
|||
/**
|
||||
* Get the list of diffs needed to activate this effect on a ship
|
||||
*/
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone, success = 1): BaseBattleDiff[] {
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
|
@ -8,24 +8,24 @@ module TK.SpaceTac {
|
|||
check.equals(weapons.map(weapon => ship.actions.getCooldown(weapon).heat), [0, 0, 0]);
|
||||
|
||||
let effect = new CooldownEffect(0, 0);
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship));
|
||||
check.equals(weapons.map(weapon => ship.actions.getCooldown(weapon).heat), [0, 0, 0]);
|
||||
|
||||
weapons.forEach(weapon => ship.actions.storeUsage(weapon));
|
||||
check.equals(weapons.map(weapon => ship.actions.getCooldown(weapon).heat), [3, 3, 3]);
|
||||
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship));
|
||||
check.equals(weapons.map(weapon => ship.actions.getCooldown(weapon).heat), [0, 0, 0]);
|
||||
|
||||
weapons.forEach(weapon => ship.actions.storeUsage(weapon));
|
||||
check.equals(weapons.map(weapon => ship.actions.getCooldown(weapon).heat), [3, 3, 3]);
|
||||
|
||||
effect = new CooldownEffect(1, 0);
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship));
|
||||
check.equals(weapons.map(weapon => ship.actions.getCooldown(weapon).heat), [2, 2, 2]);
|
||||
|
||||
effect = new CooldownEffect(1, 2);
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship));
|
||||
check.equals(weapons.map(weapon => ship.actions.getCooldown(weapon).heat).sort(), [1, 1, 2]);
|
||||
})
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ module TK.SpaceTac {
|
|||
this.maxcount = maxcount;
|
||||
}
|
||||
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] {
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
let actions = ship.actions.listOverheated();
|
||||
|
||||
if (this.maxcount && actions.length > this.maxcount) {
|
||||
|
|
|
@ -14,22 +14,21 @@ module TK.SpaceTac.Specs {
|
|||
|
||||
checkValues("initial", 150, 400);
|
||||
|
||||
battle.applyDiffs(new DamageEffect(50).getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(new DamageEffect(50).getOnDiffs(ship, ship));
|
||||
checkValues("after 50 damage", 150, 350);
|
||||
|
||||
battle.applyDiffs(new DamageEffect(250).getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(new DamageEffect(250).getOnDiffs(ship, ship));
|
||||
checkValues("after 250 damage", 150, 100);
|
||||
|
||||
battle.applyDiffs(new DamageEffect(201).getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(new DamageEffect(201).getOnDiffs(ship, ship));
|
||||
checkValues("after 201 damage", 49, 0);
|
||||
|
||||
battle.applyDiffs(new DamageEffect(8000).getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(new DamageEffect(8000).getOnDiffs(ship, ship));
|
||||
checkValues("after 8000 damage", 0, 0);
|
||||
});
|
||||
|
||||
test.case("gets a textual description", check => {
|
||||
check.equals(new DamageEffect(10).getDescription(), "do 10 damage");
|
||||
check.equals(new DamageEffect(10, 5).getDescription(), "do 10-15 damage");
|
||||
});
|
||||
|
||||
test.case("applies damage modifiers", check => {
|
||||
|
@ -37,7 +36,7 @@ module TK.SpaceTac.Specs {
|
|||
TestTools.setShipModel(ship, 1000, 1000);
|
||||
let damage = new DamageEffect(200);
|
||||
|
||||
check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 200));
|
||||
check.equals(damage.getEffectiveDamage(ship), new ShipDamageDiff(ship, 0, 200));
|
||||
|
||||
check.patch(ship, "ieffects", iterator([
|
||||
isingle(new DamageModifierEffect(-15)),
|
||||
|
@ -48,14 +47,14 @@ module TK.SpaceTac.Specs {
|
|||
isingle(new DamageModifierEffect(3))
|
||||
]));
|
||||
|
||||
check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 170));
|
||||
check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 240));
|
||||
check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 0));
|
||||
check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 400));
|
||||
check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 190));
|
||||
check.equals(damage.getEffectiveDamage(ship), new ShipDamageDiff(ship, 0, 170));
|
||||
check.equals(damage.getEffectiveDamage(ship), new ShipDamageDiff(ship, 0, 240));
|
||||
check.equals(damage.getEffectiveDamage(ship), new ShipDamageDiff(ship, 0, 0));
|
||||
check.equals(damage.getEffectiveDamage(ship), new ShipDamageDiff(ship, 0, 400));
|
||||
check.equals(damage.getEffectiveDamage(ship), new ShipDamageDiff(ship, 0, 190));
|
||||
|
||||
damage = new DamageEffect(40);
|
||||
check.equals(damage.getEffectiveDamage(ship, 1), new ShipDamageDiff(ship, 0, 41));
|
||||
check.equals(damage.getEffectiveDamage(ship), new ShipDamageDiff(ship, 0, 41));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,16 +8,12 @@ module TK.SpaceTac {
|
|||
*/
|
||||
export class DamageEffect extends BaseEffect {
|
||||
// Base damage points
|
||||
base: number
|
||||
value: number
|
||||
|
||||
// Range of randomness (effective damage will be between *value* and *value+range*)
|
||||
span: number
|
||||
|
||||
constructor(value = 0, span = 0) {
|
||||
constructor(value = 0) {
|
||||
super("damage");
|
||||
|
||||
this.base = value;
|
||||
this.span = span;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,9 +32,9 @@ module TK.SpaceTac {
|
|||
/**
|
||||
* Get the effective damage done to both shield and hull (in this order)
|
||||
*/
|
||||
getEffectiveDamage(ship: Ship, success: number): ShipDamageDiff {
|
||||
getEffectiveDamage(ship: Ship): ShipDamageDiff {
|
||||
// Apply modifiers
|
||||
let theoritical = Math.round((this.base + this.span * success) * this.getFactor(ship));
|
||||
let theoritical = Math.round(this.value * this.getFactor(ship));
|
||||
let damage = theoritical;
|
||||
|
||||
// Apply on shields
|
||||
|
@ -51,10 +47,10 @@ module TK.SpaceTac {
|
|||
return new ShipDamageDiff(ship, hull, shield, theoritical);
|
||||
}
|
||||
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] {
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
let result: BaseBattleDiff[] = [];
|
||||
|
||||
let damage = this.getEffectiveDamage(ship, success);
|
||||
let damage = this.getEffectiveDamage(ship);
|
||||
|
||||
if (damage.shield || damage.hull) {
|
||||
result.push(damage);
|
||||
|
@ -72,11 +68,7 @@ module TK.SpaceTac {
|
|||
}
|
||||
|
||||
getDescription(): string {
|
||||
if (this.span > 0) {
|
||||
return `do ${this.base}-${this.base + this.span} damage`;
|
||||
} else {
|
||||
return `do ${this.base} damage`;
|
||||
}
|
||||
return `do ${this.value} damage`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,9 @@ module TK.SpaceTac.Specs {
|
|||
ship2a.setArenaPosition(100, 280);
|
||||
|
||||
let effect = new RepelEffect(12);
|
||||
battle.applyDiffs(effect.getOnDiffs(ship1a, ship1a, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship1b, ship1a, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship2a, ship1a, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship1a, ship1a));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship1b, ship1a));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship2a, ship1a));
|
||||
|
||||
check.equals(ship1a.location, new ArenaLocationAngle(100, 100));
|
||||
check.equals(ship1b.location, new ArenaLocationAngle(262, 100));
|
||||
|
@ -33,7 +33,7 @@ module TK.SpaceTac.Specs {
|
|||
ship2b.setArenaPosition(100, 350);
|
||||
|
||||
let effect = new RepelEffect(85);
|
||||
battle.applyDiffs(effect.getOnDiffs(ship2a, ship1a, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship2a, ship1a));
|
||||
check.equals(ship2a.location, new ArenaLocationAngle(100, 250));
|
||||
})
|
||||
})
|
||||
|
|
|
@ -13,7 +13,7 @@ module TK.SpaceTac {
|
|||
this.value = value;
|
||||
}
|
||||
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] {
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
if (ship != source) {
|
||||
let angle = arenaAngle(source.location, ship.location);
|
||||
let destination = new ArenaLocation(ship.arena_x + Math.cos(angle) * this.value, ship.arena_y + Math.sin(angle) * this.value);
|
||||
|
|
|
@ -10,7 +10,7 @@ module TK.SpaceTac.Specs {
|
|||
})
|
||||
|
||||
let effect = new StickyEffect(new AttributeEffect("precision", 1), 2);
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship));
|
||||
|
||||
check.in("after", check => {
|
||||
check.equals(ship.active_effects.count(), 1, "one sticky effect");
|
||||
|
@ -25,7 +25,7 @@ module TK.SpaceTac.Specs {
|
|||
}
|
||||
})
|
||||
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship));
|
||||
|
||||
check.in("after second apply", check => {
|
||||
check.equals(ship.active_effects.count(), 1, "one sticky effect");
|
||||
|
|
|
@ -21,7 +21,7 @@ module TK.SpaceTac {
|
|||
this.duration = duration;
|
||||
}
|
||||
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] {
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
let result: BaseBattleDiff[] = [];
|
||||
|
||||
let previous = ship.active_effects.get(this.id);
|
||||
|
|
|
@ -8,10 +8,10 @@ module TK.SpaceTac {
|
|||
ship.setValue("shield", 55);
|
||||
check.equals(ship.getValue("shield"), 55);
|
||||
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship));
|
||||
check.equals(ship.getValue("shield"), 75);
|
||||
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship, ship));
|
||||
check.equals(ship.getValue("shield"), 95);
|
||||
});
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ module TK.SpaceTac {
|
|||
this.value_end = value_end;
|
||||
}
|
||||
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] {
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
if (this.value_on) {
|
||||
return ship.getValueDiffs(this.valuetype, this.value_on, true);
|
||||
} else {
|
||||
|
|
|
@ -9,12 +9,12 @@ module TK.SpaceTac.Specs {
|
|||
TestTools.setShipModel(ship2, 100, 50);
|
||||
|
||||
let effect = new ValueTransferEffect("hull", -30);
|
||||
battle.applyDiffs(effect.getOnDiffs(ship2, ship1, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship2, ship1));
|
||||
check.equals(ship1.getValue("hull"), 40);
|
||||
check.equals(ship2.getValue("hull"), 70);
|
||||
|
||||
effect = new ValueTransferEffect("hull", 1000);
|
||||
battle.applyDiffs(effect.getOnDiffs(ship2, ship1, 1));
|
||||
battle.applyDiffs(effect.getOnDiffs(ship2, ship1));
|
||||
check.equals(ship1.getValue("hull"), 0);
|
||||
check.equals(ship2.getValue("hull"), 110); // over limit but will be fixed later
|
||||
})
|
||||
|
|
|
@ -18,10 +18,10 @@ module TK.SpaceTac {
|
|||
this.amount = amount;
|
||||
}
|
||||
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone, success: number): BaseBattleDiff[] {
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
if (source instanceof Ship) {
|
||||
if (this.amount < 0) {
|
||||
return new ValueTransferEffect(this.valuetype, -this.amount).getOnDiffs(source, ship, success);
|
||||
return new ValueTransferEffect(this.valuetype, -this.amount).getOnDiffs(source, ship);
|
||||
} else {
|
||||
let amount = Math.min(source.getValue(this.valuetype), this.amount);
|
||||
if (amount) {
|
||||
|
|
|
@ -20,18 +20,16 @@ module TK.SpaceTac {
|
|||
// TODO Weapons should be less efficient in short range
|
||||
|
||||
let charged_shot = new TriggerAction("Charged Shot", {
|
||||
effects: [new DamageEffect(30, 20)],
|
||||
effects: [new DamageEffect(40)],
|
||||
power: 3,
|
||||
range: 900,
|
||||
aim: 90, evasion: 40, luck: 20
|
||||
}, "gatlinggun");
|
||||
charged_shot.configureCooldown(2, 2);
|
||||
|
||||
let long_range_missile = new TriggerAction("Long Range Missile", {
|
||||
effects: [new DamageEffect(15, 25)],
|
||||
effects: [new DamageEffect(27)],
|
||||
power: 4,
|
||||
range: 700, blast: 120,
|
||||
aim: 70, evasion: 20, luck: 50
|
||||
}, "submunitionmissile");
|
||||
long_range_missile.configureCooldown(1, 2);
|
||||
|
||||
|
|
|
@ -14,16 +14,14 @@ module TK.SpaceTac {
|
|||
if (level == 1) {
|
||||
let engine = new MoveAction("Engine", {
|
||||
distance_per_power: 300,
|
||||
safety_distance: 100,
|
||||
maneuvrability_factor: 60
|
||||
safety_distance: 100
|
||||
});
|
||||
engine.configureCooldown(2, 1);
|
||||
|
||||
let gatling = new TriggerAction("Gatling Gun", {
|
||||
effects: [new DamageEffect(35, 20)],
|
||||
effects: [new DamageEffect(45)],
|
||||
power: 2,
|
||||
range: 200,
|
||||
aim: 30, evasion: 10, luck: 20
|
||||
}, "gatlinggun");
|
||||
gatling.configureCooldown(3, 1);
|
||||
|
||||
|
|
|
@ -17,10 +17,9 @@ module TK.SpaceTac {
|
|||
});
|
||||
|
||||
let laser = new TriggerAction("Wingspan Laser", {
|
||||
effects: [new DamageEffect(25, 25)],
|
||||
effects: [new DamageEffect(25)],
|
||||
power: 4,
|
||||
range: 250, angle: 140,
|
||||
aim: 30, evasion: 45, luck: 30,
|
||||
}, "prokhorovlaser");
|
||||
laser.configureCooldown(3, 1);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ module TK.SpaceTac {
|
|||
});
|
||||
|
||||
let gatling = new TriggerAction("Gatling Gun", {
|
||||
effects: [new DamageEffect(15, 10)],
|
||||
effects: [new DamageEffect(20)],
|
||||
power: 2,
|
||||
range: 200,
|
||||
}, "gatlinggun");
|
||||
|
|
|
@ -17,14 +17,14 @@ module TK.SpaceTac {
|
|||
});
|
||||
|
||||
let missile = new TriggerAction("SubMunition Missile", {
|
||||
effects: [new DamageEffect(10, 10)],
|
||||
effects: [new DamageEffect(15)],
|
||||
power: 2,
|
||||
range: 250, blast: 150,
|
||||
}, "submunitionmissile");
|
||||
missile.configureCooldown(2, 2);
|
||||
|
||||
let gatling = new TriggerAction("Gatling Gun", {
|
||||
effects: [new DamageEffect(10, 10)],
|
||||
effects: [new DamageEffect(15)],
|
||||
power: 1,
|
||||
range: 350,
|
||||
}, "gatlinggun");
|
||||
|
|
|
@ -24,7 +24,7 @@ module TK.SpaceTac {
|
|||
depleter.configureCooldown(1, 1);
|
||||
|
||||
let gatling = new TriggerAction("Gatling Gun", {
|
||||
effects: [new DamageEffect(5, 30)],
|
||||
effects: [new DamageEffect(20)],
|
||||
power: 3,
|
||||
range: 300,
|
||||
}, "gatlinggun");
|
||||
|
|
|
@ -18,10 +18,9 @@ module TK.SpaceTac {
|
|||
});
|
||||
|
||||
let missile = new TriggerAction("SubMunition Missile", {
|
||||
effects: [new DamageEffect(30, 30)],
|
||||
effects: [new DamageEffect(30)],
|
||||
power: 3,
|
||||
range: 400, blast: 120,
|
||||
aim: 70, evasion: 30, luck: 10,
|
||||
}, "submunitionmissile");
|
||||
|
||||
let protector = new TriggerAction("Damage Reductor", {
|
||||
|
|
|
@ -17,17 +17,16 @@ module TK.SpaceTac {
|
|||
});
|
||||
|
||||
let gatling = new TriggerAction("Gatling Gun", {
|
||||
effects: [new DamageEffect(30, 20)],
|
||||
effects: [new DamageEffect(40)],
|
||||
power: 3,
|
||||
range: 400,
|
||||
}, "gatlinggun");
|
||||
gatling.configureCooldown(2, 2);
|
||||
|
||||
let laser = new TriggerAction("Prokhorov Laser", {
|
||||
effects: [new DamageEffect(25, 25)],
|
||||
effects: [new DamageEffect(35)],
|
||||
power: 4,
|
||||
range: 250, angle: 60,
|
||||
aim: 30, evasion: 45, luck: 30,
|
||||
}, "prokhorovlaser");
|
||||
|
||||
return [
|
||||
|
|
|
@ -17,26 +17,26 @@ module TK.SpaceTac {
|
|||
});
|
||||
|
||||
let gatling1 = new TriggerAction("Primary Gatling", {
|
||||
effects: [new DamageEffect(15, 15)],
|
||||
effects: [new DamageEffect(22)],
|
||||
power: 2, range: 400
|
||||
}, "gatlinggun");
|
||||
gatling1.configureCooldown(1, 1);
|
||||
|
||||
let gatling2 = new TriggerAction("Secondary Gatling", {
|
||||
effects: [new DamageEffect(20, 15)],
|
||||
effects: [new DamageEffect(28)],
|
||||
power: 1, range: 200
|
||||
}, "gatlinggun");
|
||||
gatling2.configureCooldown(1, 1);
|
||||
|
||||
let missile = new TriggerAction("Diffuse Missiles", {
|
||||
effects: [new DamageEffect(10, 18)],
|
||||
effects: [new DamageEffect(19)],
|
||||
power: 2,
|
||||
range: 200, blast: 100,
|
||||
}, "submunitionmissile");
|
||||
missile.configureCooldown(1, 1);
|
||||
|
||||
let laser = new TriggerAction("Low-power Laser", {
|
||||
effects: [new DamageEffect(20, 20)],
|
||||
effects: [new DamageEffect(26)],
|
||||
power: 2,
|
||||
range: 200, angle: 30
|
||||
}, "prokhorovlaser");
|
||||
|
|
|
@ -31,7 +31,7 @@ module TK.SpaceTac {
|
|||
depleter.configureCooldown(1, 1);
|
||||
|
||||
let missile = new TriggerAction("Defense Missiles", {
|
||||
effects: [new DamageEffect(25, 30)],
|
||||
effects: [new DamageEffect(40)],
|
||||
power: 3,
|
||||
range: 200, blast: 180,
|
||||
}, "submunitionmissile");
|
||||
|
|
|
@ -17,10 +17,9 @@ module TK.SpaceTac {
|
|||
});
|
||||
|
||||
let laser = new TriggerAction("Prokhorov Laser", {
|
||||
effects: [new DamageEffect(20, 40)],
|
||||
effects: [new DamageEffect(35)],
|
||||
power: 3,
|
||||
range: 250, angle: 80,
|
||||
aim: 30, evasion: 45, luck: 30,
|
||||
});
|
||||
|
||||
let hull = new TriggerAction("Hull Shedding", {
|
||||
|
|
|
@ -19,7 +19,7 @@ module TK.SpaceTac.UI {
|
|||
if (ship.getValue("power") == 0) {
|
||||
cost = "Not enough power";
|
||||
} else {
|
||||
cost = `Cost: 1 power per ${action.getDistanceByPower(ship)}km`;
|
||||
cost = `Cost: 1 power per ${action.distance_per_power}km`;
|
||||
}
|
||||
} else {
|
||||
let power_usage = action.getPowerUsage(ship, null);
|
||||
|
|
|
@ -112,7 +112,7 @@ module TK.SpaceTac.UI {
|
|||
let move = part.action instanceof MoveAction;
|
||||
let color = (enabled && part.possible) ? (move ? 0xe09c47 : 0xdc6441) : 0x8e8e8e;
|
||||
let src = previous ? previous.target : this.ship.location;
|
||||
let gradation = (part.action instanceof MoveAction) ? part.action.getDistanceByPower(this.ship) : 0;
|
||||
let gradation = (part.action instanceof MoveAction) ? part.action.distance_per_power : 0;
|
||||
this.drawVector(color, src.x, src.y, part.target.x, part.target.y, gradation);
|
||||
}
|
||||
|
||||
|
@ -123,19 +123,10 @@ module TK.SpaceTac.UI {
|
|||
let ships = action.getImpactedShips(ship, target, source);
|
||||
if (ships.length) {
|
||||
// TODO differential
|
||||
impacts.removeAll(true);
|
||||
let builder = new UIBuilder(this.view).in(impacts);
|
||||
builder.clear();
|
||||
ships.forEach(iship => {
|
||||
let builder = new UIBuilder(this.view, impacts);
|
||||
|
||||
let indicator = builder.image("battle-hud-ship-impacted", iship.arena_x, iship.arena_y, true);
|
||||
|
||||
if (action instanceof TriggerAction) {
|
||||
let success = action.getSuccessFactor(ship, iship);
|
||||
builder.in(indicator, builder => {
|
||||
builder.image("battle-hud-ship-success-back", -29, -50);
|
||||
builder.valuebar("battle-hud-ship-success-fill", -28, -49).setValue(success, 1);
|
||||
});
|
||||
}
|
||||
builder.image("battle-hud-ship-impacted", iship.arena_x, iship.arena_y, true);
|
||||
});
|
||||
impacts.visible = true;
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue