1
0
Fork 0

Simplified drone system

This commit is contained in:
Michaël Lemaire 2017-05-23 01:00:02 +02:00
parent cbb860b1ec
commit ee1cbc9c14
9 changed files with 39 additions and 107 deletions

View file

@ -125,11 +125,8 @@ the end of turn (it will only start cooling down after being overheated).
Drones are static objects, deployed by ships, that apply effects in a circular zone around themselves.
A drone lasts for a given number of battle cycles. For example, if there are 8 ships in play, a 2-cycles
drone will try to activate 16 times, before being destroyed.
All drones activate between two ship turns. At each activation, the drone effects are applied to any ship
in the surrounding zone, except if less than a battle cycle passed since last activation for this ship.
Drones activate between two ship turns. At each activation, the drone effects are applied to any ship
in the surrounding zone. A drone will live for a given number of activations, before being destroyed.
Drones are fully autonomous, and once deployed, are not controlled by their owner ship.

2
TODO
View file

@ -27,7 +27,7 @@
* Actions: fix targetting not resetting when using keyboard shortcuts
* Suspend AI operation when the game is paused (window not focused)
* Add actions with cost dependent of distance (like current move actions)
* Keep move actions out of arena borders
* Keep move and drone actions out of arena borders
* Find incentives to move from starting position
* Outcome: add battle statistics and/or honors
* Outcome: disable the loot button if there is no loot

View file

