diff --git a/README.md b/README.md index a32a4a4..fa2fbd6 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/TODO b/TODO index 13a237a..7cb34fc 100644 --- a/TODO +++ b/TODO @@ -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 diff --git a/src/core/Drone.spec.ts b/src/core/Drone.spec.ts index e7c9a84..d48899e 100644 --- a/src/core/Drone.spec.ts +++ b/src/core/Drone.spec.ts @@ -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]); diff --git a/src/core/Drone.ts b/src/core/Drone.ts index e39d550..2056f47 100644 --- a/src/core/Drone.ts +++ b/src/core/Drone.ts @@ -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); diff --git a/src/core/actions/DeployDroneAction.spec.ts b/src/core/actions/DeployDroneAction.spec.ts index a05da3c..c268034 100644 --- a/src/core/actions/DeployDroneAction.spec.ts +++ b/src/core/actions/DeployDroneAction.spec.ts @@ -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); diff --git a/src/core/actions/DeployDroneAction.ts b/src/core/actions/DeployDroneAction.ts index 34c1360..71b7195 100644 --- a/src/core/actions/DeployDroneAction.ts +++ b/src/core/actions/DeployDroneAction.ts @@ -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; diff --git a/src/core/equipments/RepairDrone.spec.ts b/src/core/equipments/RepairDrone.spec.ts index d9494ea..f1b8a23 100644 --- a/src/core/equipments/RepairDrone.spec.ts +++ b/src/core/equipments/RepairDrone.spec.ts @@ -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); }); diff --git a/src/core/equipments/RepairDrone.ts b/src/core/equipments/RepairDrone.ts index 5c4b92a..7a4914d 100644 --- a/src/core/equipments/RepairDrone.ts +++ b/src/core/equipments/RepairDrone.ts @@ -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)) }) ]); } } diff --git a/src/ui/battle/ArenaDrone.ts b/src/ui/battle/ArenaDrone.ts index 3e3e99c..51ffb5f 100644 --- a/src/ui/battle/ArenaDrone.ts +++ b/src/ui/battle/ArenaDrone.ts @@ -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); } } }