diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs
index 1926c2d204..e6ea859e95 100644
--- a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs
+++ b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs
@@ -235,7 +235,8 @@ namespace OpenRA.Mods.Common.Traits
if (!Ready)
return;
- var power = Instances.FirstOrDefault(i => !InstanceDisabled(i));
+ var power = Instances.Where(i => !InstanceDisabled(i))
+ .MinByOrDefault(a => (a.Self.CenterPosition - a.Self.World.Map.CenterOfCell(order.TargetLocation)).HorizontalLengthSquared);
if (power == null)
return;
diff --git a/OpenRA.Mods.TS/OpenRA.Mods.TS.csproj b/OpenRA.Mods.TS/OpenRA.Mods.TS.csproj
index 14bab04b9a..1dfe59b25f 100644
--- a/OpenRA.Mods.TS/OpenRA.Mods.TS.csproj
+++ b/OpenRA.Mods.TS/OpenRA.Mods.TS.csproj
@@ -74,6 +74,7 @@
+
diff --git a/OpenRA.Mods.TS/Traits/SupportPowers/AttackOrderPower.cs b/OpenRA.Mods.TS/Traits/SupportPowers/AttackOrderPower.cs
new file mode 100644
index 0000000000..76af6fe64a
--- /dev/null
+++ b/OpenRA.Mods.TS/Traits/SupportPowers/AttackOrderPower.cs
@@ -0,0 +1,148 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
+ * This file is part of OpenRA, which is free software. It is made
+ * available to you under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version. For more
+ * information, see COPYING.
+ */
+#endregion
+
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using OpenRA.Graphics;
+using OpenRA.Mods.Common.Graphics;
+using OpenRA.Mods.Common.Traits;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.TS.Traits
+{
+ class AttackOrderPowerInfo : SupportPowerInfo, Requires
+ {
+ public override object Create(ActorInitializer init) { return new AttackOrderPower(init.Self, this); }
+ }
+
+ class AttackOrderPower : SupportPower, INotifyCreated, INotifyBurstComplete
+ {
+ readonly AttackOrderPowerInfo info;
+ AttackBase attack;
+
+ public AttackOrderPower(Actor self, AttackOrderPowerInfo info)
+ : base(self, info)
+ {
+ this.info = info;
+ }
+
+ public override void SelectTarget(Actor self, string order, SupportPowerManager manager)
+ {
+ Game.Sound.PlayToPlayer(manager.Self.Owner, Info.SelectTargetSound);
+ self.World.OrderGenerator = new SelectAttackPowerTarget(self, order, manager, info.Cursor, MouseButton.Left, attack);
+ }
+
+ public override void Activate(Actor self, Order order, SupportPowerManager manager)
+ {
+ base.Activate(self, order, manager);
+ attack.AttackTarget(Target.FromCell(self.World, order.TargetLocation), false, false, true);
+ }
+
+ void INotifyCreated.Created(Actor self)
+ {
+ attack = self.Trait();
+ }
+
+ void INotifyBurstComplete.FiredBurst(Actor self, Target target, Armament a)
+ {
+ self.World.IssueOrder(new Order("Stop", self, false));
+ }
+ }
+
+ public class SelectAttackPowerTarget : IOrderGenerator
+ {
+ readonly SupportPowerManager manager;
+ readonly SupportPowerInstance instance;
+ readonly string order;
+ readonly string cursor;
+ readonly string cursorBlocked;
+ readonly MouseButton expectedButton;
+ readonly AttackBase attack;
+
+ public SelectAttackPowerTarget(Actor self, string order, SupportPowerManager manager, string cursor, MouseButton button, AttackBase attack)
+ {
+ // Clear selection if using Left-Click Orders
+ if (Game.Settings.Game.UseClassicMouseStyle)
+ manager.Self.World.Selection.Clear();
+
+ instance = manager.GetPowersForActor(self).FirstOrDefault();
+ this.manager = manager;
+ this.order = order;
+ this.cursor = cursor;
+ expectedButton = button;
+ this.attack = attack;
+ cursorBlocked = cursor + "-blocked";
+ }
+
+ Actor GetFiringActor(World world, CPos cell)
+ {
+ var pos = world.Map.CenterOfCell(cell);
+ var range = attack.GetMaximumRange().LengthSquared;
+
+ return instance.Instances.Where(i => !i.Self.IsDisabled()).MinByOrDefault(a => (a.Self.CenterPosition - pos).HorizontalLengthSquared).Self;
+ }
+
+ bool IsValidTarget(World world, CPos cell)
+ {
+ var pos = world.Map.CenterOfCell(cell);
+ var range = attack.GetMaximumRange().LengthSquared;
+
+ return world.Map.Contains(cell) && instance.Instances.Any(a => !a.Self.IsDisabled() && (a.Self.CenterPosition - pos).HorizontalLengthSquared < range);
+ }
+
+ IEnumerable IOrderGenerator.Order(World world, CPos cell, int2 worldPixel, MouseInput mi)
+ {
+ world.CancelInputMode();
+ if (mi.Button == expectedButton && IsValidTarget(world, cell))
+ yield return new Order(order, manager.Self, false)
+ {
+ TargetActor = GetFiringActor(world, cell),
+ TargetLocation = cell,
+ SuppressVisualFeedback = true
+ };
+ }
+
+ void IOrderGenerator.Tick(World world)
+ {
+ // Cancel the OG if we can't use the power
+ if (!manager.Powers.ContainsKey(order))
+ world.CancelInputMode();
+ }
+
+ IEnumerable IOrderGenerator.Render(WorldRenderer wr, World world) { yield break; }
+
+ IEnumerable IOrderGenerator.RenderAfterWorld(WorldRenderer wr, World world)
+ {
+ foreach (var a in instance.Instances.Where(i => !i.Self.IsDisabled()))
+ {
+ yield return new RangeCircleRenderable(
+ a.Self.CenterPosition,
+ attack.GetMinimumRange(),
+ 0,
+ Color.Red,
+ Color.FromArgb(96, Color.Black));
+
+ yield return new RangeCircleRenderable(
+ a.Self.CenterPosition,
+ attack.GetMaximumRange(),
+ 0,
+ Color.Red,
+ Color.FromArgb(96, Color.Black));
+ }
+ }
+
+ string IOrderGenerator.GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi)
+ {
+ return IsValidTarget(world, cell) ? cursor : cursorBlocked;
+ }
+ }
+}
diff --git a/mods/ts/cursors.yaml b/mods/ts/cursors.yaml
index 31c884bf40..3bd577ab17 100644
--- a/mods/ts/cursors.yaml
+++ b/mods/ts/cursors.yaml
@@ -152,6 +152,12 @@ Cursors:
nuke:
Start: 319
Length: 9
+ emp:
+ Start: 357
+ Length: 19
+ emp-blocked:
+ Start: 377
+ Length: 1
sell:
Start: 129
Length: 10
diff --git a/mods/ts/rules/shared-support.yaml b/mods/ts/rules/shared-support.yaml
index a11b01cb61..a15036d5aa 100644
--- a/mods/ts/rules/shared-support.yaml
+++ b/mods/ts/rules/shared-support.yaml
@@ -3,8 +3,8 @@ NAPULS:
Valued:
Cost: 1000
Tooltip:
- Name: EMP Cannon
- Description: Disables vehicles.\nRequires power to operate.\n Strong vs Vehicles\n Weak vs Infantry, Aircraft
+ Name: E.M. Pulse Cannon
+ Description: Disables mechanical units in an area.\nRequires power to operate.
Buildable:
Queue: Defense
BuildPaletteOrder: 90
@@ -22,12 +22,15 @@ NAPULS:
Type: Heavy
RevealsShroud:
Range: 8c0
+ -AutoTarget:
Turreted:
TurnSpeed: 10
InitialFacing: 224
AttackTurreted:
Armament:
Weapon: EMPulseCannon
+ LocalOffset: 150,0,1250
+ LocalYaw: 0,100
WithSpriteTurret:
Sequence: turret
Power:
@@ -39,3 +42,31 @@ NAPULS:
FactionImages:
gdi: napuls.gdi
nod: napuls.nod
+ ProvidesPrerequisite@gdi:
+ Prerequisite: emp.gdi
+ Factions: gdi
+ ResetOnOwnerChange: true
+ ProvidesPrerequisite@nod:
+ Prerequisite: emp.nod
+ Factions: nod
+ ResetOnOwnerChange: true
+ AttackOrderPower@nod:
+ Cursor: emp
+ Icon: emp
+ ChargeTime: 135
+ Description: E.M. Pulse
+ LongDesc: Fires a pulse blast which disables\nall mechanical units in the area.
+ EndChargeSound: speech-nod|00-i158.aud
+ SelectTargetSound: speech-nod|00-i042.aud
+ Prerequisites: emp.nod
+ OrderName: Nodemp
+ AttackOrderPower@gdi:
+ Cursor: emp
+ Icon: emp
+ ChargeTime: 135
+ Description: E.M. Pulse
+ LongDesc: Fires a pulse blast which disables\nall mechanical units in the area.
+ EndChargeSound: speech-gdi|00-i158.aud
+ SelectTargetSound: speech-gdi|00-i042.aud
+ Prerequisites: emp.gdi
+ OrderName: GDIemp
diff --git a/mods/ts/sequences/misc.yaml b/mods/ts/sequences/misc.yaml
index 9323b27762..324d14058c 100644
--- a/mods/ts/sequences/misc.yaml
+++ b/mods/ts/sequences/misc.yaml
@@ -368,6 +368,7 @@ icon:
clustermissile: mltiicon
ioncannon: ioncicon
hunterseeker: detnicon
+ emp: pulsicon
clustermissile:
up: null # TODO
diff --git a/mods/ts/weapons/superweapons.yaml b/mods/ts/weapons/superweapons.yaml
index cd12202d69..cbbbb7edb0 100644
--- a/mods/ts/weapons/superweapons.yaml
+++ b/mods/ts/weapons/superweapons.yaml
@@ -79,7 +79,7 @@ IonCannon:
EMPulseCannon:
ReloadDelay: 100
- Range: 10c0
+ Range: 40c0
Report: plsecan2.aud
Projectile: Bullet
Speed: 425
@@ -90,7 +90,7 @@ EMPulseCannon:
Warhead@1Eff: CreateEffect
Explosions: pulse_explosion
Warhead@emp: GrantUpgrade
- Range: 3c0
+ Range: 4c0
Duration: 250
Upgrades: empdisable, notmobile