@ -53,45 +53,6 @@ module TS.SpaceTac {
expect(effect.getApplyCalls()).toEqual([ship1, ship2]);
});
it("maintains ship application countdown", function () {
let battle = new Battle();
spyOn(battle, "getCycleLength").and.returnValue(7);
let ship = new Ship(battle.fleets[0]);
let drone = new Drone(ship, "test", 2);
expect(drone.getShipCountdown(ship)).toBe(0);
drone.startShipCountdown(ship);
expect(drone.getShipCountdown(ship)).toBe(7);
});
it("applies at most once per battle cycle", function () {
let battle = new Battle();
let ship1 = new Ship(battle.fleets[0], "ship1");
ship1.setArenaPosition(0, 0);
let ship2 = new Ship(battle.fleets[1], "ship2");
ship2.setArenaPosition(100, 100);
battle.throwInitiative();
expect(battle.getCycleLength()).toEqual(2);
let [drone, effect] = newTestDrone(2, 2, 8, ship1);
expect(effect.getApplyCalls()).toEqual([]);
drone.activate();
expect(effect.getApplyCalls()).toEqual([ship1]);
drone.activate();
expect(effect.getApplyCalls()).toEqual([]);
drone.activate();
expect(effect.getApplyCalls()).toEqual([ship1]);
drone.activate();
expect(effect.getApplyCalls()).toEqual([]);
drone.activate();
expect(effect.getApplyCalls()).toEqual([ship1]);
});
it("signals the need for destruction after its lifetime", function () {
let battle = new Battle();
let owner = new Ship(battle.fleets[0]);

View file

@ -23,14 +23,11 @@ module TS.SpaceTac {
// Effects to apply
effects: BaseEffect[] = [];
// Cycle countdown for ships
countdown: [Ship, number][] = [];
constructor(owner: Ship, code = "drone", base_duration = 1) {
this.battle = owner.getBattle() || new Battle();
this.owner = owner;
this.code = code;
this.duration = base_duration * this.battle.getCycleLength();
this.duration = base_duration;
}
/**
@ -44,42 +41,11 @@ module TS.SpaceTac {
return `For ${this.duration} activation${this.duration > 1 ? "s" : ""}:\n${effects}`;
}
/**
* Get countdown until next activation for a given ship
*/
getShipCountdown(ship: Ship): number {
let countdown = 0;
this.countdown.forEach(([iship, icountdown]) => {
if (iship === ship) {
countdown = icountdown;
}
});
return countdown;
}
/**
* Start the countdown for a given ship
*/
startShipCountdown(ship: Ship): void {
let found = false;
this.countdown = this.countdown.map(([iship, countdown]): [Ship, number] => {
if (iship === ship) {
found = true;
return [iship, this.battle.getCycleLength()];
} else {
return [iship, countdown];
}
});
if (!found) {
this.countdown.push([ship, this.battle.getCycleLength()]);
}
}
/**
* Get the list of affected ships.
*/
getAffectedShips(): Ship[] {
let ships = ifilter(this.battle.iships(), ship => ship.alive && ship.isInCircle(this.x, this.y, this.radius) && this.getShipCountdown(ship) == 0);
let ships = ifilter(this.battle.iships(), ship => ship.alive && ship.isInCircle(this.x, this.y, this.radius));
return imaterialize(ships);
}
@ -95,7 +61,6 @@ module TS.SpaceTac {
}
ships.forEach(ship => {
this.startShipCountdown(ship);
this.effects.forEach(effect => effect.applyOnShip(ship));
});
}
@ -107,8 +72,6 @@ module TS.SpaceTac {
activate(log = true) {
this.apply(this.getAffectedShips(), log);
this.countdown = this.countdown.map(([ship, countdown]): [Ship, number] => [ship, countdown - 1]).filter(([ship, countdown]) => countdown > 0);
this.duration--;
if (this.duration == 0) {
this.battle.removeDrone(this, log);

View file

@ -30,7 +30,6 @@ module TS.SpaceTac {
let ship = battle.fleets[0].addShip();
ship.setArenaPosition(0, 0);
battle.playing_ship = ship;
spyOn(battle, "getCycleLength").and.returnValue(4);
TestTools.setShipAP(ship, 3);
let equipment = new Equipment(SlotType.Weapon, "testdrone");
let action = new DeployDroneAction(equipment, 2, 8, 2, 4, [new DamageEffect(50)]);
@ -44,7 +43,7 @@ module TS.SpaceTac {
let drone = battle.drones[0];
expect(drone.code).toEqual("testdrone");
expect(drone.duration).toEqual(8);
expect(drone.duration).toEqual(2);
expect(drone.owner).toBe(ship);
expect(drone.x).toEqual(5);
expect(drone.y).toEqual(0);

View file

@ -65,7 +65,7 @@ module TS.SpaceTac {
}
getEffectsDescription(): string {
let desc = `Deploy drone for ${this.lifetime} cycle${this.lifetime > 1 ? "s" : ""} (power usage ${this.power}, max range ${this.deploy_distance}km)`;
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`;
return "• " + effect.getDescription() + " " + suffix;

View file

@ -5,26 +5,26 @@ module TS.SpaceTac.Equipments {
let equipment = template.generate(1);
expect(equipment.requirements).toEqual({ "skill_human": 1 });
expect(equipment.action).toEqual(new DeployDroneAction(equipment, 4, 300, 1, 100, [new ValueEffect("hull", 30)]));
expect(equipment.action).toEqual(new DeployDroneAction(equipment, 4, 300, 10, 100, [new ValueEffect("hull", 10)]));
equipment = template.generate(2);
expect(equipment.requirements).toEqual({ "skill_human": 2 });
expect(equipment.action).toEqual(new DeployDroneAction(equipment, 4, 310, 1, 110, [new ValueEffect("hull", 33)]));
expect(equipment.action).toEqual(new DeployDroneAction(equipment, 4, 310, 11, 110, [new ValueEffect("hull", 12)]));
equipment = template.generate(3);
expect(equipment.requirements).toEqual({ "skill_human": 3 });
expect(equipment.action).toEqual(new DeployDroneAction(equipment, 4, 320, 1, 120, [new ValueEffect("hull", 36)]));
expect(equipment.action).toEqual(new DeployDroneAction(equipment, 4, 320, 12, 120, [new ValueEffect("hull", 14)]));
equipment = template.generate(10);
expect(equipment.requirements).toEqual({ "skill_human": 10 });
expect(equipment.action).toEqual(new DeployDroneAction(equipment, 4, 390, 2, 190, [new ValueEffect("hull", 57)]));
expect(equipment.action).toEqual(new DeployDroneAction(equipment, 4, 390, 19, 190, [new ValueEffect("hull", 28)]));
});
it("generates a drone that may repair ships hull", function () {
let template = new RepairDrone();
let equipment = template.generate(1);
expect(equipment.action).toEqual(new DeployDroneAction(equipment, 4, 300, 1, 100, [new ValueEffect("hull", 30)]));
expect(equipment.action).toEqual(new DeployDroneAction(equipment, 4, 300, 10, 100, [new ValueEffect("hull", 10)]));
let battle = new Battle();
let ship = battle.fleets[0].addShip();
@ -36,11 +36,11 @@ module TS.SpaceTac.Equipments {
expect(battle.drones.length).toBe(1);
let drone = battle.drones[0];
expect(drone.duration).toBe(1);
expect(drone.duration).toBe(10);
ship.setAttribute("hull_capacity", 100);
ship.setValue("hull", 55);
ship.setValue("hull", 85);
drone.apply([ship]);
expect(ship.getValue("hull")).toBe(85);
expect(ship.getValue("hull")).toBe(95);
drone.apply([ship]);
expect(ship.getValue("hull")).toBe(100);
});

View file

@ -10,8 +10,8 @@ module TS.SpaceTac.Equipments {
this.setSkillsRequirements({ "skill_human": 1 });
this.setCooldown(irepeat(1), istep(3, irepeat(0.2)));
this.addDroneAction(irepeat(4), istep(300, irepeat(10)), istep(1, irepeat(0.2)), istep(100, irepeat(10)), [
new EffectTemplate(new ValueEffect("hull"), { "value": istep(30, irepeat(3)) })
this.addDroneAction(irepeat(4), istep(300, irepeat(10)), istep(10, irepeat(1)), istep(100, irepeat(10)), [
new EffectTemplate(new ValueEffect("hull"), { "value": istep(10, irepeat(2)) })
]);
}
}

View file

@ -4,22 +4,25 @@ module TS.SpaceTac.UI {
*/
export class ArenaDrone extends Phaser.Group {
// Link to view
view: BattleView;
view: BattleView
// Link to displayed drone
drone: Drone;
drone: Drone
// Sprite
sprite: Phaser.Button;
sprite: Phaser.Button
// Radius
radius: Phaser.Graphics;
radius: Phaser.Graphics
// Activation effect
activation: Phaser.Graphics;
activation: Phaser.Graphics
// Duration info
duration: Phaser.Text
// Destroyed state
destroyed = false;
destroyed = false
constructor(battleview: BattleView, drone: Drone) {
super(battleview.game);
@ -32,7 +35,7 @@ module TS.SpaceTac.UI {
this.radius.beginFill(0xe9f2f9, 0.0);
this.radius.drawCircle(0, 0, drone.radius * 2);
this.radius.endFill();
this.addChild(this.radius);
this.add(this.radius);
this.activation = new Phaser.Graphics(this.game, 0, 0);
this.activation.lineStyle(2, 0xe9f2f9, 0.7);
@ -40,12 +43,17 @@ module TS.SpaceTac.UI {
this.activation.drawCircle(0, 0, drone.radius * 2);
this.activation.endFill();
this.activation.visible = false;
this.addChild(this.activation);
this.add(this.activation);
this.sprite = new Phaser.Button(this.game, 0, 0, `battle-actions-deploy-${drone.code}`);
this.sprite.anchor.set(0.5, 0.5);
this.sprite.scale.set(0.1, 0.1);
this.addChild(this.sprite);
this.add(this.sprite);
this.duration = new Phaser.Text(this.game, 0, 40, "", { font: "bold 16pt Arial", 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.destroyed ? "" : this.drone.getDescription();
@ -90,8 +98,12 @@ module TS.SpaceTac.UI {
* Set the tactical mode display
*/
setTacticalMode(active: boolean) {
if (active) {
this.duration.text = `${this.drone.duration}`;
}
this.sprite.rotation = active ? -this.rotation : 0;
this.sprite.scale.set(active ? 0.3 : 0.1);
this.sprite.scale.set(active ? 0.2 : 0.1);
this.view.animations.setVisible(this.duration, active, 200);
}
}
}