From 09f642805e4d0b63eff89a397bebe2ac057cbf4b Mon Sep 17 00:00:00 2001
From: joris <joris.dauphin@gmail.com>
Date: Mon, 23 Jan 2012 11:59:13 +0100
Subject: [PATCH] Fix ActionRessource and DeassignWorkerFromMine

---
 src/action/action_resource.cpp    | 314 +++++++++++++++++-------------
 src/action/action_returngoods.cpp |   2 +-
 src/action/actions.cpp            |  13 +-
 src/ai/ai_resource.cpp            | 128 +++++++-----
 src/include/unit.h                |   2 +-
 src/unit/script_unit.cpp          |  16 +-
 src/unit/unit.cpp                 |  51 +++--
 src/unit/unit_save.cpp            |  17 +-
 8 files changed, 318 insertions(+), 225 deletions(-)

diff --git a/src/action/action_resource.cpp b/src/action/action_resource.cpp
index d58ba87b5..c0d7ac58f 100644
--- a/src/action/action_resource.cpp
+++ b/src/action/action_resource.cpp
@@ -66,67 +66,87 @@
 --  Functions
 ----------------------------------------------------------------------------*/
 
+/**
+**  Move unit to terrain.
+**
+**  @return      1 if reached, -1 if unreacheable, 0 if on the way.
+*/
+static int MoveToResource_Terrain(CUnit &unit)
+{
+	Vec2i pos = unit.CurrentOrder()->goalPos;
+
+	// Wood gone, look somewhere else.
+	if ((!Map.ForestOnMap(pos)) && (!unit.IX) && (!unit.IY)) {
+		if (!FindTerrainType(unit.Type->MovementMask, MapFieldForest, 0, 16,
+				unit.Player, unit.CurrentOrder()->goalPos, &pos)) {
+			// no wood in range
+			return -1;
+		} else {
+			unit.CurrentOrder()->goalPos = pos;
+			NewResetPath(unit);
+		}
+	}
+	switch (DoActionMove(unit)) {
+		case PF_UNREACHABLE:
+			unit.Wait = 10;
+			if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 0, 9999,
+					unit.Player, unit.tilePos, &pos)) {
+				unit.CurrentOrder()->goalPos = pos;
+				NewResetPath(unit);
+				DebugPrint("Found a better place to harvest %d,%d\n" _C_ pos.x _C_ pos.y);
+				// FIXME: can't this overflow? It really shouldn't, since
+				// x and y are really supossed to be reachable, checked thorugh a flood fill.
+				// I don't know, sometimes stuff happens.
+				return 0;
+			}
+			return -1;
+		case PF_REACHED:
+			return 1;
+		default:
+			return 0;
+	}
+}
+
+/**
+**  Move unit to unit resource.
+**
+**  @return      1 if reached, -1 if unreacheable, 0 if on the way.
+*/
+static int MoveToResource_Unit(CUnit &unit)
+{
+	const CUnit *goal = unit.CurrentOrder()->GetGoal();
+	Assert(goal);
+
+	switch (DoActionMove(unit)) { // reached end-point?
+		case PF_UNREACHABLE:
+			return -1;
+		case PF_REACHED:
+			break;
+		default:
+			// Goal gone or something.
+			if (unit.Anim.Unbreakable || goal->IsVisibleAsGoal(*unit.Player)) {
+				return 0;
+			}
+			break;
+	}
+	return 1;
+}
+
 /**
 **  Move unit to resource.
 **
 **  @param unit  Pointer to unit.
 **
-**  @return      TRUE if reached, otherwise FALSE.
+**  @return      1 if reached, -1 if unreacheable, 0 if on the way.
 */
 static int MoveToResource(CUnit &unit)
 {
 	const ResourceInfo &resinfo = *unit.Type->ResInfo[unit.CurrentResource];
 
 	if (resinfo.TerrainHarvester) {
-		Vec2i pos = unit.CurrentOrder()->goalPos;
-
-		// Wood gone, look somewhere else.
-		if ((!Map.ForestOnMap(pos)) && (!unit.IX) && (!unit.IY)) {
-			if (!FindTerrainType(unit.Type->MovementMask, MapFieldForest, 0, 16,
-					unit.Player, unit.CurrentOrder()->goalPos, &pos)) {
-				// no wood in range
-				return -1;
-			} else {
-				unit.CurrentOrder()->goalPos = pos;
-				NewResetPath(unit);
-			}
-		}
-		switch (DoActionMove(unit)) {
-			case PF_UNREACHABLE:
-				unit.Wait = 10;
-				if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 0, 9999,
-						unit.Player, unit.tilePos, &pos)) {
-					unit.CurrentOrder()->goalPos = pos;
-					NewResetPath(unit);
-					DebugPrint("Found a better place to harvest %d,%d\n" _C_ pos.x _C_ pos.y);
-					// FIXME: can't this overflow? It really shouldn't, since
-					// x and y are really supossed to be reachable, checked thorugh a flood fill.
-					// I don't know, sometimes stuff happens.
-					return 0;
-				}
-				return -1;
-			case PF_REACHED:
-				return 1;
-			default:
-				return 0;
-		}
+		return MoveToResource_Terrain(unit);
 	} else {
-		CUnit *goal = unit.CurrentOrder()->GetGoal();
-		Assert(goal);
-		switch (DoActionMove(unit)) { // reached end-point?
-			case PF_UNREACHABLE:
-				return -1;
-			case PF_REACHED:
-				break;
-			default:
-				// Goal gone or something.
-				if (unit.Anim.Unbreakable ||
-						goal->IsVisibleAsGoal(*unit.Player)) {
-					return 0;
-				}
-				break;
-		}
-		return 1;
+		return MoveToResource_Unit(unit);
 	}
 }
 
