diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj
index da4ca11ed3..bee3b6d805 100644
--- a/OpenRa.Game/OpenRa.Game.csproj
+++ b/OpenRa.Game/OpenRa.Game.csproj
@@ -114,6 +114,7 @@
+
@@ -175,6 +176,7 @@
+
diff --git a/OpenRa.Game/Traits/Activities/HeliAttack.cs b/OpenRa.Game/Traits/Activities/HeliAttack.cs
new file mode 100644
index 0000000000..28eeab1210
--- /dev/null
+++ b/OpenRa.Game/Traits/Activities/HeliAttack.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenRa.Game.Traits.Activities
+{
+ class HeliAttack : IActivity
+ {
+ Actor target;
+ const int CruiseAltitude = 20;
+ public HeliAttack( Actor target ) { this.target = target; }
+
+ public IActivity NextActivity { get; set; }
+
+ public IActivity Tick(Actor self)
+ {
+ if (target == null || target.IsDead)
+ return NextActivity;
+
+ var limitedAmmo = self.traits.GetOrDefault();
+ if (limitedAmmo != null && !limitedAmmo.HasAmmo())
+ return NextActivity;
+
+ var unit = self.traits.Get();
+
+ if (unit.Altitude != CruiseAltitude)
+ {
+ unit.Altitude += Math.Sign(CruiseAltitude - unit.Altitude);
+ return this;
+ }
+
+ var range = Rules.WeaponInfo[ self.Info.Primary ].Range;
+ var dist = target.CenterLocation - self.CenterLocation;
+
+ var desiredFacing = Util.GetFacing(dist, unit.Facing);
+ Util.TickFacing(ref unit.Facing, desiredFacing, self.Info.ROT);
+
+ if (!float2.WithinEpsilon(float2.Zero, dist, range * Game.CellSize))
+ {
+ var rawSpeed = .2f * Util.GetEffectiveSpeed(self);
+ self.CenterLocation += (rawSpeed / dist.Length) * dist;
+ self.Location = ((1 / 24f) * self.CenterLocation).ToInt2();
+ }
+
+ /* todo: maintain seperation wrt other helis */
+ return this;
+ }
+
+ public void Cancel(Actor self) { target = null; NextActivity = null; }
+ }
+}
diff --git a/OpenRa.Game/Traits/AttackHeli.cs b/OpenRa.Game/Traits/AttackHeli.cs
new file mode 100644
index 0000000000..093870a0dd
--- /dev/null
+++ b/OpenRa.Game/Traits/AttackHeli.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using OpenRa.Game.Traits.Activities;
+
+namespace OpenRa.Game.Traits
+{
+ class AttackHeli : AttackBase
+ {
+ public AttackHeli(Actor self) : base(self) { }
+
+ const int facingTolerance = 20;
+ public override void Tick(Actor self)
+ {
+ base.Tick(self);
+
+ if (target == null) return;
+
+ var unit = self.traits.Get();
+ var facingToTarget = Util.GetFacing(target.CenterLocation - self.CenterLocation, unit.Facing);
+
+ if (Math.Abs(facingToTarget - unit.Facing) % 256 < facingTolerance)
+ DoAttack(self);
+ }
+
+ protected override void QueueAttack(Actor self, Order order)
+ {
+ self.CancelActivity();
+ self.QueueActivity(new HeliAttack(order.TargetActor));
+ target = order.TargetActor;
+ // todo: fly home
+ }
+ }
+}
diff --git a/units.ini b/units.ini
index 0457488a27..5382f0eec9 100755
--- a/units.ini
+++ b/units.ini
@@ -185,14 +185,14 @@ LongDesc=Fast Infantry Transport Helicopter.\n Unarmed
[HELI]
Description=Longbow
BuiltAt=hpad
-Traits=Unit, Helicopter, RenderUnitRotor, WithShadow, LimitedAmmo
+Traits=Unit, AttackHeli, Helicopter, RenderUnitRotor, WithShadow, LimitedAmmo
PrimaryOffset=0,0,0,-2
InitialFacing=20
LongDesc=Helicopter Gunship with AG Missiles.\n Strong vs Buildings, Tanks\n Weak vs Infantry
[HIND]
Description=Hind
BuiltAt=hpad
-Traits=Unit, Helicopter, RenderUnitRotor, WithShadow, LimitedAmmo
+Traits=Unit, AttackHeli, Helicopter, RenderUnitRotor, WithShadow, LimitedAmmo
InitialFacing=20
LongDesc=Helicopter Gunship with Chainguns.\n Strong vs Infantry, Light Vehicles.\n Weak vs Tanks