diff --git a/graphics/exported/equipment/ionthruster.png b/graphics/exported/equipment/ionthruster.png new file mode 100644 index 0000000..1f1c189 Binary files /dev/null and b/graphics/exported/equipment/ionthruster.png differ diff --git a/graphics/exported/equipment/voidhawkengine.png b/graphics/exported/equipment/voidhawkengine.png new file mode 100644 index 0000000..d9d679a Binary files /dev/null and b/graphics/exported/equipment/voidhawkengine.png differ diff --git a/graphics/ui/actions.svg b/graphics/ui/actions.svg index b60bb71..1a5411f 100644 --- a/graphics/ui/actions.svg +++ b/graphics/ui/actions.svg @@ -16,13 +16,29 @@ version="1.1" inkscape:version="0.92.1 r15371" sodipodi:docname="actions.svg" - inkscape:export-filename="/home/michael/workspace/perso/spacetac/graphics/exported/equipment/invertershield.png" + inkscape:export-filename="/home/michael/workspace/perso/spacetac/graphics/exported/equipment/voidhawkengine.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90" viewBox="0 0 256 256" enable-background="new"> + + + + + @@ -819,6 +835,244 @@ stdDeviation="1.6714617" id="feGaussianBlur5574" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + style="display:none"> + + + + + + + + + + + + + + + + + { - equipment.action = new MoveAction(equipment, resolveForLevel(distance_per_power, level)); + equipment.action = new MoveAction(equipment, resolveForLevel(distance_per_power, level), resolveForLevel(safety_distance, level)); }); } diff --git a/src/core/actions/MoveAction.spec.ts b/src/core/actions/MoveAction.spec.ts index afec4bc..34e9380 100644 --- a/src/core/actions/MoveAction.spec.ts +++ b/src/core/actions/MoveAction.spec.ts @@ -106,7 +106,7 @@ module TS.SpaceTac { expect(result).toEqual(Target.newFromLocation(12, 5)); }); - it("move exclusion radius is applied correctly over two ships", function () { + it("exclusion radius is applied correctly over two ships", function () { var battle = TestTools.createBattle(1, 2); var ship = battle.fleets[0].ships[0]; var enemy1 = battle.fleets[1].ships[0]; @@ -122,5 +122,24 @@ module TS.SpaceTac { var result = action.checkLocationTarget(ship, Target.newFromLocation(0, 110)); expect(result).toEqual(Target.newFromLocation(0, 65)); }); + + it("exclusion radius does not make the ship go back", function () { + var battle = TestTools.createBattle(1, 2); + var ship = battle.fleets[0].ships[0]; + var enemy1 = battle.fleets[1].ships[0]; + var enemy2 = battle.fleets[1].ships[1]; + TestTools.setShipAP(ship, 100); + enemy1.setArenaPosition(0, 50); + enemy2.setArenaPosition(0, 80); + + var action = new MoveAction(new Equipment()); + action.distance_per_power = 1000; + action.safety_distance = 60; + + let result = action.checkLocationTarget(ship, Target.newFromLocation(0, 100)); + expect(result).toBeNull(); + result = action.checkLocationTarget(ship, Target.newFromLocation(0, 140)); + expect(result).toEqual(Target.newFromLocation(0, 140)); + }); }); } diff --git a/src/core/actions/MoveAction.ts b/src/core/actions/MoveAction.ts index 4b7dd73..50e4dd2 100644 --- a/src/core/actions/MoveAction.ts +++ b/src/core/actions/MoveAction.ts @@ -10,11 +10,11 @@ module TS.SpaceTac { // Equipment cannot be null (engine) equipment: Equipment - constructor(equipment: Equipment, distance_per_power = 0) { + constructor(equipment: Equipment, distance_per_power = 0, safety_distance = 120) { super("move", "Move", true, equipment); this.distance_per_power = distance_per_power; - this.safety_distance = 120; + this.safety_distance = safety_distance; } checkCannotBeApplied(ship: Ship, remaining_ap: number | null = null): string | null { @@ -70,7 +70,13 @@ module TS.SpaceTac { let ships = imaterialize(ifilter(battle.iships(true), s => s !== ship)); ships = ships.sort((a, b) => cmp(a.getDistanceTo(ship), b.getDistanceTo(ship), true)); ships.forEach(s => { - target = target.moveOutOfCircle(s.arena_x, s.arena_y, this.safety_distance, ship.arena_x, ship.arena_y); + let new_target = target.moveOutOfCircle(s.arena_x, s.arena_y, this.safety_distance, ship.arena_x, ship.arena_y); + if (target != new_target && s.getDistanceTo(ship) < this.safety_distance) { + // Already inside the nearest ship's exclusion area + target = Target.newFromLocation(ship.arena_x, ship.arena_y); + } else { + target = new_target; + } }); } return target; @@ -85,10 +91,10 @@ module TS.SpaceTac { return target.constraintInRange(ship.arena_x, ship.arena_y, max_distance); } - checkLocationTarget(ship: Ship, target: Target): Target { + checkLocationTarget(ship: Ship, target: Target): Target | null { target = this.applyReachableRange(ship, target); target = this.applyExclusion(ship, target); - return target; + return target.getDistanceTo(ship.location) > 0 ? target : null; } protected customApply(ship: Ship, target: Target) { @@ -96,7 +102,7 @@ module TS.SpaceTac { } getEffectsDescription(): string { - return `Move: ${this.distance_per_power}km per power point`; + return `Move: ${this.distance_per_power}km per power point (safety: ${this.safety_distance}km)`; } } } diff --git a/src/core/equipments/Engines.spec.ts b/src/core/equipments/Engines.spec.ts new file mode 100644 index 0000000..0ba7bd6 --- /dev/null +++ b/src/core/equipments/Engines.spec.ts @@ -0,0 +1,99 @@ +module TS.SpaceTac.Equipments { + describe("Engines", function () { + it("generates RocketEngine based on level", function () { + let template = new RocketEngine(); + + let equipment = template.generate(1); + expect(equipment.requirements).toEqual({ "skill_materials": 1 }); + expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", 2)]); + expect(equipment.cooldown).toEqual(new Cooldown(2, 0)); + expect(equipment.action).toEqual(new MoveAction(equipment, 200)); + expect(equipment.price).toEqual(120); + + equipment = template.generate(2); + expect(equipment.requirements).toEqual({ "skill_materials": 2 }); + expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", 4)]); + expect(equipment.cooldown).toEqual(new Cooldown(2, 0)); + expect(equipment.action).toEqual(new MoveAction(equipment, 220)); + expect(equipment.price).toEqual(320); + + equipment = template.generate(3); + expect(equipment.requirements).toEqual({ "skill_materials": 3 }); + expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", 6)]); + expect(equipment.cooldown).toEqual(new Cooldown(2, 0)); + expect(equipment.action).toEqual(new MoveAction(equipment, 240)); + expect(equipment.price).toEqual(720); + + equipment = template.generate(10); + expect(equipment.requirements).toEqual({ "skill_materials": 10 }); + expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", 20)]); + expect(equipment.cooldown).toEqual(new Cooldown(2, 0)); + expect(equipment.action).toEqual(new MoveAction(equipment, 380)); + expect(equipment.price).toEqual(9120); + }); + + it("generates IonEngine based on level", function () { + let template = new IonEngine(); + + let equipment = template.generate(1); + expect(equipment.requirements).toEqual({ "skill_photons": 1 }); + expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", 1)]); + expect(equipment.cooldown).toEqual(new Cooldown(3, 1)); + expect(equipment.action).toEqual(new MoveAction(equipment, 120)); + expect(equipment.price).toEqual(150); + + equipment = template.generate(2); + expect(equipment.requirements).toEqual({ "skill_photons": 2 }); + expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", 2)]); + expect(equipment.cooldown).toEqual(new Cooldown(3, 1)); + expect(equipment.action).toEqual(new MoveAction(equipment, 135)); + expect(equipment.price).toEqual(380); + + equipment = template.generate(3); + expect(equipment.requirements).toEqual({ "skill_photons": 3 }); + expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", 3)]); + expect(equipment.cooldown).toEqual(new Cooldown(3, 1)); + expect(equipment.action).toEqual(new MoveAction(equipment, 150)); + expect(equipment.price).toEqual(840); + + equipment = template.generate(10); + expect(equipment.requirements).toEqual({ "skill_photons": 10 }); + expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", 10)]); + expect(equipment.cooldown).toEqual(new Cooldown(3, 1)); + expect(equipment.action).toEqual(new MoveAction(equipment, 255)); + expect(equipment.price).toEqual(10500); + }); + + it("generates VoidhawkEngine based on level", function () { + let template = new VoidhawkEngine(); + + let equipment = template.generate(1); + expect(equipment.requirements).toEqual({ "skill_gravity": 2 }); + expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", -5)]); + expect(equipment.cooldown).toEqual(new Cooldown(1, 0)); + expect(equipment.action).toEqual(new MoveAction(equipment, 2000, 250)); + expect(equipment.price).toEqual(340); + + equipment = template.generate(2); + expect(equipment.requirements).toEqual({ "skill_gravity": 4 }); + expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", -5)]); + expect(equipment.cooldown).toEqual(new Cooldown(1, 0)); + expect(equipment.action).toEqual(new MoveAction(equipment, 2000, 240)); + expect(equipment.price).toEqual(500); + + equipment = template.generate(3); + expect(equipment.requirements).toEqual({ "skill_gravity": 6 }); + expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", -4)]); + expect(equipment.cooldown).toEqual(new Cooldown(1, 0)); + expect(equipment.action).toEqual(new MoveAction(equipment, 2000, 230)); + expect(equipment.price).toEqual(820); + + equipment = template.generate(10); + expect(equipment.requirements).toEqual({ "skill_gravity": 20 }); + expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", 2)]); + expect(equipment.cooldown).toEqual(new Cooldown(1, 0)); + expect(equipment.action).toEqual(new MoveAction(equipment, 2000, 160)); + expect(equipment.price).toEqual(7540); + }); + }); +} diff --git a/src/core/equipments/Engines.ts b/src/core/equipments/Engines.ts new file mode 100644 index 0000000..67bd2cf --- /dev/null +++ b/src/core/equipments/Engines.ts @@ -0,0 +1,36 @@ +/// + +module TS.SpaceTac.Equipments { + export class RocketEngine extends LootTemplate { + constructor() { + super(SlotType.Engine, "Rocket Engine", "First-era conventional deep-space engine, based on gas exhausts pushed through a nozzle", 120); + + this.setSkillsRequirements({ "skill_materials": 1 }); + this.setCooldown(irepeat(2), 0); + this.addAttributeEffect("maneuvrability", 2); + this.addMoveAction(istep(200, irepeat(20))); + } + } + + export class IonEngine extends LootTemplate { + constructor() { + super(SlotType.Engine, "Ion Thruster", "Electric propulsion based on accelerating ions through an electrostatic grid", 150, 230); + + this.setSkillsRequirements({ "skill_photons": 1 }); + this.setCooldown(irepeat(3), irepeat(1)); + this.addAttributeEffect("maneuvrability", 1); + this.addMoveAction(istep(120, irepeat(15))); + } + } + + export class VoidhawkEngine extends LootTemplate { + constructor() { + super(SlotType.Engine, "VoidHawk Engine", "Mid-range gravity field warp generator, allowing to make small jumps", 340, 160); + + this.setSkillsRequirements({ "skill_gravity": 2 }); + this.setCooldown(irepeat(1), 0); + this.addAttributeEffect("maneuvrability", istep(-5, irepeat(0.8))); + this.addMoveAction(irepeat(2000), istep(250, irepeat(-10))); + } + } +} diff --git a/src/core/equipments/RocketEngine.spec.ts b/src/core/equipments/RocketEngine.spec.ts deleted file mode 100644 index fb0937a..0000000 --- a/src/core/equipments/RocketEngine.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -module TS.SpaceTac.Equipments { - describe("Rocket Engine", function () { - it("generates equipment based on level", function () { - let template = new RocketEngine(); - - let equipment = template.generate(1); - expect(equipment.requirements).toEqual({ "skill_photons": 1 }); - expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", 1)]); - expect(equipment.action).toEqual(new MoveAction(equipment, 200)); - - equipment = template.generate(2); - expect(equipment.requirements).toEqual({ "skill_photons": 2 }); - expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", 2)]); - expect(equipment.action).toEqual(new MoveAction(equipment, 220)); - - equipment = template.generate(3); - expect(equipment.requirements).toEqual({ "skill_photons": 3 }); - expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", 3)]); - expect(equipment.action).toEqual(new MoveAction(equipment, 240)); - - equipment = template.generate(10); - expect(equipment.requirements).toEqual({ "skill_photons": 10 }); - expect(equipment.effects).toEqual([new AttributeEffect("maneuvrability", 10)]); - expect(equipment.action).toEqual(new MoveAction(equipment, 380)); - }); - }); -} diff --git a/src/core/equipments/RocketEngine.ts b/src/core/equipments/RocketEngine.ts deleted file mode 100644 index a7c7d87..0000000 --- a/src/core/equipments/RocketEngine.ts +++ /dev/null @@ -1,14 +0,0 @@ -/// - -module TS.SpaceTac.Equipments { - export class RocketEngine extends LootTemplate { - constructor() { - super(SlotType.Engine, "Rocket Engine", "First-era conventional deep-space engine, based on gas exhausts pushed through a nozzle"); - - this.setSkillsRequirements({ "skill_photons": 1 }); - this.setCooldown(irepeat(2), 0); - this.addAttributeEffect("maneuvrability", 1); - this.addMoveAction(istep(200, irepeat(20))); - } - } -} diff --git a/src/ui/battle/ActionTooltip.spec.ts b/src/ui/battle/ActionTooltip.spec.ts index e366d55..629d094 100644 --- a/src/ui/battle/ActionTooltip.spec.ts +++ b/src/ui/battle/ActionTooltip.spec.ts @@ -21,7 +21,7 @@ module TS.SpaceTac.UI.Specs { ActionTooltip.fill(tooltip.getFiller(), ship, action1, 0); checkText((tooltip).container.content.children[1], "Engine"); checkText((tooltip).container.content.children[2], "Cost: 1 power per 0km"); - checkText((tooltip).container.content.children[3], "Move: 0km per power point"); + checkText((tooltip).container.content.children[3], "Move: 0km per power point (safety: 120km)"); checkText((tooltip).container.content.children[4], "[ 1 ]"); tooltip.hide();