diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj
index 1759394327..a22e85d1b4 100644
--- a/OpenRa.Game/OpenRa.Game.csproj
+++ b/OpenRa.Game/OpenRa.Game.csproj
@@ -129,6 +129,7 @@
+
diff --git a/OpenRa.Game/Traits/Activities/Harvest.cs b/OpenRa.Game/Traits/Activities/Harvest.cs
new file mode 100644
index 0000000000..5e9c05ada6
--- /dev/null
+++ b/OpenRa.Game/Traits/Activities/Harvest.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenRa.Game.Traits.Activities
+{
+ class Harvest : Activity
+ {
+ public Activity NextActivity { get; set; }
+
+ public void Tick(Actor self, Mobile mobile)
+ {
+ var harv = self.traits.Get();
+ var isGem = false;
+
+ if (!harv.IsFull &&
+ Game.map.ContainsResource(self.Location) &&
+ Game.map.Harvest(self.Location, out isGem))
+ {
+ harv.AcceptResource(isGem);
+ return;
+ }
+
+ /* nothing to do here, either:
+ * - return to base, if full, or
+ * - seek out new ore, schedule a move there, and then this activity */
+
+ if (harv.IsFull)
+ PlanReturnToBase(self, mobile);
+ else
+ PlanMoreHarvesting(self, mobile);
+ }
+
+ /* maybe this doesnt really belong here, since it's the
+ * same as what UnitOrders has to do for an explicit return */
+
+ void PlanReturnToBase(Actor self, Mobile mobile)
+ {
+ /* find a proc */
+ var proc = Game.world.Actors.Where(
+ a => a.Owner == self.Owner &&
+ a.traits.Contains())
+ .FirstOrDefault(); /* todo: *closest* proc, maybe? */
+
+ if (proc == null)
+ {
+ Cancel(self, mobile); /* is this a sane way to cancel? */
+ return;
+ }
+
+ mobile.QueueActivity(new Move(proc.Location + new int2(1, 2)));
+ mobile.QueueActivity(new Turn(64));
+ /* todo: DeliverOre activity */
+
+ mobile.InternalSetActivity(NextActivity);
+ }
+
+ void PlanMoreHarvesting(Actor self, Mobile mobile)
+ {
+ /* find a nearby patch */
+ /* todo: add the queries we need to support this! */
+
+ mobile.InternalSetActivity(NextActivity);
+ }
+
+ public void Cancel(Actor self, Mobile mobile)
+ {
+ mobile.InternalSetActivity(null); /* bob: anything else required? */
+ }
+ }
+}
diff --git a/OpenRa.Game/Traits/Harvester.cs b/OpenRa.Game/Traits/Harvester.cs
index 4e9ad43f95..a9a2ca89f5 100644
--- a/OpenRa.Game/Traits/Harvester.cs
+++ b/OpenRa.Game/Traits/Harvester.cs
@@ -11,8 +11,14 @@ namespace OpenRa.Game.Traits
int oreCarried = 0; /* sum of these must not exceed capacity */
int gemsCarried = 0;
- bool IsFull { get { return oreCarried + gemsCarried == capacity; } }
- bool IsEmpty { get { return oreCarried == 0 && gemsCarried == 0; } }
+ public bool IsFull { get { return oreCarried + gemsCarried == capacity; } }
+ public bool IsEmpty { get { return oreCarried == 0 && gemsCarried == 0; } }
+
+ public void AcceptResource(bool isGem)
+ {
+ if (isGem) gemsCarried++;
+ else oreCarried++;
+ }
public Order Order(Actor self, int2 xy, bool lmb, Actor underCursor)
{
diff --git a/OpenRa.Game/UnitOrders.cs b/OpenRa.Game/UnitOrders.cs
index 7c77aa7fcf..dd2fb73eec 100755
--- a/OpenRa.Game/UnitOrders.cs
+++ b/OpenRa.Game/UnitOrders.cs
@@ -63,6 +63,14 @@ namespace OpenRa.Game
/* todo: actual deliver activity! [animation + add cash] */
break;
}
+ case "Harvest":
+ {
+ var mobile = order.Subject.traits.Get();
+ mobile.Cancel(order.Subject);
+ mobile.QueueActivity(new Traits.Activities.Move(order.TargetLocation));
+ mobile.QueueActivity(new Traits.Activities.Harvest() );
+ break;
+ }
case "PlaceBuilding":
{
Game.world.AddFrameEndTask( _ =>