@@ -324,6 +344,8 @@ static void LoseResource(CUnit &unit, const CUnit &source)
 	}
 }
 
+
+
 /**
 **  Gather the resource
 **
@@ -441,7 +463,6 @@ static int GatherResource(CUnit &unit)
 						LoseResource(*uins, *source);
 					}
 				}
-
 				if (source->Data.Resource.Workers) {
 					CUnit *next, *worker = source->Data.Resource.Workers;
 					for (; NULL != worker; worker = next)
@@ -462,7 +483,6 @@ static int GatherResource(CUnit &unit)
 					//Assert(source->Data.Resource.Assigned == 0);
 					source->Data.Resource.Workers = NULL;
 				}
-
 				// Don't destroy the resource twice.
 				// This only happens when it's empty.
 				if (!dead) {
@@ -517,7 +537,6 @@ int GetNumWaitingWorkers(const CUnit &mine)
 */
 static int StopGathering(CUnit &unit)
 {
-	CUnit *depot;
 	CUnit *source = 0;
 	const ResourceInfo &resinfo = *unit.Type->ResInfo[unit.CurrentResource];
 
@@ -531,12 +550,15 @@ static int StopGathering(CUnit &unit)
 		source->Data.Resource.Active--;
 		Assert(source->Data.Resource.Active >= 0);
 
-		if (!resinfo.HarvestFromOutside && source->Data.Resource.Active == 0)
+		if (!resinfo.HarvestFromOutside && source->Data.Resource.Active == 0) {
 			source->SubAction = 1;
-
+		}
 		//Store resource position.
-		//source->RefsIncrease();
-		//unit.Orders[0]->Arg1.Resource.Mine = source;
+		if (unit.Orders[0]->Arg1.Resource.Mine) {
+			unit.Orders[0]->Arg1.Resource.Mine->RefsDecrease();
+		}
+		source->RefsIncrease();
+		unit.Orders[0]->Arg1.Resource.Mine = source;
 
 		if (source->Type->MaxOnBoard) {
 			int count = 0;
@@ -544,8 +566,7 @@ static int StopGathering(CUnit &unit)
 			CUnit *next = NULL;
 			for(; NULL != worker; worker = worker->NextWorker)
 			{
-				if (worker != &unit &&
-					worker->SubAction == SUB_START_GATHERING && worker->Wait) {
+				if (worker != &unit && worker->SubAction == SUB_START_GATHERING && worker->Wait) {
 					count++;
 					if (next) {
 						if (next->Wait > worker->Wait)
@@ -572,6 +593,7 @@ static int StopGathering(CUnit &unit)
 	} else {
 		// Store resource position.
 		unit.CurrentOrder()->Arg1.Resource.Pos = unit.tilePos;
+		Assert(unit.CurrentOrder()->Arg1.Resource.Mine == NULL);
 	}
 
 #ifdef DEBUG
@@ -581,17 +603,24 @@ static int StopGathering(CUnit &unit)
 #endif
 
 	// Find and send to resource deposit.
-	if (!(depot = FindDeposit(unit, 1000, unit.CurrentResource))
-		|| !unit.ResourcesHeld) {
+	CUnit *depot = FindDeposit(unit, 1000, unit.CurrentResource);
+	if (!depot || !unit.ResourcesHeld) {
 		if (!(resinfo.HarvestFromOutside || resinfo.TerrainHarvester)) {
 			Assert(unit.Container);
 			DropOutOnSide(unit, LookingW, source);
 		}
+		CUnit *mine = unit.Orders[0]->Arg1.Resource.Mine;
+
+		if (mine) {
+			unit.DeAssignWorkerFromMine(*mine);
+			mine->RefsDecrease();
+			unit.Orders[0]->Arg1.Resource.Mine = NULL;
+		}
+
 		DebugPrint("%d: Worker %d report: Can't find a resource [%d] deposit.\n"
 				_C_ unit.Player->Index _C_ unit.Slot _C_ unit.CurrentResource);
 		unit.CurrentOrder()->ClearGoal();
 		unit.ClearAction();
-		// should return 0, done below!
 		return 0;
 	} else {
 		if (!(resinfo.HarvestFromOutside || resinfo.TerrainHarvester)) {
@@ -638,8 +667,7 @@ static int MoveToDepot(CUnit &unit)
 	// Target is dead, stop getting resources.
 	//
 	if (!goal->IsVisibleAsGoal(*unit.Player)) {
-		DebugPrint("%d: Worker %d report: Destroyed depot\n"
-			_C_ unit.Player->Index _C_ unit.Slot);
+		DebugPrint("%d: Worker %d report: Destroyed depot\n" _C_ unit.Player->Index _C_ unit.Slot);
 
 		unit.CurrentOrder()->ClearGoal();
 
@@ -647,8 +675,7 @@ static int MoveToDepot(CUnit &unit)
 
 		if (depot) {
 			UnitGotoGoal(unit, depot, SUB_MOVE_TO_DEPOT);
-			DebugPrint("%d: Worker %d report: Going to new deposit.\n"
-				_C_ unit.Player->Index _C_ unit.Slot);
+			DebugPrint("%d: Worker %d report: Going to new deposit.\n" _C_ unit.Player->Index _C_ unit.Slot);
 		} else {
 			DebugPrint("%d: Worker %d report: Can't find a new resource deposit.\n"
 				_C_ unit.Player->Index _C_ unit.Slot);
@@ -666,7 +693,6 @@ static int MoveToDepot(CUnit &unit)
 	}
 
 	// If resource depot is still under construction, wait!
-	//
 	if (goal->CurrentOrder()->Action == UnitActionBuilt) {
 		unit.Wait = 10;
 		return 0;
@@ -675,17 +701,13 @@ static int MoveToDepot(CUnit &unit)
 	unit.CurrentOrder()->ClearGoal();
 	unit.Wait = resinfo.WaitAtDepot;
 
-	//
 	// Place unit inside the depot
-	//
 	if (unit.Wait) {
 		unit.Remove(goal);
 		unit.Anim.CurrAnim = NULL;
 	}
 
-	//
 	// Update resource.
-	//
 	unit.Player->Resources[resinfo.FinalResource] += (unit.ResourcesHeld * unit.Player->Incomes[resinfo.FinalResource]) / 100;
 	unit.Player->TotalResources[resinfo.FinalResource] += (unit.ResourcesHeld * unit.Player->Incomes[resinfo.FinalResource]) / 100;
 	unit.ResourcesHeld = 0;
@@ -708,7 +730,6 @@ static int MoveToDepot(CUnit &unit)
 */
 static int WaitInDepot(CUnit &unit)
 {
-	Vec2i pos;
 	const ResourceInfo &resinfo = *unit.Type->ResInfo[unit.CurrentResource];
 	const CUnit *depot = ResourceDepositOnMap(unit.tilePos, resinfo.ResourceId);
 
@@ -716,7 +737,7 @@ static int WaitInDepot(CUnit &unit)
 
 	// Range hardcoded. don't stray too far though
 	if (resinfo.TerrainHarvester) {
-		pos = unit.CurrentOrder()->Arg1.Resource.Pos;
+		Vec2i pos = unit.CurrentOrder()->Arg1.Resource.Pos;
 
 		if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 0, 10, unit.Player, pos, &pos)) {
 			if (depot) {
@@ -730,12 +751,11 @@ static int WaitInDepot(CUnit &unit)
 			unit.ClearAction();
 		}
 	} else {
-		CUnit *goal;
 		CUnit *mine = unit.CurrentOrder()->Arg1.Resource.Mine;
 		const int range = (mine ? 15 : 1000);
-		pos = mine ? mine->tilePos : unit.tilePos;
+		const Vec2i pos = mine ? mine->tilePos : unit.tilePos;
+		CUnit *goal = UnitFindResource(unit, pos, range, unit.CurrentResource, unit.Player->AiEnabled, depot);
 
-		goal = UnitFindResource(unit, pos, range, unit.CurrentResource, unit.Player->AiEnabled, depot);
 		if (goal) {
 			if (depot) {
 				DropOutNearest(unit, goal->tilePos + goal->Type->GetHalfTileSize(), depot);
@@ -750,7 +770,6 @@ static int WaitInDepot(CUnit &unit)
 				goal->RefsIncrease();
 				unit.CurrentOrder()->Arg1.Resource.Mine = goal;
 			}
-
 			unit.CurrentOrder()->SetGoal(goal);
 			unit.CurrentOrder()->Range = 1;
 			unit.CurrentOrder()->goalPos.x = unit.CurrentOrder()->goalPos.y = -1;
@@ -812,6 +831,53 @@ void ResourceGiveUp(CUnit &unit)
 	unit.ClearAction();
 }
 
+/**
+**  Initialize
+**
+**  return false if action is canceled, true otherwise.
+*/
+static bool ActionResourceInit(CUnit &unit)
+{
+	Assert(unit.SubAction == SUB_START_RESOURCE);
+
+	CUnit *const goal = unit.CurrentOrder()->GetGoal();
+	int newres;
+
+	if (goal) {
+		newres = goal->Type->GivesResource;
+	} else {
+		//FIXME: hardcoded wood
+		newres = WoodCost;
+	}
+	if (newres != unit.CurrentResource) {
+		DropResource(unit);
+	}
+	unit.CurrentResource = newres;
+	if (newres == 0) {
+		unit.ResourcesHeld = 0;
+		ResourceGiveUp(unit);
+		return false;
+	}
+	COrderPtr order = unit.CurrentOrder();
+	order->CurrentResource = newres;
+	if (goal && order->Arg1.Resource.Mine != goal) {
+		CUnit *mine = order->Arg1.Resource.Mine;
+
+		if (mine) {
+			unit.DeAssignWorkerFromMine(*mine);
+			mine->RefsDecrease();
+			order->Arg1.Resource.Mine = NULL;
+		}
+		if (goal->CurrentAction() != UnitActionBuilt) {
+			unit.AssignWorkerToMine(*goal);
+			goal->RefsIncrease();
+			order->Arg1.Resource.Mine = goal;
+		}
+	}
+	UnitGotoGoal(unit, goal, SUB_MOVE_TO_RESOURCE);
+	return true;
+}
+
 /**
 **  Control the unit action: getting a resource.
 **
@@ -821,9 +887,6 @@ void ResourceGiveUp(CUnit &unit)
 */
 void HandleActionResource(CUnit &unit)
 {
-	int ret;
-	int newres;
-
 	if (unit.Wait) {
 		// FIXME: show idle animation while we wait?
 		unit.Wait--;
@@ -832,61 +895,35 @@ void HandleActionResource(CUnit &unit)
 
 	// Let's start mining.
 	if (unit.SubAction == SUB_START_RESOURCE) {
-		CUnit *const goal = unit.CurrentOrder()->GetGoal();
-		if (goal) {
-			newres = goal->Type->GivesResource;
-		} else {
-			//FIXME: hardcoded wood
-			newres = WoodCost;
-		}
-		if (newres != unit.CurrentResource) {
-			DropResource(unit);
-		}
-		if ((unit.CurrentResource = newres))
-		{
-			COrderPtr order = unit.CurrentOrder();
-			order->CurrentResource = newres;
-			if (goal && order->Arg1.Resource.Mine != goal) {
-				CUnit *mine = order->Arg1.Resource.Mine;
-				if (mine) {
-					unit.DeAssignWorkerFromMine(*mine);
-					mine->RefsDecrease();
-					order->Arg1.Resource.Mine = NULL;
-				}
-				if (goal->CurrentAction() != UnitActionBuilt) {
-					unit.AssignWorkerToMine(*goal);
-					goal->RefsIncrease();
-					order->Arg1.Resource.Mine = goal;
-				}
-			}
-			UnitGotoGoal(unit, goal, SUB_MOVE_TO_RESOURCE);
-			//NewResetPath(unit);
-			//unit.SubAction = SUB_MOVE_TO_RESOURCE;
-			//unit.Data.Move.Cycles = 0;
-		} else {
-			unit.ResourcesHeld = 0;
-			ResourceGiveUp(unit);
+		if (ActionResourceInit(unit) == false) {
 			return;
 		}
 	}
 
 	// Move to the resource location.
-	if (unit.SubAction >= SUB_MOVE_TO_RESOURCE
-		&& unit.SubAction < SUB_UNREACHABLE_RESOURCE) {
-		// -1 failure, 0 not yet reached, 1 reached
-		if ((ret = MoveToResource(unit))) {
-			if (ret == -1) {
-				// Can't Reach
+	if (SUB_MOVE_TO_RESOURCE <= unit.SubAction && unit.SubAction < SUB_UNREACHABLE_RESOURCE) {
+		const int ret = MoveToResource(unit);
+
+		switch (ret)
+		{
+			case -1: // Can't Reach
+			{
 				unit.SubAction++;
 				unit.Wait = 5;
 				return;
-			} else {
-				// Reached
-				unit.SubAction = SUB_START_GATHERING;
 			}
-		} else {
-			// Move along.
-			return;
+			case 1: // Reached
+			{
+				unit.SubAction = SUB_START_GATHERING;
+				break;
+			}
+			case 0: // Move along.
+				return;
+			default:
+			{
+				Assert(0);
+				break;
+			}
 		}
 	}
 
@@ -924,19 +961,30 @@ void HandleActionResource(CUnit &unit)
 	}
 
 	// Move back home.
-	if (unit.SubAction >= SUB_MOVE_TO_DEPOT
-		&& unit.SubAction < SUB_UNREACHABLE_DEPOT) {
-		// -1 failure, 0 not yet reached, 1 reached
-		if ((ret = MoveToDepot(unit))) {
-			if (ret == -1) {
-				// Can't Reach
+	if (SUB_MOVE_TO_DEPOT <= unit.SubAction && unit.SubAction < SUB_UNREACHABLE_DEPOT) {
+		const int ret = MoveToDepot(unit);
+
+		switch (ret)
+		{
+			case -1: // Can't Reach
+			{
 				unit.SubAction++;
 				unit.Wait = 5;
-			} else {
+				return;
+			}
+			case 1: // Reached
+			{
 				unit.SubAction = SUB_RETURN_RESOURCE;
+				return;
+			}
+			case 0: // Move along.
+				return;
+			default:
+			{
+				Assert(0);
+				return;
 			}
 		}
-		return;
 	}
 
 	// Depot seems to be unreachable
diff --git a/src/action/action_returngoods.cpp b/src/action/action_returngoods.cpp
index 5e85ec33d..cffcb3050 100644
--- a/src/action/action_returngoods.cpp
+++ b/src/action/action_returngoods.cpp
@@ -88,7 +88,7 @@ void HandleActionReturnGoods(CUnit &unit)
 	//unit.CurrentOrder()->Arg1.ResourcePos = -1;
 
 	NewResetPath(unit);
-	unit.SubAction = 70; // FIXME : Define value.
+	unit.SubAction = /* SUB_MOVE_TO_DEPOT */ 70; // FIXME : Define value.
 }
 
 //@}
diff --git a/src/action/actions.cpp b/src/action/actions.cpp
index cf6b1cac0..8083e43d9 100644
--- a/src/action/actions.cpp
+++ b/src/action/actions.cpp
@@ -503,12 +503,21 @@ static void HandleUnitAction(CUnit &unit)
 			//
 			// Release pending references.
 			//
+			if (order->Action == UnitActionResource) {
+				CUnit *mine = order->Arg1.Resource.Mine;
+
+				if (mine) {
+					unit.DeAssignWorkerFromMine(*mine);
+					mine->RefsDecrease();
+					order->Arg1.Resource.Mine = NULL;
+
+				}
+			}
 			if (order->HasGoal()) {
 				CUnit *goal = order->GetGoal();
 				// If mining decrease the active count on the resource.
 				if (order->Action == UnitActionResource) {
-					if(unit.SubAction == 60) {
-						// FIXME: SUB_GATHER_RESOURCE ?
+					if (unit.SubAction == 60 /* SUB_GATHER_RESOURCE */ ) {
 						goal->Data.Resource.Active--;
 						Assert(goal->Data.Resource.Active >= 0);
 					}
diff --git a/src/ai/ai_resource.cpp b/src/ai/ai_resource.cpp
index d69b1e303..ad8ccf57a 100644
--- a/src/ai/ai_resource.cpp
+++ b/src/ai/ai_resource.cpp
@@ -844,6 +844,75 @@ static void AiCheckingWork()
 --  WORKERS/RESOURCES
 ----------------------------------------------------------------------------*/
 
+/**
+**  Assign worker to gather a certain resource from terrain.
+**
+**  @param unit      pointer to the unit.
+**  @param resource  resource identification.
+**
+**  @return          1 if the worker was assigned, 0 otherwise.
+*/
+static int AiAssignHarvesterFromTerrain(CUnit &unit, int resource)
+{
+	// TODO : hardcoded forest
+	Vec2i forestPos;
+
+	// Code for terrain harvesters. Search for piece of terrain to mine.
+	if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 0, 1000, unit.Player, unit.tilePos, &forestPos)) {
+		CommandResourceLoc(unit, forestPos, FlushCommands);
+		return 1;
+	}
+	// Ask the AI to explore...
+	AiExplore(unit.tilePos, MapFieldLandUnit);
+
+	// Failed.
+	return 0;
+}
+
+/**
+**  Assign worker to gather a certain resource from Unit.
+**
+**  @param unit      pointer to the unit.
+**  @param resource  resource identification.
+**
+**  @return          1 if the worker was assigned, 0 otherwise.
+*/
+static int AiAssignHarvesterFromUnit(CUnit &unit, int resource)
+{
+	// Find a resource to harvest from.
+	CUnit *mine = UnitFindResource(unit, unit.tilePos, 1000, resource, true);
+
+	if (mine) {
+		CommandResource(unit, *mine, FlushCommands);
+		return 1;
+	}
+
+	int exploremask = 0;
+
+	for (size_t i = 0; i != UnitTypes.size(); ++i) {
+		const CUnitType* type = UnitTypes[i];
+
+		if (type && type->GivesResource == resource) {
+			switch (type->UnitType) {
+			case UnitTypeLand:
+				exploremask |= MapFieldLandUnit;
+				break;
+			case UnitTypeFly:
+				exploremask |= MapFieldAirUnit;
+				break;
+			case UnitTypeNaval:
+				exploremask |= MapFieldSeaUnit;
+				break;
+			default:
+				Assert(0);
+			}
+		}
+	}
+	// Ask the AI to explore
+	AiExplore(unit.tilePos, exploremask);
+	// Failed.
+	return 0;
+}
 /**
 **  Assign worker to gather a certain resource.
 **
@@ -854,68 +923,19 @@ static void AiCheckingWork()
 */
 static int AiAssignHarvester(CUnit &unit, int resource)
 {
-	ResourceInfo *resinfo;
-
 	// It can't.
 	if (unit.Removed) {
 		return 0;
 	}
 
-	resinfo = unit.Type->ResInfo[resource];
-	Assert(resinfo);
-	if (resinfo->TerrainHarvester) {
-		Vec2i forestPos;
+	const ResourceInfo &resinfo = *unit.Type->ResInfo[resource];
+	Assert(&resinfo);
 
-		//
-		// Code for terrain harvesters. Search for piece of terrain to mine.
-		//
-		if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 0, 1000,
-				unit.Player, unit.tilePos, &forestPos)) {
-			CommandResourceLoc(unit, forestPos, FlushCommands);
-			return 1;
-		}
-		// Ask the AI to explore...
-		AiExplore(unit.tilePos, MapFieldLandUnit);
+	if (resinfo.TerrainHarvester) {
+		return AiAssignHarvesterFromTerrain(unit, resource);
 	} else {
-		int exploremask = 0;
-		//
-		// Find a resource to harvest from.
-		//
-		CUnit *dest = UnitFindResource(unit, unit.tilePos, 1000, resource, true);
-
-		if (dest) {
-			//FIXME: rb - when workers can speedup building then such assign may be ok.
-			//if(dest->CurrentAction() == UnitActionBuilt)
-				//CommandBuildBuilding(unit, dest->tilePos, dest->Type, FlushCommands);
-			//else
-				CommandResource(unit, *dest, FlushCommands);
-			return 1;
-		}
-
-		for (std::vector<CUnitType *>::iterator i = UnitTypes.begin();
-			 i != UnitTypes.end(); i++) {
-			if (*i && (*i)->GivesResource == resource) {
-				switch ((*i)->UnitType) {
-				case UnitTypeLand:
-					exploremask |= MapFieldLandUnit;
-					break;
-				case UnitTypeFly:
-					exploremask |= MapFieldAirUnit;
-					break;
-				case UnitTypeNaval:
-					exploremask |= MapFieldSeaUnit;
-					break;
-				default:
-					Assert(0);
-				}
-			}
-		}
-		// Ask the AI to explore
-		AiExplore(unit.tilePos, exploremask);
+		return AiAssignHarvesterFromUnit(unit, resource);
 	}
-
-	// Failed.
-	return 0;
 }
 
 static int CmpWorkers(const void *w0,const void *w1) {
diff --git a/src/include/unit.h b/src/include/unit.h
index 088e3ab1a..b6dd61a70 100644
--- a/src/include/unit.h
+++ b/src/include/unit.h
@@ -566,7 +566,7 @@ public:
 
 		union {
 			Vec2i Patrol; /// position for patroling.
-			union {
+			struct {
 				Vec2i Pos; /// position for terrain resource.
 				CUnit *Mine;
 			} Resource;
diff --git a/src/unit/script_unit.cpp b/src/unit/script_unit.cpp
index 82a8dfb7b..a2fe22954 100644
--- a/src/unit/script_unit.cpp
+++ b/src/unit/script_unit.cpp
@@ -276,19 +276,19 @@ void CclParseOrder(lua_State *l, COrderPtr order)
 		} else if (!strcmp(value, "resource-pos")) {
 			++j;
 			lua_rawgeti(l, -1, j + 1);
-			CclGetPos(l, &order->Arg1.Resource.Pos.x ,
-								&order->Arg1.Resource.Pos.y);
+			order->Arg1.Resource.Mine = NULL;
+			CclGetPos(l, &order->Arg1.Resource.Pos.x , &order->Arg1.Resource.Pos.y);
 			lua_pop(l, 1);
 
-			//FIXME: hardcoded wood
-			Assert(order->CurrentResource && order->CurrentResource == WoodCost);
+			Assert(order->CurrentResource);
 		} else if (!strcmp(value, "resource-mine")) {
 			++j;
 			lua_rawgeti(l, -1, j + 1);
+			Vec2i invalidPos = {-1, -1};
+			order->Arg1.Resource.Pos = invalidPos;
 			order->Arg1.Resource.Mine = CclGetUnitFromRef(l);
 			lua_pop(l, 1);
-		} else if (!strcmp(value, "mine")) {
-			/* old save format */
+		} else if (!strcmp(value, "mine")) { /* old save format */
 			int pos;
 			++j;
 			lua_rawgeti(l, -1, j + 1);
@@ -303,11 +303,15 @@ void CclParseOrder(lua_State *l, COrderPtr order)
 				mine = ResourceOnMap(mpos, pos, true);
 			} while (!mine && pos < MaxCosts);
 			if (mine) {
+				Vec2i invalidPos = {-1, -1};
+				order->Arg1.Resource.Pos = invalidPos;
+
 				mine->RefsIncrease();
 				order->Arg1.Resource.Mine = mine;
 				order->CurrentResource = pos;
 			} else {
 				order->CurrentResource = WoodCost;
+				order->Arg1.Resource.Mine = NULL;
 				order->Arg1.Resource.Pos = mpos;
 			}
 		} else {
diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp
index cb80a6dfb..267b3139b 100644
--- a/src/unit/unit.cpp
+++ b/src/unit/unit.cpp
@@ -126,9 +126,7 @@ void CUnit::COrder::Release() {
 		Goal->RefsDecrease();
 		Goal = NoUnitP;
 	}
-	//FIXME: Hardcoded wood
-	if (Action == UnitActionResource && CurrentResource != WoodCost &&
-		Arg1.Resource.Mine ) {
+	if (Action == UnitActionResource && Arg1.Resource.Mine) {
 		Arg1.Resource.Mine->RefsDecrease();
 		Arg1.Resource.Mine = NoUnitP;
 	}
@@ -145,22 +143,13 @@ CUnit::COrder::COrder(const CUnit::COrder &ths): Goal(ths.Goal), Range(ths.Range
 
 	memcpy(&Arg1, &ths.Arg1, sizeof(Arg1));
 
-	//FIXME: Hardcoded wood
-	if (Action == UnitActionResource &&
-		 CurrentResource != WoodCost && Arg1.Resource.Mine) {
-		 Arg1.Resource.Mine->RefsIncrease();
+	if (Action == UnitActionResource && Arg1.Resource.Mine) {
+		Arg1.Resource.Mine->RefsIncrease();
 	}
 }
 
 CUnit::COrder& CUnit::COrder::operator=(const CUnit::COrder &rhs) {
 	if (this != &rhs) {
-
-		//FIXME: Hardcoded wood
-		if (Action == UnitActionResource &&
-			 CurrentResource != WoodCost && Arg1.Resource.Mine) {
-			 Arg1.Resource.Mine->RefsDecrease();
-		}
-
 		Action = rhs.Action;
 		Range = rhs.Range;
 		MinRange = rhs.MinRange;
@@ -172,8 +161,7 @@ CUnit::COrder& CUnit::COrder::operator=(const CUnit::COrder &rhs) {
 		memcpy(&Arg1, &rhs.Arg1, sizeof(Arg1));
 
 		//FIXME: Hardcoded wood
-		if (Action == UnitActionResource &&
-			 CurrentResource != WoodCost && Arg1.Resource.Mine) {
+		if (Action == UnitActionResource && Arg1.Resource.Mine) {
 			 Arg1.Resource.Mine->RefsIncrease();
 		}
 	}
@@ -1555,8 +1543,26 @@ void CUnit::ChangeOwner(CPlayer &newplayer)
 	UpdateForNewUnit(*this, 1);
 }
 
+#ifdef DEBUG
+
+static bool IsMineAssignedBy(const CUnit &mine, const CUnit &worker)
+{
+	for (CUnit* it = mine.Data.Resource.Workers; it; it = it->NextWorker) {
+		if (it == &worker) {
+			return true;
+		}
+	}
+	return false;
+}
+
+#endif
+
+
 void CUnit::AssignWorkerToMine(CUnit &mine)
 {
+	Assert(this->NextWorker == NULL);
+	Assert(IsMineAssignedBy(mine, *this) == false);
+
 	CUnit *head = mine.Data.Resource.Workers;
 /*
 	DebugPrint("%d: Worker [%d] is adding into %s [%d] on %d pos\n"
@@ -1573,6 +1579,7 @@ void CUnit::AssignWorkerToMine(CUnit &mine)
 
 void CUnit::DeAssignWorkerFromMine(CUnit &mine)
 {
+	Assert(IsMineAssignedBy(mine, *this) == true);
 	CUnit *prev = NULL, *worker = mine.Data.Resource.Workers;
 /*
 	DebugPrint("%d: Worker [%d] is removing from %s [%d] left %d units assigned\n"
@@ -1581,10 +1588,11 @@ void CUnit::DeAssignWorkerFromMine(CUnit &mine)
 					_C_ mine.Slot
 					_C_ mine.Data.Resource.Assigned);
 */
-	for(int i = 0; NULL != worker; worker = worker->NextWorker,++i)
+	for (int i = 0; NULL != worker; worker = worker->NextWorker, ++i)
 	{
 		if (worker == this) {
 			CUnit *next = worker->NextWorker;
+			worker->NextWorker = NULL;
 			if (prev) {
 				prev->NextWorker = next;
 			}
@@ -3357,9 +3365,12 @@ void CleanUnits()
 		int count = NumUnits;
 		do {
 			CUnit *unit = Units[count - 1];
+
+			if (unit == NULL) {
+				continue;
+			}
 			if (!unit->Destroyed) {
-				if (//unit->Type->Harvester &&
-					unit->CurrentAction() == UnitActionResource) {
+				if (unit->CurrentAction() == UnitActionResource) {
 					ResourceInfo *resinfo = unit->Type->ResInfo[unit->CurrentResource];
 					if (resinfo && !resinfo->TerrainHarvester) {
 						CUnit *mine = unit->CurrentOrder()->Arg1.Resource.Mine;
@@ -3371,7 +3382,7 @@ void CleanUnits()
 					}
 				}
 				unit->CurrentOrder()->ClearGoal();
-				if(!unit->Removed) {
+				if (!unit->Removed) {
 					unit->Remove(NULL);
 				}
 				UnitClearOrders(*unit);
diff --git a/src/unit/unit_save.cpp b/src/unit/unit_save.cpp
index 285afbacb..ba9cfc208 100644
--- a/src/unit/unit_save.cpp
+++ b/src/unit/unit_save.cpp
@@ -183,19 +183,20 @@ void SaveOrder(const COrderPtr order, CFile *file)
 		case UnitActionResource :
 		case UnitActionReturnGoods :
 			if (order->CurrentResource) {
-				file->printf(", \"current-resource\", \"%s\",",
-					DefaultResourceNames[order->CurrentResource].c_str());
-				if(order->CurrentResource == WoodCost) {
-					file->printf(" \"resource-pos\", {%d, %d}",
-						order->Arg1.Resource.Pos.x, order->Arg1.Resource.Pos.y);
+				file->printf(", \"current-resource\", \"%s\",", DefaultResourceNames[order->CurrentResource].c_str());
+				const CUnit *mine = order->Arg1.Resource.Mine;
+
+				if (mine == NULL) {
+					const Vec2i &pos = order->Arg1.Resource.Pos;
+
+					file->printf(" \"resource-pos\", {%d, %d}", pos.x, pos.y);
 				} else {
-					if (order->Arg1.Resource.Mine->Destroyed) {
+					if (mine->Destroyed) {
 						/* this unit is destroyed so it's not in the global unit
 						 * array - this means it won't be saved!!! */
 						printf ("FIXME: storing destroyed Mine - loading will fail.\n");
 					}
-					file->printf(" \"resource-mine\", \"%s\"",
-						UnitReference(*order->Arg1.Resource.Mine).c_str());
+					file->printf(" \"resource-mine\", \"%s\"", UnitReference(*mine).c_str());
 				}
 			}
 			break;