Restored drones feature
This commit is contained in:
parent
1e681d6df3
commit
05a0607445
3
TODO.md
3
TODO.md
|
@ -37,7 +37,6 @@ Character sheet
|
|||
Battle
|
||||
------
|
||||
|
||||
* Fix drone effects not applying, and drone never disappearing (Repair Drone)
|
||||
* Fix arena's ship hovering happening even when the character sheet (or a dialog) is open on top
|
||||
* Add a voluntary retreat option
|
||||
* Add scroll buttons when there are too many actions
|
||||
|
@ -49,7 +48,6 @@ Battle
|
|||
* Find incentives to move from starting position (permanent drones or anomalies?)
|
||||
* Add a "loot all" button (on the character sheet or outcome dialog?)
|
||||
* Mark targetting in error when target is refused by the action (there is already an arrow for this)
|
||||
* Repair drone has its activation effect sometimes displayed as permanent effect on ships in the radius
|
||||
* Allow to undo last moves
|
||||
* Add a battle log display
|
||||
* Allow to move targetting indicator with arrow keys
|
||||
|
@ -76,7 +74,6 @@ Ships models and equipments
|
|||
* RepelEffect should apply on ships in a good order (distance decreasing)
|
||||
* Add hull points to drones and make them take area damage
|
||||
* Quality modifiers should be based on an "quality difference" to reach
|
||||
* Drones effects should be classified: permanent effects apply permanently, ponctual effects may be applied by an owner's action (if in range)
|
||||
|
||||
Artificial Intelligence
|
||||
-----------------------
|
||||
|
|
|
@ -133,7 +133,7 @@ module TK.SpaceTac {
|
|||
ship.active_effects.list().forEach(effect => {
|
||||
if (!(effect instanceof StickyEffect) && !expected.get(effect.id)) {
|
||||
result.push(new ShipEffectRemovedDiff(ship, effect));
|
||||
result = result.concat(effect.getOffDiffs(ship, ship));
|
||||
result = result.concat(effect.getOffDiffs(ship));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -141,7 +141,7 @@ module TK.SpaceTac {
|
|||
expected.list().forEach(effect => {
|
||||
if (!ship.active_effects.get(effect.id)) {
|
||||
result.push(new ShipEffectAddedDiff(ship, effect));
|
||||
result = result.concat(effect.getOnDiffs(ship, ship));
|
||||
result = result.concat(effect.getOnDiffs(ship, ship)); // TODO correct source
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,132 +1,64 @@
|
|||
/// <reference path="effects/BaseEffect.ts" />
|
||||
|
||||
module TK.SpaceTac {
|
||||
/**
|
||||
* Fake effect to capture apply requests
|
||||
*/
|
||||
class FakeEffect extends BaseEffect {
|
||||
applied: Ship[] = []
|
||||
|
||||
constructor() {
|
||||
super("fake");
|
||||
}
|
||||
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
this.applied.push(ship);
|
||||
return [];
|
||||
}
|
||||
|
||||
getApplyCalls() {
|
||||
let result = acopy(this.applied);
|
||||
this.applied = [];
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function newTestDrone(x: number, y: number, radius: number, owner: Ship): [Drone, FakeEffect] {
|
||||
let drone = new Drone(owner);
|
||||
drone.x = x;
|
||||
drone.y = y;
|
||||
drone.radius = radius;
|
||||
let effect = new FakeEffect();
|
||||
drone.effects.push(effect);
|
||||
let battle = owner.getBattle();
|
||||
if (battle) {
|
||||
battle.addDrone(drone);
|
||||
}
|
||||
return [drone, effect];
|
||||
}
|
||||
|
||||
testing("Drone", test => {
|
||||
test.case("applies effects on all ships inside the radius", check => {
|
||||
let battle = new Battle();
|
||||
let ship1 = new Ship(battle.fleets[0], "ship1");
|
||||
ship1.setArenaPosition(0, 0);
|
||||
let ship2 = new Ship(battle.fleets[0], "ship2");
|
||||
ship2.setArenaPosition(5, 5);
|
||||
let ship3 = new Ship(battle.fleets[0], "ship3");
|
||||
ship3.setArenaPosition(10, 10);
|
||||
let ship4 = new Ship(battle.fleets[0], "ship4");
|
||||
ship4.setArenaPosition(0, 0);
|
||||
ship4.setDead();
|
||||
let [drone, effect] = newTestDrone(2, 2, 8, ship1);
|
||||
test.case("applies area effects when deployed", check => {
|
||||
let battle = TestTools.createBattle();
|
||||
let ship = nn(battle.playing_ship);
|
||||
TestTools.setShipAP(ship, 10);
|
||||
let weapon = TestTools.addWeapon(ship);
|
||||
weapon.action = new DeployDroneAction(weapon, 2, 300, 30, [new AttributeEffect("precision", 15)]);
|
||||
let engine = TestTools.addEngine(ship, 1000);
|
||||
|
||||
check.equals(effect.getApplyCalls(), []);
|
||||
|
||||
drone.activate(battle);
|
||||
check.equals(effect.getApplyCalls(), [ship1, ship2]);
|
||||
});
|
||||
|
||||
test.case("signals the need for destruction after its lifetime", check => {
|
||||
let battle = new Battle();
|
||||
let owner = new Ship(battle.fleets[0]);
|
||||
let [drone, effect] = newTestDrone(0, 0, 5, owner);
|
||||
drone.duration = 3;
|
||||
|
||||
let removeDrone = check.patch(battle, "removeDrone", null);
|
||||
|
||||
drone.activate(battle);
|
||||
check.equals(drone.duration, 2);
|
||||
check.called(removeDrone, 0);
|
||||
|
||||
drone.activate(battle);
|
||||
check.equals(drone.duration, 1);
|
||||
check.called(removeDrone, 0);
|
||||
|
||||
drone.activate(battle);
|
||||
check.equals(drone.duration, 0);
|
||||
check.called(removeDrone, [[drone]]);
|
||||
});
|
||||
|
||||
test.case("builds diffs on activation", check => {
|
||||
let battle = new Battle();
|
||||
let ship = new Ship();
|
||||
ship.fleet.setBattle(battle);
|
||||
let other = new Ship();
|
||||
let drone = new Drone(ship);
|
||||
|
||||
drone.duration = 2;
|
||||
check.in("duration=2", check => {
|
||||
check.equals(drone.getDiffs(battle, [ship, other]), [
|
||||
new DroneAppliedDiff(drone, [ship, other]),
|
||||
], "two ships in range");
|
||||
check.equals(drone.getDiffs(battle, []), [
|
||||
], "no ship in range");
|
||||
});
|
||||
|
||||
drone.duration = 1;
|
||||
check.in("duration=1", check => {
|
||||
check.equals(drone.getDiffs(battle, [ship, other]), [
|
||||
new DroneAppliedDiff(drone, [ship, other]),
|
||||
new DroneDestroyedDiff(drone),
|
||||
], "two ships in range");
|
||||
check.equals(drone.getDiffs(battle, []), [
|
||||
new DroneDestroyedDiff(drone),
|
||||
], "no ship in range");
|
||||
});
|
||||
|
||||
drone.duration = 0;
|
||||
check.in("duration=0", check => {
|
||||
check.equals(drone.getDiffs(battle, [ship, other]), [
|
||||
new DroneDestroyedDiff(drone),
|
||||
], "two ships in range");
|
||||
check.equals(drone.getDiffs(battle, []), [
|
||||
new DroneDestroyedDiff(drone),
|
||||
], "no ship in range");
|
||||
});
|
||||
TestTools.actionChain(check, battle, [
|
||||
[ship, nn(weapon.action), Target.newFromLocation(150, 50)], // deploy out of effects radius
|
||||
[ship, nn(engine.action), Target.newFromLocation(110, 50)], // move out of effects radius
|
||||
[ship, nn(engine.action), Target.newFromLocation(130, 50)], // move in effects radius
|
||||
[ship, nn(weapon.action), Target.newFromShip(ship)], // recall
|
||||
[ship, nn(weapon.action), Target.newFromLocation(130, 70)], // deploy in effects radius
|
||||
], [
|
||||
check => {
|
||||
check.equals(ship.active_effects.count(), 0, "active effects");
|
||||
check.equals(ship.getValue("power"), 10, "power");
|
||||
check.equals(battle.drones.count(), 0, "drone count");
|
||||
},
|
||||
check => {
|
||||
check.equals(ship.active_effects.count(), 0, "active effects");
|
||||
check.equals(ship.getValue("power"), 8, "power");
|
||||
check.equals(battle.drones.count(), 1, "drone count");
|
||||
},
|
||||
check => {
|
||||
check.equals(ship.active_effects.count(), 0, "active effects");
|
||||
check.equals(ship.getValue("power"), 7, "power");
|
||||
check.equals(battle.drones.count(), 1, "drone count");
|
||||
},
|
||||
check => {
|
||||
check.equals(ship.active_effects.count(), 1, "active effects");
|
||||
check.equals(ship.getValue("power"), 6, "power");
|
||||
check.equals(battle.drones.count(), 1, "drone count");
|
||||
},
|
||||
check => {
|
||||
check.equals(ship.active_effects.count(), 0, "active effects");
|
||||
check.equals(ship.getValue("power"), 8, "power");
|
||||
check.equals(battle.drones.count(), 0, "drone count");
|
||||
},
|
||||
check => {
|
||||
check.equals(ship.active_effects.count(), 1, "active effects");
|
||||
check.equals(ship.getValue("power"), 6, "power");
|
||||
check.equals(battle.drones.count(), 1, "drone count");
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test.case("builds a textual description", check => {
|
||||
let drone = new Drone(new Ship());
|
||||
drone.duration = 1;
|
||||
check.equals(drone.getDescription(), "For 1 activation:\n• do nothing");
|
||||
check.equals(drone.getDescription(), "While deployed:\n• do nothing");
|
||||
|
||||
drone.duration = 3;
|
||||
drone.effects = [
|
||||
new DamageEffect(5),
|
||||
new AttributeEffect("skill_quantum", 1)
|
||||
]
|
||||
check.equals(drone.getDescription(), "For 3 activations:\n• do 5 damage\n• quantum skill +1");
|
||||
check.equals(drone.getDescription(), "While deployed:\n• do 5 damage\n• quantum skill +1");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,18 +14,17 @@ module TK.SpaceTac {
|
|||
y: number
|
||||
radius: number
|
||||
|
||||
// Remaining lifetime in number of turns
|
||||
duration: number
|
||||
|
||||
// Effects to apply
|
||||
effects: BaseEffect[] = []
|
||||
|
||||
constructor(owner: Ship, code = "drone", base_duration = 1) {
|
||||
// Action that triggered that drone
|
||||
parent: DeployDroneAction | null = null;
|
||||
|
||||
constructor(owner: Ship, code = "drone") {
|
||||
super();
|
||||
|
||||
this.owner = owner.id;
|
||||
this.code = code;
|
||||
this.duration = base_duration;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,7 +42,7 @@ module TK.SpaceTac {
|
|||
if (effects.length == 0) {
|
||||
effects = "• do nothing";
|
||||
}
|
||||
return `For ${this.duration} activation${this.duration > 1 ? "s" : ""}:\n${effects}`;
|
||||
return `While deployed:\n${effects}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,36 +59,5 @@ module TK.SpaceTac {
|
|||
let ships = ifilter(battle.iships(), ship => ship.alive && ship.isInCircle(this.x, this.y, this.radius));
|
||||
return imaterialize(ships);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of diffs needed to apply the drone effects on a list of ships.
|
||||
*
|
||||
* This does not check if the ships are in range.
|
||||
*/
|
||||
getDiffs(battle: Battle, ships: Ship[]): BaseBattleDiff[] {
|
||||
let result: BaseBattleDiff[] = [];
|
||||
|
||||
if (this.duration >= 1 && ships.length > 0) {
|
||||
result.push(new DroneAppliedDiff(this, ships));
|
||||
|
||||
ships.forEach(ship => {
|
||||
result = result.concat(flatten(this.effects.map(effect => effect.getOnDiffs(ship, this))));
|
||||
});
|
||||
}
|
||||
|
||||
if (this.duration <= 1) {
|
||||
result.push(new DroneDestroyedDiff(this));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply one drone "activation"
|
||||
*/
|
||||
activate(battle: Battle) {
|
||||
let diffs = this.getDiffs(battle, this.getAffectedShips(battle));
|
||||
battle.applyDiffs(diffs);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,7 +40,12 @@ module TK.SpaceTac {
|
|||
}
|
||||
|
||||
function effectFactor(effect: BaseEffect) {
|
||||
if (effect instanceof ValueEffect || effect instanceof AttributeEffect) {
|
||||
if (effect instanceof ValueEffect) {
|
||||
simpleFactor(effect, 'value_on');
|
||||
simpleFactor(effect, 'value_off');
|
||||
simpleFactor(effect, 'value_start');
|
||||
simpleFactor(effect, 'value_end');
|
||||
} else if (effect instanceof AttributeEffect || effect instanceof AttributeMultiplyEffect) {
|
||||
simpleFactor(effect, 'value');
|
||||
} else if (effect instanceof AttributeLimitEffect) {
|
||||
simpleFactor(effect, 'value', true);
|
||||
|
@ -79,8 +84,8 @@ module TK.SpaceTac {
|
|||
|
||||
if (equipment.action instanceof DeployDroneAction) {
|
||||
simpleFactor(equipment.action, 'deploy_distance');
|
||||
simpleFactor(equipment.action, 'effect_radius');
|
||||
equipment.action.effects.forEach(effectFactor);
|
||||
simpleFactor(equipment.action, 'drone_radius');
|
||||
equipment.action.drone_effects.forEach(effectFactor);
|
||||
}
|
||||
|
||||
if (equipment.action instanceof MoveAction) {
|
||||
|
|
|
@ -55,12 +55,13 @@ module TK.SpaceTac.Specs {
|
|||
compare_effects(check, action1.effects, action2.effects);
|
||||
}
|
||||
}
|
||||
|
||||
export function compare_drone_action(check: TestContext, action1: BaseAction | null, action2: DeployDroneAction | null): void {
|
||||
if (action1 === null || action2 === null || !(action1 instanceof DeployDroneAction)) {
|
||||
check.equals(action1, action2, "action");
|
||||
} else {
|
||||
check.equals(strip_id(strip(action1, "effects")), strip_id(strip(action2, "effects")), "action");
|
||||
compare_effects(check, action1.effects, action2.effects);
|
||||
check.equals(strip_id(strip(action1, "drone_effects")), strip_id(strip(action2, "drone_effects")), "action");
|
||||
compare_effects(check, action1.drone_effects, action2.drone_effects);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,15 +164,15 @@ module TK.SpaceTac.Specs {
|
|||
|
||||
test.case("adds drone actions", check => {
|
||||
let template = new LootTemplate(SlotType.Weapon, "Weapon");
|
||||
template.addDroneAction(istep(1), istep(100), istep(2), istep(50), [
|
||||
template.addDroneAction(istep(1), istep(100), istep(50), [
|
||||
new EffectTemplate(new FakeEffect(3), { "fakevalue": istep(8) })
|
||||
]);
|
||||
|
||||
let result = template.generate(1);
|
||||
compare_drone_action(check, result.action, new DeployDroneAction(result, 1, 100, 2, 50, [new FakeEffect(8)]));
|
||||
compare_drone_action(check, result.action, new DeployDroneAction(result, 1, 100, 50, [new FakeEffect(8)]));
|
||||
|
||||
result = template.generate(2);
|
||||
compare_drone_action(check, result.action, new DeployDroneAction(result, 2, 101, 3, 51, [new FakeEffect(9)]));
|
||||
compare_drone_action(check, result.action, new DeployDroneAction(result, 2, 101, 51, [new FakeEffect(9)]));
|
||||
});
|
||||
|
||||
test.case("checks the presence of damaging effects", check => {
|
||||
|
|
|
@ -3,6 +3,7 @@ module TK.SpaceTac {
|
|||
* A leveled value is an iterator yielding the desired value for each level (first item is for level 1, and so on)
|
||||
*/
|
||||
type LeveledValue = Iterator<number>;
|
||||
type LeveledModifiers<T extends BaseEffect> = {[P in keyof T]?: LeveledValue }
|
||||
|
||||
/**
|
||||
* Modifiers of generated equipment
|
||||
|
@ -35,12 +36,12 @@ module TK.SpaceTac {
|
|||
// Effect value modifiers
|
||||
modifiers: [keyof T, LeveledValue][];
|
||||
|
||||
constructor(effect: T, modifiers: { [attr: string]: LeveledValue }) {
|
||||
constructor(effect: T, modifiers: LeveledModifiers<T>) {
|
||||
this.effect = effect;
|
||||
this.modifiers = [];
|
||||
|
||||
iteritems(modifiers, (key, value) => {
|
||||
if (effect.hasOwnProperty(key)) {
|
||||
if (effect.hasOwnProperty(key) && value) {
|
||||
this.addModifier(<keyof T>key, value);
|
||||
}
|
||||
});
|
||||
|
@ -56,7 +57,7 @@ module TK.SpaceTac {
|
|||
let result = copy(this.effect);
|
||||
this.modifiers.forEach(modifier => {
|
||||
let [name, value] = modifier;
|
||||
(<any>result)[name] = resolveForLevel(value, level);
|
||||
result[name] = resolveForLevel(value, level);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
@ -215,10 +216,10 @@ module TK.SpaceTac {
|
|||
/**
|
||||
* Add a deploy drone action.
|
||||
*/
|
||||
addDroneAction(power: LeveledValue, range: LeveledValue, lifetime: LeveledValue, radius: LeveledValue, effects: EffectTemplate<any>[]): void {
|
||||
addDroneAction(power: LeveledValue, range: LeveledValue, radius: LeveledValue, effects: EffectTemplate<any>[]): void {
|
||||
this.base_modifiers.push((equipment, level) => {
|
||||
let reffects = effects.map(effect => effect.generate(level));
|
||||
equipment.action = new DeployDroneAction(equipment, resolveForLevel(power, level), resolveForLevel(range, level), resolveForLevel(lifetime, level), resolveForLevel(radius, level), reffects);
|
||||
equipment.action = new DeployDroneAction(equipment, resolveForLevel(power, level), resolveForLevel(range, level), resolveForLevel(radius, level), reffects);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ module TK.SpaceTac {
|
|||
*/
|
||||
static addWeapon(ship: Ship, damage = 100, power_usage = 1, max_distance = 100, blast = 0, angle = 0): Equipment {
|
||||
var equipment = ship.addSlot(SlotType.Weapon).attach(new Equipment(SlotType.Weapon));
|
||||
equipment.action = new TriggerAction(equipment, [new DamageEffect(damage)], power_usage, max_distance, blast, angle, "Test Weapon");
|
||||
equipment.action = new TriggerAction(equipment, [new DamageEffect(damage)], power_usage, max_distance, blast, angle);
|
||||
return equipment;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ module TK.SpaceTac.Specs {
|
|||
testing("BaseAction", test => {
|
||||
test.case("check if equipment can be used with remaining AP", check => {
|
||||
var equipment = new Equipment(SlotType.Hull);
|
||||
var action = new BaseAction("test", "Test", equipment);
|
||||
var action = new BaseAction("test", equipment);
|
||||
check.patch(action, "getActionPointsUsage", () => 3);
|
||||
var ship = new Ship();
|
||||
ship.addSlot(SlotType.Hull).attach(equipment);
|
||||
|
@ -27,7 +27,7 @@ module TK.SpaceTac.Specs {
|
|||
|
||||
test.case("check if equipment can be used with overheat", check => {
|
||||
let equipment = new Equipment();
|
||||
let action = new BaseAction("test", "Test", equipment);
|
||||
let action = new BaseAction("test", equipment);
|
||||
let ship = new Ship();
|
||||
|
||||
check.equals(action.checkCannotBeApplied(ship), null);
|
||||
|
@ -71,7 +71,7 @@ module TK.SpaceTac.Specs {
|
|||
TestTools.setShipAP(ship, 10);
|
||||
let power = ship.listEquipment(SlotType.Power)[0];
|
||||
let equipment = new Equipment(SlotType.Weapon);
|
||||
let action = new BaseAction("test", "Test", equipment);
|
||||
let action = new BaseAction("test", equipment);
|
||||
equipment.action = action;
|
||||
ship.addSlot(SlotType.Weapon).attach(equipment);
|
||||
|
||||
|
|
|
@ -26,21 +26,24 @@ module TK.SpaceTac {
|
|||
// Identifier code for the type of action
|
||||
code: string
|
||||
|
||||
// Human-readable name
|
||||
name: string
|
||||
|
||||
// Equipment that triggers this action
|
||||
equipment: Equipment | null
|
||||
|
||||
// Create the action
|
||||
constructor(code = "nothing", name = "Idle", equipment: Equipment | null = null) {
|
||||
constructor(code = "nothing", equipment: Equipment | null = null) {
|
||||
super();
|
||||
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.equipment = equipment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the verb for this action
|
||||
*/
|
||||
getVerb(): string {
|
||||
return "Idle";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relevent cooldown for this action
|
||||
*/
|
||||
|
|
|
@ -5,7 +5,7 @@ module TK.SpaceTac.Specs {
|
|||
let action = new DeployDroneAction(equipment);
|
||||
|
||||
check.equals(action.code, "deploy-testdrone");
|
||||
check.equals(action.name, "Deploy");
|
||||
check.equals(action.getVerb(), "Deploy");
|
||||
check.same(action.equipment, equipment);
|
||||
});
|
||||
|
||||
|
@ -29,7 +29,7 @@ module TK.SpaceTac.Specs {
|
|||
TestTools.setShipAP(ship, 3);
|
||||
|
||||
let equipment = new Equipment(SlotType.Weapon, "testdrone");
|
||||
let action = new DeployDroneAction(equipment, 2, 8, 2, 4, [new DamageEffect(50)]);
|
||||
let action = new DeployDroneAction(equipment, 2, 8, 4, [new DamageEffect(50)]);
|
||||
equipment.action = action;
|
||||
ship.addSlot(SlotType.Weapon).attach(equipment);
|
||||
|
||||
|
@ -46,7 +46,6 @@ module TK.SpaceTac.Specs {
|
|||
|
||||
let drone = battle.drones.list()[0];
|
||||
check.equals(drone.code, "testdrone");
|
||||
check.equals(drone.duration, 2);
|
||||
check.same(drone.owner, ship.id);
|
||||
check.equals(drone.x, 5);
|
||||
check.equals(drone.y, 0);
|
||||
|
|
|
@ -1,74 +1,78 @@
|
|||
/// <reference path="BaseAction.ts"/>
|
||||
/// <reference path="ToggleAction.ts"/>
|
||||
|
||||
module TK.SpaceTac {
|
||||
/**
|
||||
* Action to deploy a drone in space
|
||||
*
|
||||
* This is a toggled action, meaning that deploying a drone requires a permanent power supply from the ship
|
||||
*/
|
||||
export class DeployDroneAction extends BaseAction {
|
||||
// Power usage
|
||||
power: number;
|
||||
|
||||
export class DeployDroneAction extends ToggleAction {
|
||||
// Maximal distance the drone may be deployed
|
||||
deploy_distance: number;
|
||||
deploy_distance: number
|
||||
|
||||
// Effect radius of the deployed drone
|
||||
effect_radius: number;
|
||||
|
||||
// Duration of the drone in turns, before being destroyed
|
||||
lifetime: number;
|
||||
drone_radius: number
|
||||
|
||||
// Effects applied to ships in range of the drone
|
||||
effects: BaseEffect[];
|
||||
drone_effects: BaseEffect[]
|
||||
|
||||
// Source equipment
|
||||
equipment: Equipment;
|
||||
constructor(equipment: Equipment, power = 1, deploy_distance = 0, radius = 0, effects: BaseEffect[] = []) {
|
||||
super(equipment, power, 0, [], `deploy-${equipment.code}`);
|
||||
|
||||
constructor(equipment: Equipment, power = 1, deploy_distance = 0, lifetime = 0, effect_radius = 0, effects: BaseEffect[] = []) {
|
||||
super("deploy-" + equipment.code, "Deploy", equipment);
|
||||
|
||||
this.power = power;
|
||||
this.deploy_distance = deploy_distance;
|
||||
this.lifetime = lifetime;
|
||||
this.effect_radius = effect_radius;
|
||||
this.effects = effects;
|
||||
this.drone_radius = radius;
|
||||
this.drone_effects = effects;
|
||||
}
|
||||
|
||||
getVerb(): string {
|
||||
return this.activated ? "Recall" : "Deploy";
|
||||
}
|
||||
|
||||
getTargettingMode(ship: Ship): ActionTargettingMode {
|
||||
return ActionTargettingMode.SPACE;
|
||||
}
|
||||
|
||||
getActionPointsUsage(ship: Ship, target: Target | null): number {
|
||||
return this.power;
|
||||
return this.activated ? ActionTargettingMode.SELF : ActionTargettingMode.SPACE;
|
||||
}
|
||||
|
||||
getRangeRadius(ship: Ship): number {
|
||||
return this.deploy_distance;
|
||||
return this.activated ? 0 : this.deploy_distance;
|
||||
}
|
||||
|
||||
filterImpactedShips(source: ArenaLocation, target: Target, ships: Ship[]): Ship[] {
|
||||
return ships.filter(ship => arenaDistance(ship.location, target) <= this.effect_radius);
|
||||
return ships.filter(ship => arenaDistance(ship.location, target) <= this.drone_radius);
|
||||
}
|
||||
|
||||
checkLocationTarget(ship: Ship, target: Target): Target {
|
||||
// TODO Not too close to other ships and drones
|
||||
target = target.constraintInRange(ship.arena_x, ship.arena_y, this.deploy_distance);
|
||||
return target;
|
||||
}
|
||||
|
||||
protected getSpecificDiffs(ship: Ship, battle: Battle, target: Target): BaseBattleDiff[] {
|
||||
let drone = new Drone(ship, this.equipment.code, this.lifetime);
|
||||
drone.x = target.x;
|
||||
drone.y = target.y;
|
||||
drone.radius = this.effect_radius;
|
||||
drone.effects = this.effects;
|
||||
let result = super.getSpecificDiffs(ship, battle, target);
|
||||
|
||||
return [new DroneDeployedDiff(drone)];
|
||||
if (this.activated) {
|
||||
let drone = first(battle.drones.list(), drone => drone.parent == this);
|
||||
if (drone) {
|
||||
result.push(new DroneRecalledDiff(drone));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
let drone = new Drone(ship, this.equipment.code);
|
||||
drone.parent = this;
|
||||
drone.x = target.x;
|
||||
drone.y = target.y;
|
||||
drone.radius = this.drone_radius;
|
||||
drone.effects = this.drone_effects;
|
||||
|
||||
result.push(new DroneDeployedDiff(drone));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getEffectsDescription(): string {
|
||||
let desc = `Deploy drone for ${this.lifetime} activation${this.lifetime > 1 ? "s" : ""} (power usage ${this.power}, max range ${this.deploy_distance}km)`;
|
||||
let effects = this.effects.map(effect => {
|
||||
let suffix = `for ships in ${this.effect_radius}km radius`;
|
||||
let desc = `Deploy drone (power usage ${this.power}, max range ${this.deploy_distance}km)`;
|
||||
let effects = this.drone_effects.map(effect => {
|
||||
let suffix = `for ships in ${this.drone_radius}km radius`;
|
||||
return "• " + effect.getDescription() + " " + suffix;
|
||||
});
|
||||
return `${desc}:\n${effects.join("\n")}`;
|
||||
|
|
|
@ -11,7 +11,11 @@ module TK.SpaceTac {
|
|||
static SINGLETON = new EndTurnAction();
|
||||
|
||||
constructor() {
|
||||
super("endturn", "End ship's turn");
|
||||
super("endturn");
|
||||
}
|
||||
|
||||
getVerb(): string {
|
||||
return "End ship's turn";
|
||||
}
|
||||
|
||||
protected getSpecificDiffs(ship: Ship, battle: Battle, target: Target): BaseBattleDiff[] {
|
||||
|
@ -29,21 +33,20 @@ module TK.SpaceTac {
|
|||
result.push(new ShipCooldownDiff(ship, equ, 1));
|
||||
});
|
||||
|
||||
// Fade sticky effects
|
||||
// "On turn end" effects
|
||||
iforeach(ship.active_effects.iterator(), effect => {
|
||||
if (effect instanceof StickyEffect) {
|
||||
if (effect.duration > 1) {
|
||||
result.push(new ShipEffectChangedDiff(ship, effect, -1));
|
||||
} else {
|
||||
result = result.concat(effect.getOffDiffs(ship, ship));
|
||||
}
|
||||
}
|
||||
result = result.concat(effect.getTurnEndDiffs(ship));
|
||||
});
|
||||
|
||||
// Change the active ship
|
||||
let cycle_diff = (battle.play_order.indexOf(new_ship) == 0) ? 1 : 0;
|
||||
result.push(new ShipChangeDiff(ship, new_ship, cycle_diff));
|
||||
|
||||
// "On turn start" effects
|
||||
iforeach(new_ship.active_effects.iterator(), effect => {
|
||||
result = result.concat(effect.getTurnStartDiffs(ship));
|
||||
});
|
||||
|
||||
return result;
|
||||
} else {
|
||||
return [];
|
||||
|
|
|
@ -16,13 +16,17 @@ module TK.SpaceTac {
|
|||
maneuvrability_factor: number
|
||||
|
||||
constructor(equipment: Equipment, distance_per_power = 0, safety_distance = 120, maneuvrability_factor = 0) {
|
||||
super("move", "Move", equipment);
|
||||
super("move", equipment);
|
||||
|
||||
this.distance_per_power = distance_per_power;
|
||||
this.safety_distance = safety_distance;
|
||||
this.maneuvrability_factor = maneuvrability_factor;
|
||||
}
|
||||
|
||||
getVerb(): string {
|
||||
return "Move";
|
||||
}
|
||||
|
||||
getTargettingMode(ship: Ship): ActionTargettingMode {
|
||||
return ActionTargettingMode.SPACE;
|
||||
}
|
||||
|
|
|
@ -22,14 +22,18 @@ module TK.SpaceTac {
|
|||
// Current activation status
|
||||
activated = false
|
||||
|
||||
constructor(equipment: Equipment, power = 1, radius = 0, effects: BaseEffect[] = [], name = "(De)activate") {
|
||||
super("toggle-" + equipment.code, name, equipment);
|
||||
constructor(equipment: Equipment, power = 1, radius = 0, effects: BaseEffect[] = [], code = `toggle-${equipment.code}`) {
|
||||
super(code, equipment);
|
||||
|
||||
this.power = power;
|
||||
this.radius = radius;
|
||||
this.effects = effects;
|
||||
}
|
||||
|
||||
getVerb(): string {
|
||||
return this.activated ? "Deactivate" : "Activate";
|
||||
}
|
||||
|
||||
getTargettingMode(ship: Ship): ActionTargettingMode {
|
||||
if (this.activated || !this.radius) {
|
||||
return ActionTargettingMode.SELF_CONFIRM;
|
||||
|
@ -64,7 +68,7 @@ module TK.SpaceTac {
|
|||
this.effects.forEach(effect => {
|
||||
if (this.activated) {
|
||||
result.push(new ShipEffectRemovedDiff(iship, effect));
|
||||
result = result.concat(effect.getOffDiffs(iship, ship));
|
||||
result = result.concat(effect.getOffDiffs(iship));
|
||||
} else {
|
||||
result.push(new ShipEffectAddedDiff(iship, effect));
|
||||
result = result.concat(effect.getOnDiffs(iship, ship));
|
||||
|
|
|
@ -5,7 +5,7 @@ module TK.SpaceTac.Specs {
|
|||
let action = new TriggerAction(equipment, [], 4, 30, 10);
|
||||
|
||||
check.equals(action.code, "fire-testweapon");
|
||||
check.equals(action.name, "Fire");
|
||||
check.equals(action.getVerb(), "Fire");
|
||||
check.same(action.equipment, equipment);
|
||||
})
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ module TK.SpaceTac {
|
|||
// Equipment cannot be null
|
||||
equipment: Equipment
|
||||
|
||||
constructor(equipment: Equipment, effects: BaseEffect[] = [], power = 1, range = 0, blast = 0, angle = 0, name = range ? "Fire" : "Trigger") {
|
||||
super("fire-" + equipment.code, name, equipment);
|
||||
constructor(equipment: Equipment, effects: BaseEffect[] = [], power = 1, range = 0, blast = 0, angle = 0, code = `fire-${equipment.code}`) {
|
||||
super(code, equipment);
|
||||
|
||||
this.power = power;
|
||||
this.range = range;
|
||||
|
@ -35,6 +35,10 @@ module TK.SpaceTac {
|
|||
this.angle = angle;
|
||||
}
|
||||
|
||||
getVerb(): string {
|
||||
return this.range ? "Fire" : "Trigger";
|
||||
}
|
||||
|
||||
getDefaultTarget(ship: Ship): Target {
|
||||
if (this.range == 0) {
|
||||
return Target.newFromShip(ship);
|
||||
|
@ -175,7 +179,7 @@ module TK.SpaceTac {
|
|||
info.push(`max range ${this.range}km`);
|
||||
}
|
||||
|
||||
let desc = `${this.name} (${info.join(", ")})`;
|
||||
let desc = `${this.getVerb()} (${info.join(", ")})`;
|
||||
let effects = this.effects.map(effect => {
|
||||
let suffix: string;
|
||||
if (this.blast) {
|
||||
|
|
|
@ -32,7 +32,7 @@ module TK.SpaceTac.Specs {
|
|||
let ship2 = battle.fleets[0].addShip();
|
||||
let ship3 = battle.fleets[1].addShip();
|
||||
let weapon = ship1.addSlot(SlotType.Weapon).attach(new Equipment(SlotType.Weapon));
|
||||
weapon.action = new DeployDroneAction(weapon, 0, 100, 1, 10, [new ValueEffect("shield", 10)]);
|
||||
weapon.action = new DeployDroneAction(weapon, 0, 100, 10, [new ValueEffect("shield", 10)]);
|
||||
ship1.setArenaPosition(0, 0);
|
||||
TestTools.setShipHP(ship1, 20, 20);
|
||||
ship2.setArenaPosition(0, 5);
|
||||
|
|
|
@ -81,8 +81,8 @@ module TK.SpaceTac {
|
|||
if (this.action instanceof TriggerAction) {
|
||||
result = result.concat(this.action.getEffects(this.ship, this.target));
|
||||
} else if (this.action instanceof DeployDroneAction) {
|
||||
let ships = this.battle.collectShipsInCircle(this.target, this.action.effect_radius, true);
|
||||
this.action.effects.forEach(effect => {
|
||||
let ships = this.battle.collectShipsInCircle(this.target, this.action.drone_radius, true);
|
||||
this.action.drone_effects.forEach(effect => {
|
||||
result = result.concat(ships.map(ship => <[Ship, BaseEffect]>[ship, effect]));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ module TK.SpaceTac.Specs {
|
|||
constructor(score: number) {
|
||||
let battle = new Battle();
|
||||
let ship = battle.fleets[0].addShip();
|
||||
super(ship, new BaseAction("nothing", "Do nothing"), new Target(0, 0));
|
||||
super(ship, new BaseAction("nothing"), new Target(0, 0));
|
||||
this.score = score;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ module TK.SpaceTac.Specs {
|
|||
let action = nn(weapon.action);
|
||||
let engine = TestTools.addEngine(ship, 25);
|
||||
|
||||
let maneuver = new Maneuver(ship, new BaseAction("fake", "Nothing"), new Target(0, 0), 0);
|
||||
let maneuver = new Maneuver(ship, new BaseAction("fake"), new Target(0, 0), 0);
|
||||
check.same(TacticalAIHelpers.evaluateTurnCost(ship, battle, maneuver), -1);
|
||||
|
||||
maneuver = new Maneuver(ship, action, Target.newFromLocation(100, 0), 0);
|
||||
|
|
|
@ -37,9 +37,9 @@ module TK.SpaceTac {
|
|||
dshield -= ds;
|
||||
} else if (effect instanceof ValueEffect) {
|
||||
if (effect.valuetype == "hull") {
|
||||
dhull = clamp(hull + effect.value, 0, chull) - hull;
|
||||
dhull = clamp(hull + effect.value_on, 0, chull) - hull;
|
||||
} else if (effect.valuetype == "shield") {
|
||||
dshield += clamp(shield + effect.value, 0, cshield) - shield;
|
||||
dshield += clamp(shield + effect.value_on, 0, cshield) - shield;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
/// <reference path="BaseBattleDiff.ts"/>
|
||||
|
||||
module TK.SpaceTac {
|
||||
/**
|
||||
* A drone applies its effect on ships around
|
||||
*/
|
||||
export class DroneAppliedDiff extends BaseBattleDiff {
|
||||
// ID of the drone
|
||||
drone: RObjectId
|
||||
|
||||
// IDs of impacted ships
|
||||
ships: RObjectId[]
|
||||
|
||||
constructor(drone: Drone, ships: Ship[]) {
|
||||
super();
|
||||
|
||||
this.drone = drone.id;
|
||||
this.ships = ships.map(ship => ship.id);
|
||||
}
|
||||
|
||||
apply(battle: Battle): void {
|
||||
let drone = battle.drones.get(this.drone);
|
||||
if (drone) {
|
||||
drone.duration -= 1;
|
||||
} else {
|
||||
console.error("Cannot apply diff - Drone not found", this);
|
||||
}
|
||||
}
|
||||
|
||||
revert(battle: Battle): void {
|
||||
let drone = battle.drones.get(this.drone);
|
||||
if (drone) {
|
||||
drone.duration += 1;
|
||||
} else {
|
||||
console.error("Cannot revert diff - Drone not found", this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,41 +3,25 @@ module TK.SpaceTac.Specs {
|
|||
test.case("applies and reverts", check => {
|
||||
let battle = TestTools.createBattle();
|
||||
let drone1 = new Drone(battle.play_order[0]);
|
||||
let drone2 = new Drone(battle.play_order[0], "test", 2);
|
||||
let drone2 = new Drone(battle.play_order[0], "test");
|
||||
|
||||
TestTools.diffChain(check, battle, [
|
||||
new DroneDeployedDiff(drone1, 3),
|
||||
new DroneDeployedDiff(drone1),
|
||||
new DroneDeployedDiff(drone2),
|
||||
new DroneAppliedDiff(drone1, []),
|
||||
new DroneAppliedDiff(drone1, []),
|
||||
new DroneDestroyedDiff(drone1, 1),
|
||||
new DroneDestroyedDiff(drone2),
|
||||
new DroneRecalledDiff(drone1),
|
||||
new DroneRecalledDiff(drone2),
|
||||
], [
|
||||
check => {
|
||||
check.equals(battle.drones.count(), 0, "drone count");
|
||||
},
|
||||
check => {
|
||||
check.equals(battle.drones.count(), 1, "drone count");
|
||||
check.equals(nn(battle.drones.get(drone1.id)).duration, 3, "drone1 duration");
|
||||
},
|
||||
check => {
|
||||
check.equals(battle.drones.count(), 2, "drone count");
|
||||
check.equals(nn(battle.drones.get(drone1.id)).duration, 3, "drone1 duration");
|
||||
check.equals(nn(battle.drones.get(drone2.id)).duration, 2, "drone2 duration");
|
||||
},
|
||||
check => {
|
||||
check.equals(battle.drones.count(), 2, "drone count");
|
||||
check.equals(nn(battle.drones.get(drone1.id)).duration, 2, "drone1 duration");
|
||||
check.equals(nn(battle.drones.get(drone2.id)).duration, 2, "drone2 duration");
|
||||
},
|
||||
check => {
|
||||
check.equals(battle.drones.count(), 2, "drone count");
|
||||
check.equals(nn(battle.drones.get(drone1.id)).duration, 1, "drone1 duration");
|
||||
check.equals(nn(battle.drones.get(drone2.id)).duration, 2, "drone2 duration");
|
||||
},
|
||||
check => {
|
||||
check.equals(battle.drones.count(), 1, "drone count");
|
||||
check.equals(nn(battle.drones.get(drone2.id)).duration, 2, "drone2 duration");
|
||||
},
|
||||
check => {
|
||||
check.equals(battle.drones.count(), 0, "drone count");
|
||||
|
|
|
@ -8,41 +8,32 @@ module TK.SpaceTac {
|
|||
// Drone object
|
||||
drone: Drone
|
||||
|
||||
// Initial duration (number of activations)
|
||||
duration: number
|
||||
|
||||
constructor(drone: Drone, duration = drone.duration) {
|
||||
constructor(drone: Drone) {
|
||||
super(drone.owner);
|
||||
|
||||
this.drone = drone;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
protected applyOnShip(ship: Ship, battle: Battle): void {
|
||||
this.drone.duration = this.duration;
|
||||
battle.addDrone(this.drone);
|
||||
}
|
||||
|
||||
protected getReverse(): BaseBattleDiff {
|
||||
return new DroneDestroyedDiff(this.drone, this.duration);
|
||||
return new DroneRecalledDiff(this.drone);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A drone is destroyed
|
||||
* A drone is recalled
|
||||
*/
|
||||
export class DroneDestroyedDiff extends BaseBattleShipDiff {
|
||||
export class DroneRecalledDiff extends BaseBattleShipDiff {
|
||||
// Drone object
|
||||
drone: Drone
|
||||
|
||||
// Remaining duration
|
||||
duration: number
|
||||
|
||||
constructor(drone: Drone, duration = drone.duration) {
|
||||
constructor(drone: Drone) {
|
||||
super(drone.owner);
|
||||
|
||||
this.drone = drone;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
protected applyOnShip(ship: Ship, battle: Battle): void {
|
||||
|
@ -50,7 +41,7 @@ module TK.SpaceTac {
|
|||
}
|
||||
|
||||
protected getReverse(): BaseBattleDiff {
|
||||
return new DroneDeployedDiff(this.drone, this.duration);
|
||||
return new DroneDeployedDiff(this.drone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,10 @@ module TK.SpaceTac {
|
|||
battle.applyDiffs(effect2.getOnDiffs(ship, ship));
|
||||
check.equals(ship.getAttribute("maneuvrability"), 30, "applied 2");
|
||||
|
||||
battle.applyDiffs(effect1.getOffDiffs(ship, ship));
|
||||
battle.applyDiffs(effect1.getOffDiffs(ship));
|
||||
check.equals(ship.getAttribute("maneuvrability"), 10, "reverted 1");
|
||||
|
||||
battle.applyDiffs(effect2.getOffDiffs(ship, ship));
|
||||
battle.applyDiffs(effect2.getOffDiffs(ship));
|
||||
check.equals(ship.getAttribute("maneuvrability"), 0, "reverted 2");
|
||||
});
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ module TK.SpaceTac {
|
|||
];
|
||||
}
|
||||
|
||||
getOffDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
getOffDiffs(ship: Ship): BaseBattleDiff[] {
|
||||
return [
|
||||
new ShipAttributeDiff(ship, this.attrcode, {}, { cumulative: this.value }),
|
||||
];
|
||||
|
|
|
@ -14,10 +14,10 @@ module TK.SpaceTac {
|
|||
battle.applyDiffs(effect2.getOnDiffs(ship, ship));
|
||||
check.equals(ship.getAttribute("precision"), 3, "applied 2");
|
||||
|
||||
battle.applyDiffs(effect1.getOffDiffs(ship, ship));
|
||||
battle.applyDiffs(effect1.getOffDiffs(ship));
|
||||
check.equals(ship.getAttribute("precision"), 3, "reverted 1");
|
||||
|
||||
battle.applyDiffs(effect2.getOffDiffs(ship, ship));
|
||||
battle.applyDiffs(effect2.getOffDiffs(ship));
|
||||
check.equals(ship.getAttribute("precision"), 12, "reverted 2");
|
||||
});
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ module TK.SpaceTac {
|
|||
];
|
||||
}
|
||||
|
||||
getOffDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
getOffDiffs(ship: Ship): BaseBattleDiff[] {
|
||||
return [
|
||||
new ShipAttributeDiff(ship, this.attrcode, {}, { limit: this.value }),
|
||||
];
|
||||
|
|
|
@ -14,10 +14,10 @@ module TK.SpaceTac {
|
|||
battle.applyDiffs(effect2.getOnDiffs(ship, ship));
|
||||
check.equals(ship.getAttribute("hull_capacity"), 120, "applied 2");
|
||||
|
||||
battle.applyDiffs(effect1.getOffDiffs(ship, ship));
|
||||
battle.applyDiffs(effect1.getOffDiffs(ship));
|
||||
check.equals(ship.getAttribute("hull_capacity"), 90, "reverted 1");
|
||||
|
||||
battle.applyDiffs(effect2.getOffDiffs(ship, ship));
|
||||
battle.applyDiffs(effect2.getOffDiffs(ship));
|
||||
check.equals(ship.getAttribute("hull_capacity"), 100, "reverted 2");
|
||||
});
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ module TK.SpaceTac {
|
|||
];
|
||||
}
|
||||
|
||||
getOffDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
getOffDiffs(ship: Ship): BaseBattleDiff[] {
|
||||
return [
|
||||
new ShipAttributeDiff(ship, this.attrcode, {}, { multiplier: this.value }),
|
||||
];
|
||||
|
|
|
@ -28,7 +28,21 @@ module TK.SpaceTac {
|
|||
/**
|
||||
* Get the list of diffs needed to remove this effect on a ship
|
||||
*/
|
||||
getOffDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
getOffDiffs(ship: Ship): BaseBattleDiff[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of diffs to apply when this effect is active on a ship beginning its turn
|
||||
*/
|
||||
getTurnStartDiffs(ship: Ship): BaseBattleDiff[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of diffs to apply when this effect is active on a ship ending its turn
|
||||
*/
|
||||
getTurnEndDiffs(ship: Ship): BaseBattleDiff[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ module TK.SpaceTac {
|
|||
|
||||
let previous = ship.active_effects.get(this.id);
|
||||
if (previous) {
|
||||
result = result.concat(previous.getOffDiffs(ship, source));
|
||||
result = result.concat(previous.getOffDiffs(ship));
|
||||
}
|
||||
|
||||
result.push(new ShipEffectAddedDiff(ship, this));
|
||||
|
@ -35,17 +35,38 @@ module TK.SpaceTac {
|
|||
return result;
|
||||
}
|
||||
|
||||
getOffDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
getOffDiffs(ship: Ship): BaseBattleDiff[] {
|
||||
let result: BaseBattleDiff[] = [];
|
||||
|
||||
if (ship.active_effects.get(this.id)) {
|
||||
result.push(new ShipEffectRemovedDiff(ship, this));
|
||||
result = result.concat(this.base.getOffDiffs(ship, source));
|
||||
result = result.concat(this.base.getOffDiffs(ship));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getTurnStartDiffs(ship: Ship): BaseBattleDiff[] {
|
||||
if (ship.active_effects.get(this.id)) {
|
||||
return this.base.getTurnStartDiffs(ship);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
getTurnEndDiffs(ship: Ship): BaseBattleDiff[] {
|
||||
if (ship.active_effects.get(this.id)) {
|
||||
if (this.duration > 1) {
|
||||
let result: BaseBattleDiff[] = [new ShipEffectChangedDiff(ship, this, -1)];
|
||||
return result.concat(this.base.getTurnEndDiffs(ship));
|
||||
} else {
|
||||
return this.getOffDiffs(ship);
|
||||
}
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
isBeneficial(): boolean {
|
||||
return this.base.isBeneficial();
|
||||
}
|
||||
|
|
|
@ -15,12 +15,89 @@ module TK.SpaceTac {
|
|||
check.equals(ship.getValue("shield"), 95);
|
||||
});
|
||||
|
||||
test.case("estimates if the effect is beneficial", check => {
|
||||
let effect = new ValueEffect("hull", 12);
|
||||
check.equals(effect.isBeneficial(), true, "12");
|
||||
|
||||
effect = new ValueEffect("hull", -12);
|
||||
check.equals(effect.isBeneficial(), false, "-12");
|
||||
|
||||
effect = new ValueEffect("hull", 0, 8);
|
||||
check.equals(effect.isBeneficial(), true, "0 8");
|
||||
|
||||
effect = new ValueEffect("hull", 0, -8);
|
||||
check.equals(effect.isBeneficial(), false, "0 -8");
|
||||
|
||||
effect = new ValueEffect("hull", 4, -3);
|
||||
check.equals(effect.isBeneficial(), true, "4 -3");
|
||||
|
||||
effect = new ValueEffect("hull", 4, -4);
|
||||
check.equals(effect.isBeneficial(), true, "4 -4");
|
||||
|
||||
effect = new ValueEffect("hull", 3, -4);
|
||||
check.equals(effect.isBeneficial(), false, "3 -4");
|
||||
|
||||
effect = new ValueEffect("hull", -4, 4);
|
||||
check.equals(effect.isBeneficial(), false, "-4 4");
|
||||
|
||||
effect = new ValueEffect("hull", 0, 0, 12);
|
||||
check.equals(effect.isBeneficial(), true, "0 0 12");
|
||||
|
||||
effect = new ValueEffect("hull", 0, 0, -12);
|
||||
check.equals(effect.isBeneficial(), false, "0 0 -12");
|
||||
|
||||
effect = new ValueEffect("hull", 0, 0, 0, 8);
|
||||
check.equals(effect.isBeneficial(), true, "0 0 0 8");
|
||||
|
||||
effect = new ValueEffect("hull", 0, 0, 0, -8);
|
||||
check.equals(effect.isBeneficial(), false, "0 0 0 -8");
|
||||
|
||||
effect = new ValueEffect("hull", 0, 0, 4, -3);
|
||||
check.equals(effect.isBeneficial(), true, "0 0 4 -3");
|
||||
|
||||
effect = new ValueEffect("hull", 0, 0, 4, -4);
|
||||
check.equals(effect.isBeneficial(), true, "0 0 4 -4");
|
||||
|
||||
effect = new ValueEffect("hull", 0, 0, 3, -4);
|
||||
check.equals(effect.isBeneficial(), false, "0 0 3 -4");
|
||||
|
||||
effect = new ValueEffect("hull", 0, 0, -4, 4);
|
||||
check.equals(effect.isBeneficial(), false, "0 0 -4 4");
|
||||
});
|
||||
|
||||
test.case("has a description", check => {
|
||||
let effect = new ValueEffect("power", 12);
|
||||
check.equals(effect.getDescription(), "power +12");
|
||||
|
||||
effect = new ValueEffect("power", -4);
|
||||
check.equals(effect.getDescription(), "power -4");
|
||||
|
||||
effect = new ValueEffect("power");
|
||||
check.equals(effect.getDescription(), "no effect");
|
||||
|
||||
effect = new ValueEffect("power", 0, -5);
|
||||
check.equals(effect.getDescription(), "power -5 when removed");
|
||||
|
||||
effect = new ValueEffect("power", 5, -5);
|
||||
check.equals(effect.getDescription(), "power +5 while active");
|
||||
|
||||
effect = new ValueEffect("power", 5, -6);
|
||||
check.equals(effect.getDescription(), "power +5 on, -6 off");
|
||||
|
||||
effect = new ValueEffect("power", 0, 0, 6);
|
||||
check.equals(effect.getDescription(), "power +6 on turn start");
|
||||
|
||||
effect = new ValueEffect("power", 0, 0, 0, -3);
|
||||
check.equals(effect.getDescription(), "power -3 on turn end");
|
||||
|
||||
effect = new ValueEffect("power", 0, 0, 3, -3);
|
||||
check.equals(effect.getDescription(), "power +3 during turn");
|
||||
|
||||
effect = new ValueEffect("power", 0, 0, 4, -3);
|
||||
check.equals(effect.getDescription(), "power +4 on turn start, -3 on turn end");
|
||||
|
||||
effect = new ValueEffect("power", 1, 2, 3, 4);
|
||||
check.equals(effect.getDescription(), "power +1 on, +2 off, +3 on turn start, +4 on turn end");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
/// <reference path="BaseEffect.ts"/>
|
||||
|
||||
module TK.SpaceTac {
|
||||
function strval(value: number) {
|
||||
return `${value > 0 ? "+" : "-"}${Math.abs(value)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Effect to add (or subtract if negative) an amount to a ship value.
|
||||
*
|
||||
|
@ -10,22 +14,69 @@ module TK.SpaceTac {
|
|||
// Affected value
|
||||
valuetype: keyof ShipValues
|
||||
|
||||
// Value to add (or subtract if negative)
|
||||
value: number
|
||||
// Value to add (or subtract if negative), when the effect is applied to a ship
|
||||
value_on: number
|
||||
|
||||
constructor(valuetype: keyof ShipValues, value: number = 0) {
|
||||
// Value to add (or subtract if negative), when the effect is removed from a ship
|
||||
value_off: number
|
||||
|
||||
// Value to add (or subtract if negative), when the effect is active on a ship starting its turn
|
||||
value_start: number
|
||||
|
||||
// Value to add (or subtract if negative), when the effect is active on a ship ending its turn
|
||||
value_end: number
|
||||
|
||||
constructor(valuetype: keyof ShipValues, value_on = 0, value_off = 0, value_start = 0, value_end = 0) {
|
||||
super("value");
|
||||
|
||||
this.valuetype = valuetype;
|
||||
this.value = value;
|
||||
this.value_on = value_on;
|
||||
this.value_off = value_off;
|
||||
this.value_start = value_start;
|
||||
this.value_end = value_end;
|
||||
}
|
||||
|
||||
getOnDiffs(ship: Ship, source: Ship | Drone): BaseBattleDiff[] {
|
||||
return ship.getValueDiffs(this.valuetype, this.value, true);
|
||||
if (this.value_on) {
|
||||
return ship.getValueDiffs(this.valuetype, this.value_on, true);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
getOffDiffs(ship: Ship): BaseBattleDiff[] {
|
||||
if (this.value_off) {
|
||||
return ship.getValueDiffs(this.valuetype, this.value_off, true);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
getTurnStartDiffs(ship: Ship): BaseBattleDiff[] {
|
||||
if (this.value_start) {
|
||||
return ship.getValueDiffs(this.valuetype, this.value_start, true);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
getTurnEndDiffs(ship: Ship): BaseBattleDiff[] {
|
||||
if (this.value_end) {
|
||||
return ship.getValueDiffs(this.valuetype, this.value_end, true);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
isBeneficial(): boolean {
|
||||
return this.value >= 0;
|
||||
if (this.value_off < -this.value_on || this.value_end < -this.value_start) {
|
||||
// after value is lower than before
|
||||
return false;
|
||||
} else if ((this.value_off && this.value_off == -this.value_on) || (this.value_end && this.value_end == -this.value_start)) {
|
||||
return this.value_on > 0 || this.value_start > 0;
|
||||
} else {
|
||||
return this.value_on > 0 || this.value_off > 0 || this.value_start > 0 || this.value_end > 0;
|
||||
}
|
||||
}
|
||||
|
||||
getFullCode(): string {
|
||||
|
@ -34,7 +85,44 @@ module TK.SpaceTac {
|
|||
|
||||
getDescription(): string {
|
||||
let attrname = SHIP_VALUES_NAMES[this.valuetype];
|
||||
return `${attrname} ${this.value > 0 ? "+" : "-"}${Math.abs(this.value)}`;
|
||||
|
||||
let parts: string[] = [];
|
||||
|
||||
if (this.value_on) {
|
||||
if (this.value_off == -this.value_on) {
|
||||
parts.push(`${strval(this.value_on)} while active`);
|
||||
} else {
|
||||
if (this.value_off) {
|
||||
parts.push(`${strval(this.value_on)} on`);
|
||||
parts.push(`${strval(this.value_off)} off`);
|
||||
} else {
|
||||
parts.push(strval(this.value_on));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.value_start) {
|
||||
if (this.value_end == -this.value_start) {
|
||||
parts.push(`${strval(this.value_start)} during turn`);
|
||||
} else {
|
||||
parts.push(`${strval(this.value_start)} on turn start`);
|
||||
if (this.value_end) {
|
||||
parts.push(`${strval(this.value_end)} on turn end`);
|
||||
}
|
||||
}
|
||||
} else if (this.value_end) {
|
||||
parts.push(`${strval(this.value_end)} on turn end`);
|
||||
}
|
||||
|
||||
if (this.value_off && !this.value_on) {
|
||||
parts.push(`${strval(this.value_off)} when removed`);
|
||||
}
|
||||
|
||||
if (parts.length) {
|
||||
return `${attrname} ${parts.join(', ')}`;
|
||||
} else {
|
||||
return "no effect";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ module TK.SpaceTac.Equipments {
|
|||
this.addAttributeEffect("hull_capacity", leveled(60));
|
||||
this.addAttributeEffect("precision", leveled(2));
|
||||
this.addTriggerAction(leveled(1, 0.1), [
|
||||
new EffectTemplate(new ValueEffect("hull"), { value: leveled(60) })
|
||||
new EffectTemplate(new ValueEffect("hull"), { value_on: leveled(60) })
|
||||
])
|
||||
this.setCooldown(irepeat(1), irepeat(4));
|
||||
}
|
||||
|
|
|
@ -5,26 +5,26 @@ module TK.SpaceTac.Specs {
|
|||
|
||||
let equipment = template.generate(1);
|
||||
check.equals(equipment.requirements, { "skill_quantum": 1 });
|
||||
compare_drone_action(check, equipment.action, new DeployDroneAction(equipment, 4, 300, 10, 150, [
|
||||
new ValueEffect("hull", 2)
|
||||
compare_drone_action(check, equipment.action, new DeployDroneAction(equipment, 3, 300, 150, [
|
||||
new ValueEffect("hull", 0, 0, 0, 30)
|
||||
]));
|
||||
|
||||
equipment = template.generate(2);
|
||||
check.equals(equipment.requirements, { "skill_quantum": 4 });
|
||||
compare_drone_action(check, equipment.action, new DeployDroneAction(equipment, 4, 310, 11, 155, [
|
||||
new ValueEffect("hull", 3)
|
||||
compare_drone_action(check, equipment.action, new DeployDroneAction(equipment, 3, 310, 155, [
|
||||
new ValueEffect("hull", 0, 0, 0, 42)
|
||||
]));
|
||||
|
||||
equipment = template.generate(3);
|
||||
check.equals(equipment.requirements, { "skill_quantum": 7 });
|
||||
compare_drone_action(check, equipment.action, new DeployDroneAction(equipment, 4, 322, 12, 161, [
|
||||
new ValueEffect("hull", 4)
|
||||
compare_drone_action(check, equipment.action, new DeployDroneAction(equipment, 3, 322, 161, [
|
||||
new ValueEffect("hull", 0, 0, 0, 56)
|
||||
]));
|
||||
|
||||
equipment = template.generate(10);
|
||||
check.equals(equipment.requirements, { "skill_quantum": 49 });
|
||||
compare_drone_action(check, equipment.action, new DeployDroneAction(equipment, 10, 462, 26, 231, [
|
||||
new ValueEffect("hull", 11)
|
||||
compare_drone_action(check, equipment.action, new DeployDroneAction(equipment, 6, 462, 231, [
|
||||
new ValueEffect("hull", 0, 0, 0, 224)
|
||||
]));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,9 +9,8 @@ module TK.SpaceTac.Equipments {
|
|||
super(SlotType.Weapon, "Repair Drone", "Drone able to repair small hull breaches, using quantum patches", 190);
|
||||
|
||||
this.setSkillsRequirements({ "skill_quantum": leveled(1, 3) });
|
||||
this.setCooldown(irepeat(1), leveled(3));
|
||||
this.addDroneAction(leveled(4, 0.4), leveled(300, 10), leveled(10, 1), leveled(150, 5), [
|
||||
new EffectTemplate(new ValueEffect("hull"), { "value": istep(2) })
|
||||
this.addDroneAction(leveled(3, 0.2), leveled(300, 10), leveled(150, 5), [
|
||||
new EffectTemplate(new ValueEffect("hull"), { "value_end": leveled(30) })
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ module TK.SpaceTac.UI {
|
|||
this.loadSound("battle/weapon-missile-explosion.wav");
|
||||
this.loadSound("battle/drone-deploy.wav");
|
||||
this.loadSound("battle/drone-destroy.wav");
|
||||
this.loadSound("battle/drone-activate.wav");
|
||||
// this.loadSound("battle/drone-activate.wav");
|
||||
|
||||
this.loadSound("music/mechanolith.mp3");
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ module TK.SpaceTac.UI.Specs {
|
|||
test.case("displays power usage", check => {
|
||||
let bar = testgame.view.action_bar;
|
||||
let ship = new Ship();
|
||||
let action = new BaseAction("something", "Do something");
|
||||
let action = new BaseAction("something");
|
||||
let icon = new ActionIcon(bar, ship, action, 0);
|
||||
check.same(icon.img_power.visible, false, "initial state");
|
||||
|
||||
|
@ -124,7 +124,7 @@ module TK.SpaceTac.UI.Specs {
|
|||
icon.refresh(action);
|
||||
check.same(icon.img_targetting.visible, true, "selected");
|
||||
|
||||
icon.refresh(new BaseAction("other", "Other action"));
|
||||
icon.refresh(new BaseAction("other"));
|
||||
check.same(icon.img_targetting.visible, false, "other");
|
||||
})
|
||||
});
|
||||
|
|
|
@ -11,12 +11,12 @@ module TK.SpaceTac.UI.Specs {
|
|||
|
||||
let action1 = new MoveAction(new Equipment());
|
||||
nn(action1.equipment).name = "Engine";
|
||||
action1.name = "Move";
|
||||
check.patch(action1, "getVerb", () => "Move");
|
||||
let action2 = new TriggerAction(new Equipment(), [new DamageEffect(12)], 2, 50, 0);
|
||||
nn(action2.equipment).name = "Weapon";
|
||||
action2.name = "Fire";
|
||||
check.patch(action2, "getVerb", () => "Fire");
|
||||
let action3 = new EndTurnAction();
|
||||
action3.name = "End turn";
|
||||
check.patch(action3, "getVerb", () => "End turn");
|
||||
|
||||
ActionTooltip.fill(tooltip.getFiller(), ship, action1, 0);
|
||||
checkText(check, (<any>tooltip).container.content.children[1], "Engine");
|
||||
|
|
|
@ -12,7 +12,7 @@ module TK.SpaceTac.UI {
|
|||
let icon = builder.image([`equipment-${action.equipment ? action.equipment.code : "---"}`, `action-${action.code}`]);
|
||||
icon.scale.set(0.5);
|
||||
|
||||
builder.text(action.equipment ? action.equipment.name : action.name, 150, 0, { size: 24 });
|
||||
builder.text(action.equipment ? action.equipment.name : action.getVerb(), 150, 0, { size: 24 });
|
||||
|
||||
let cost = "";
|
||||
if (action instanceof MoveAction) {
|
||||
|
|
|
@ -18,9 +18,6 @@ module TK.SpaceTac.UI {
|
|||
// Activation effect
|
||||
activation: Phaser.Graphics
|
||||
|
||||
// Duration info
|
||||
duration: Phaser.Text
|
||||
|
||||
constructor(battleview: BattleView, drone: Drone) {
|
||||
super(battleview.game);
|
||||
|
||||
|
@ -48,11 +45,6 @@ module TK.SpaceTac.UI {
|
|||
this.sprite.scale.set(0.1, 0.1);
|
||||
this.add(this.sprite);
|
||||
|
||||
this.duration = new Phaser.Text(this.game, 0, 40, "", { font: "bold 16pt SpaceTac", fill: "#ffdd4b" });
|
||||
this.duration.anchor.set(0.5, 0.5);
|
||||
this.duration.visible = false;
|
||||
this.add(this.duration);
|
||||
|
||||
this.view.tooltip.bindDynamicText(this.sprite, () => {
|
||||
return this.drone.getDescription();
|
||||
});
|
||||
|
@ -93,11 +85,7 @@ module TK.SpaceTac.UI {
|
|||
* Set the tactical mode display
|
||||
*/
|
||||
setTacticalMode(active: boolean) {
|
||||
if (active) {
|
||||
this.duration.text = `${this.drone.duration}`;
|
||||
}
|
||||
this.sprite.scale.set(active ? 0.2 : 0.1);
|
||||
this.view.animations.setVisible(this.duration, active, 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,10 +184,8 @@ module TK.SpaceTac.UI {
|
|||
durations.push(this.processEndBattleEvent(diff));
|
||||
} else if (diff instanceof DroneDeployedDiff) {
|
||||
durations.push(this.processDroneDeployedEvent(diff));
|
||||
} else if (diff instanceof DroneDestroyedDiff) {
|
||||
durations.push(this.processDroneDestroyedEvent(diff));
|
||||
} else if (diff instanceof DroneAppliedDiff) {
|
||||
durations.push(this.processDroneAppliedEvent(diff));
|
||||
} else if (diff instanceof DroneRecalledDiff) {
|
||||
durations.push(this.processDroneRecalledEvent(diff));
|
||||
}
|
||||
|
||||
let delay = max([0].concat(durations));
|
||||
|
@ -286,7 +284,7 @@ module TK.SpaceTac.UI {
|
|||
}
|
||||
|
||||
// Drone destroyed
|
||||
private processDroneDestroyedEvent(event: DroneDestroyedDiff): number {
|
||||
private processDroneRecalledEvent(event: DroneRecalledDiff): number {
|
||||
let duration = this.view.arena.removeDrone(event.drone);
|
||||
|
||||
if (duration) {
|
||||
|
@ -297,7 +295,7 @@ module TK.SpaceTac.UI {
|
|||
}
|
||||
|
||||
// Drone applied
|
||||
private processDroneAppliedEvent(event: DroneAppliedDiff): number {
|
||||
/*private processDroneAppliedEvent(event: DroneAppliedDiff): number {
|
||||
let drone = this.view.arena.findDrone(event.drone);
|
||||
if (drone) {
|
||||
let duration = drone.setApplied();
|
||||
|
@ -310,6 +308,6 @@ module TK.SpaceTac.UI {
|
|||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ module TK.SpaceTac.UI {
|
|||
}
|
||||
} else if (action instanceof DeployDroneAction) {
|
||||
color = 0xe9f2f9;
|
||||
radius = action.effect_radius;
|
||||
radius = action.drone_radius;
|
||||
} else if (action instanceof ToggleAction) {
|
||||
color = 0xd3e448;
|
||||
radius = action.radius;
|
||||
|
|
Loading…
Reference in a new issue