From 2a313e217b029d38b14aba1d2c5e77e5666553b4 Mon Sep 17 00:00:00 2001
From: Joris Dauphin <joris.dauphin@gmail.com>
Date: Mon, 18 Jul 2011 12:54:15 +0200
Subject: [PATCH] Fix some action with big units (TileSize > (1;1)) Fix
 regression whan AI should attack with transporter.

---
 src/action/action_build.cpp    |   4 +-
 src/action/action_die.cpp      |  64 ++++----
 src/action/action_follow.cpp   |   2 +-
 src/action/action_resource.cpp |  47 +++---
 src/action/action_train.cpp    |   2 +-
 src/action/action_unload.cpp   | 290 ++++++++++++++-------------------
 src/ai/ai_force.cpp            |   4 +-
 src/ai/ai_local.h              |   1 +
 src/include/actions.h          |   3 +-
 src/include/unit.h             |   6 +-
 src/pathfinder/astar.cpp       |  29 ++--
 src/stratagus/spells.cpp       |   5 +-
 src/unit/script_unit.cpp       |  20 +--
 src/unit/unit.cpp              | 103 +++++++-----
 14 files changed, 275 insertions(+), 305 deletions(-)

diff --git a/src/action/action_build.cpp b/src/action/action_build.cpp
index cd89f20ac..55abf07f4 100644
--- a/src/action/action_build.cpp
+++ b/src/action/action_build.cpp
@@ -500,7 +500,7 @@ void HandleActionBuilt(CUnit &unit)
 			unit.Data.Built.Worker = NoUnitP;
 			// HACK: make sure the sight is updated correctly
 			unit.CurrentSightRange = 1;
-			DropOutOnSide(*worker, LookingW, type->TileWidth, type->TileHeight);
+			DropOutOnSide(*worker, LookingW, &unit);
 			unit.CurrentSightRange = 0;
 		}
 
@@ -544,7 +544,7 @@ void HandleActionBuilt(CUnit &unit)
 				worker->SubAction = 0;//may be 40
 				// HACK: make sure the sight is updated correctly
 				unit.CurrentSightRange = 1;
-				DropOutOnSide(*worker, LookingW, type->TileWidth, type->TileHeight);
+				DropOutOnSide(*worker, LookingW, &unit);
 
 				worker->CurrentOrder()->ClearGoal();
 
diff --git a/src/action/action_die.cpp b/src/action/action_die.cpp
index 4239ff1ff..88d662067 100644
--- a/src/action/action_die.cpp
+++ b/src/action/action_die.cpp
@@ -55,9 +55,7 @@
 */
 void HandleActionDie(CUnit &unit)
 {
-	//
 	// Show death animation
-	//
 	if (unit.Type->Animations && unit.Type->Animations->Death[unit.DamagedType]) {
 		UnitShowAnimation(unit, unit.Type->Animations->Death[unit.DamagedType]);
 	}
@@ -68,43 +66,41 @@ void HandleActionDie(CUnit &unit)
 		unit.Anim.Unbreakable = 0;
 	}
 
-	//
+	if (unit.Anim.Unbreakable) {
+		return;
+	}
 	// Die sequence terminated, generate corpse.
-	//
-	if (!unit.Anim.Unbreakable) {
-		if (!unit.Type->CorpseType) {
-			// We may be in the cache if we just finished out death animation
-			// even though there is no corpse.
-			// (unit.Type->Animations && unit.Type->Animations->Death)
-			// Remove us from the map to be safe
-			unit.Remove(NULL);
-			unit.Release();
-			return;
-		}
+	if (!unit.Type->CorpseType) {
+		// We may be in the cache if we just finished out death animation
+		// even though there is no corpse.
+		// (unit.Type->Animations && unit.Type->Animations->Death)
+		// Remove us from the map to be safe
+		unit.Remove(NULL);
+		unit.Release();
+		return;
+	}
 
-		Assert(unit.Type->TileWidth == unit.Type->CorpseType->TileWidth &&
-			unit.Type->TileHeight == unit.Type->CorpseType->TileHeight);
+	Assert(unit.Type->TileWidth >= unit.Type->CorpseType->TileWidth &&
+		unit.Type->TileHeight >= unit.Type->CorpseType->TileHeight);
 
-		// Update sight for new corpse
-		// We have to unmark BEFORE changing the type.
-		// Always do that, since types can have different vision properties.
-		MapUnmarkUnitGuard(unit);
-		MapUnmarkUnitSight(unit);
-		unit.Type = unit.Type->CorpseType;
-		unit.CurrentSightRange =
-			unit.Type->Stats[unit.Player->Index].Variables[SIGHTRANGE_INDEX].Max;
-		MapMarkUnitSight(unit);
+	// Update sight for new corpse
+	// We have to unmark BEFORE changing the type.
+	// Always do that, since types can have different vision properties.
 
-		// We must be dead to get here, it we aren't we need to know why
-		// This assert replaces and old DEBUG message "Reset to die is really needed"
-		Assert(unit.CurrentAction() == UnitActionDie);
+	unit.Remove(NULL);
+	unit.Type = unit.Type->CorpseType;
+	unit.Stats = &unit.Type->Stats[unit.Player->Index];
+	unit.Place(unit.tilePos);
 
-		unit.SubAction = 0;
-		unit.Frame = 0;
-		UnitUpdateHeading(unit);
-		if (unit.Type->Animations && unit.Type->Animations->Death[ANIMATIONS_DEATHTYPES]) {
-			UnitShowAnimation(unit, unit.Type->Animations->Death[ANIMATIONS_DEATHTYPES]);
-		}
+	// We must be dead to get here, it we aren't we need to know why
+	// This assert replaces and old DEBUG message "Reset to die is really needed"
+	Assert(unit.CurrentAction() == UnitActionDie);
+
+	unit.SubAction = 0;
+	unit.Frame = 0;
+	UnitUpdateHeading(unit);
+	if (unit.Type->Animations && unit.Type->Animations->Death[ANIMATIONS_DEATHTYPES]) {
+		UnitShowAnimation(unit, unit.Type->Animations->Death[ANIMATIONS_DEATHTYPES]);
 	}
 }
 
diff --git a/src/action/action_follow.cpp b/src/action/action_follow.cpp
index bce20592e..0254c8db3 100644
--- a/src/action/action_follow.cpp
+++ b/src/action/action_follow.cpp
@@ -132,7 +132,7 @@ void HandleActionFollow(CUnit &unit)
 				// Teleport the unit
 				unit.Remove(NULL);
 				unit.tilePos = goal->Goal->tilePos;
-				DropOutOnSide(unit, unit.Direction, 1, 1);
+				DropOutOnSide(unit, unit.Direction, NULL);
 #if 0
 				// FIXME: SoundForName() should be called once
 				PlayGameSound(SoundForName("invisibility"), MaxSampleVolume);
diff --git a/src/action/action_resource.cpp b/src/action/action_resource.cpp
index b22d7485d..a33f52a78 100644
--- a/src/action/action_resource.cpp
+++ b/src/action/action_resource.cpp
@@ -275,7 +275,7 @@ static void LoseResource(CUnit &unit, const CUnit &source)
 	CUnit *depot;
 	ResourceInfo *resinfo = unit.Type->ResInfo[unit.CurrentResource];
 
-	Assert((unit.Container && !resinfo->HarvestFromOutside) ||
+	Assert((unit.Container == &source && !resinfo->HarvestFromOutside) ||
 		(!unit.Container && resinfo->HarvestFromOutside));
 
 	if (resinfo->HarvestFromOutside) {
@@ -287,8 +287,7 @@ static void LoseResource(CUnit &unit, const CUnit &source)
 	//
 	if (unit.ResourcesHeld && (depot = FindDeposit(unit, 1000, unit.CurrentResource))) {
 		if (unit.Container) {
-			DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(),
-				source.Type->TileWidth, source.Type->TileHeight);
+			DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), &source);
 		}
 		//
 		// Remember were it mined, so it can look around for another resource.
@@ -306,8 +305,7 @@ static void LoseResource(CUnit &unit, const CUnit &source)
 	//
 	if (unit.Container) {
 		Assert(!resinfo->HarvestFromOutside);
-		DropOutOnSide(unit, LookingW, source.Type->TileWidth,
-			source.Type->TileHeight);
+		DropOutOnSide(unit, LookingW, &source);
 	}
 	unit.CurrentOrder()->goalPos.x = unit.CurrentOrder()->goalPos.y = -1;
 	//use depot as goal
@@ -593,8 +591,7 @@ static int StopGathering(CUnit &unit)
 			!unit.ResourcesHeld) {
 		if (!(resinfo->HarvestFromOutside || resinfo->TerrainHarvester)) {
 			Assert(unit.Container);
-			DropOutOnSide(unit, LookingW, source->Type->TileWidth,
-				source->Type->TileHeight);
+			DropOutOnSide(unit, LookingW, source);
 		}
 		DebugPrint("%d: Worker %d report: Can't find a resource [%d] deposit.\n"
 				_C_ unit.Player->Index _C_ unit.Slot _C_ unit.CurrentResource);
@@ -605,8 +602,7 @@ static int StopGathering(CUnit &unit)
 	} else {
 		if (!(resinfo->HarvestFromOutside || resinfo->TerrainHarvester)) {
 			Assert(unit.Container);
-			DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(),
-				source->Type->TileWidth, source->Type->TileHeight);
+			DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), source);
 		}
 		UnitGotoGoal(unit, depot, SUB_MOVE_TO_DEPOT);
 	}
@@ -733,14 +729,14 @@ static int WaitInDepot(CUnit &unit)
 		pos = unit.CurrentOrder()->Arg1.Resource.Pos;
 
 		if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 0, 10, unit.Player, pos, &pos)) {
-			if(depot)
-				DropOutNearest(unit, pos, depot->Type->TileWidth,
-						 depot->Type->TileHeight);
+			if (depot) {
+				DropOutNearest(unit, pos, depot);
+			}
 			unit.CurrentOrder()->goalPos = pos;
 		} else {
-			if(depot)
-				DropOutOnSide(unit, LookingW, depot->Type->TileWidth,
-						depot->Type->TileHeight);
+			if (depot) {
+				DropOutOnSide(unit, LookingW, depot);
+			}
 			unit.ClearAction();
 		}
 	} else {
@@ -752,12 +748,12 @@ static int WaitInDepot(CUnit &unit)
 		goal = UnitFindResource(unit, pos.x, pos.y, range,
 					unit.CurrentResource, unit.Player->AiEnabled, depot);
 		if (goal) {
-			if(depot)
-				DropOutNearest(unit, goal->tilePos + goal->Type->GetHalfTileSize(),
-					depot->Type->TileWidth, depot->Type->TileHeight);
+			if (depot) {
+				DropOutNearest(unit, goal->tilePos + goal->Type->GetHalfTileSize(), depot);
+			}
 
-			if(goal != mine) {
-				if(mine) {
+			if (goal != mine) {
+				if (mine) {
 					unit.DeAssignWorkerFromMine(*mine);
 					mine->RefsDecrease();
 				}
@@ -774,20 +770,17 @@ static int WaitInDepot(CUnit &unit)
 				_C_ unit.Player->Index _C_ unit.Slot
 				_C_ unit.tilePos.x _C_ unit.tilePos.y
 				_C_ pos.x _C_ pos.y _C_ range);
-			if(depot)
-				DropOutOnSide(unit,
-					LookingW, depot->Type->TileWidth, depot->Type->TileHeight);
-
-			if(mine) {
+			if (depot) {
+				DropOutOnSide(unit, LookingW, depot);
+			}
+			if (mine) {
 				unit.DeAssignWorkerFromMine(*mine);
 				mine->RefsDecrease();
 				unit.CurrentOrder()->Arg1.Resource.Mine = NULL;
 			}
-
 			unit.ClearAction();
 		}
 	}
-
 	return unit.CurrentAction() != UnitActionStill;
 }
 
diff --git a/src/action/action_train.cpp b/src/action/action_train.cpp
index 00a8f2bd1..4c93c2e48 100644
--- a/src/action/action_train.cpp
+++ b/src/action/action_train.cpp
@@ -161,7 +161,7 @@ void HandleActionTrain(CUnit &unit)
 				AddToGroup(&nunit, 1, num);
 			}
 
-			DropOutOnSide(*nunit, LookingW, type->TileWidth, type->TileHeight);
+			DropOutOnSide(*nunit, LookingW, &unit);
 
 			// Set life span
 			if (type->DecayRate) {
diff --git a/src/action/action_unload.cpp b/src/action/action_unload.cpp
index e0e75ff1e..3cfbd5cfc 100644
--- a/src/action/action_unload.cpp
+++ b/src/action/action_unload.cpp
@@ -49,84 +49,64 @@
 --  Functions
 ----------------------------------------------------------------------------*/
 
-
-// Flag for searching a valid tileset for unloading
-#define LandUnitMask ( \
-	MapFieldLandUnit | \
-	MapFieldBuilding | \
-	MapFieldWall | \
-	MapFieldRocks | \
-	MapFieldForest | \
-	MapFieldCoastAllowed | \
-	MapFieldWaterAllowed | \
-	MapFieldUnpassable)
-
-#define NavalUnitMask ( \
-	MapFieldLandUnit | \
-	MapFieldBuilding | \
-	MapFieldWall | \
-	MapFieldRocks | \
-	MapFieldForest | \
-	MapFieldCoastAllowed | \
-	MapFieldLandAllowed | \
-	MapFieldUnpassable)
-
-
 /**
 **  Find a free position close to startPos
 **
+**  @param transporter
+**  @param unit         Unit to unload.
 **  @param startPos     Original search position
-**  @param res  Unload position.
-**  @param mask  Movement mask for the unit to be droped.
+**  @param maxrange     maximal range to unload.
+**  @param res          Unload position.
 **
 **  @return      True if a position was found, False otherwise.
-**  @note        resx and resy are undefined if a position is not found.
+**  @note        res is undefined if a position is not found.
 **
 **  @bug         FIXME: Place unit only on fields reachable from the transporter
-**  @bug         FIXME: This function fails for units larger than 1x1.
 */
-static int FindUnloadPosition(const Vec2i startPos, Vec2i *res, int mask)
+static bool FindUnloadPosition(const CUnit &transporter, const CUnit &unit, const Vec2i startPos, int maxRange, Vec2i *res)
 {
-	int i;
-	int n;
-	int addx;
-	int addy;
 	Vec2i pos = startPos;
-	addx = addy = 1;
+
+	pos.x -= unit.Type->TileWidth - 1;
+	pos.y -= unit.Type->TileHeight - 1;
+	int addx = transporter.Type->TileWidth + unit.Type->TileWidth - 1;
+	int addy = transporter.Type->TileHeight + unit.Type->TileHeight - 1;
+
 	--pos.x;
-	for (n = 0; n < 2; ++n) {
-		// Nobody: There was some code here to check for unloading units that can
-		// only go on even tiles. It's useless, since we can only unload land units.
-		for (i = addy; i--; ++pos.y) {
-			if (CheckedCanMoveToMask(pos, mask)) {
+	for (int range = 0; range < maxRange; ++range) {
+		for (int i = addy; i--; ++pos.y) {
+			if (UnitCanBeAt(unit, pos)) {
 				*res = pos;
-				return 1;
+				return true;
 			}
 		}
 		++addx;
-		for (i = addx; i--; ++pos.x) {
-			if (CheckedCanMoveToMask(pos, mask)) {
+
+		for (int i = addx; i--; ++pos.x) {
+			if (UnitCanBeAt(unit, pos)) {
 				*res = pos;
-				return 1;
+				return true;
 			}
 		}
 		++addy;
-		for (i = addy; i--; --pos.y) {
-			if (CheckedCanMoveToMask(pos, mask)) {
+
+		for (int i = addy; i--; --pos.y) {
+			if (UnitCanBeAt(unit, pos)) {
 				*res = pos;
-				return 1;
+				return true;
 			}
 		}
 		++addx;
-		for (i = addx; i--; --pos.x) {
-			if (CheckedCanMoveToMask(pos, mask)) {
+
+		for (int i = addx; i--; --pos.x) {
+			if (UnitCanBeAt(unit, pos)) {
 				*res = pos;
-				return 1;
+				return true;
 			}
 		}
 		++addy;
 	}
-	return 0;
+	return false;
 }
 
 /**
@@ -138,12 +118,13 @@ static int FindUnloadPosition(const Vec2i startPos, Vec2i *res, int mask)
 **
 **  @bug         FIXME: Place unit only on fields reachable from the transporter
 */
-int UnloadUnit(CUnit &unit)
+static int UnloadUnit(CUnit &transporter, CUnit &unit)
 {
+	const int maxRange = 1;
 	Vec2i pos;
 
 	Assert(unit.Removed);
-	if (!FindUnloadPosition(unit.tilePos, &pos, unit.Type->MovementMask)) {
+	if (!FindUnloadPosition(transporter, unit, transporter.tilePos, maxRange, &pos)) {
 		return 0;
 	}
 	unit.Boarded = 0;
@@ -152,72 +133,80 @@ int UnloadUnit(CUnit &unit)
 }
 
 /**
-**  Find the closest piece of coast you can unload units on
+**  Return true is possition is a correct place to drop out units.
 **
-**  @param  x     start location for the search
-**  @param  y     start location for the search
-**  @param  resPos  coast position
-**
-**  @return       1 if a location was found, 0 otherwise
+**  @param transporter  Transporter unit.
+**  @param pos          position to drop out units.
 */
-static int ClosestFreeCoast(const Vec2i &startPos, Vec2i *resPos)
+static bool IsDropZonePossible(const CUnit &transporter, const Vec2i &pos)
 {
-	int i;
-	int addx;
-	int addy;
-	Vec2i nullpos;
-	Vec2i pos = startPos;
-	int n;
+	const int maxUnloadRange = 1;
 
-	addx = addy = 1;
-	if (Map.CoastOnMap(pos) &&
-			FindUnloadPosition(pos, &nullpos, LandUnitMask)) {
-		*resPos = pos;
-		return 1;
+	if (!UnitCanBeAt(transporter, pos)) {
+		return false;
 	}
-	--pos.x;
-	// The maximum distance to the coast. We have to stop somewhere...
-	n = 20;
-	while (n--) {
-		for (i = addy; i--; ++pos.y) {
-			if (Map.Info.IsPointOnMap(pos) &&
-					Map.CoastOnMap(pos) && !UnitOnMapTile(pos, -1) &&
-					FindUnloadPosition(pos, &nullpos, LandUnitMask)) {
+	Vec2i dummyPos;
+	CUnit* unit = transporter.UnitInside;
+	for (int i = 0; i < transporter.InsideCount; ++i, unit = unit->NextContained) {
+		if (FindUnloadPosition(transporter, *unit, pos, maxUnloadRange, &dummyPos)) {
+			return true;
+		}
+	}
+	// Check unit can be droped from here.
+	return false;
+}
+
+
+/**
+**  Find the closest available drop zone for a transporter.
+**  Fail if transporter don't transport any unit..
+**
+**  @param  transporter  the transporter
+**  @param  startPos     start location for the search
+**	@param  maxRange     The maximum distance from initial position to search...
+**  @param  resPos       drop zone position
+**
+**  @return              true if a location was found, false otherwise
+**  @note to be called only from ClosestFreeDropZone.
+*/
+static bool ClosestFreeDropZone_internal(const CUnit &transporter, const Vec2i &startPos, int maxRange, Vec2i *resPos)
+{
+	int addx = 0;
+	int addy = 1;
+	Vec2i pos = startPos;
+
+	for (int range = 0; range < maxRange; ++range) {
+		for (int i = addy; i--; ++pos.y) {
+			if (IsDropZonePossible(transporter, pos)) {
 				*resPos = pos;
-				return 1;
+				return true;
 			}
 		}
 		++addx;
-		for (i = addx; i--; ++pos.x) {
-			if (Map.Info.IsPointOnMap(pos) &&
-					Map.CoastOnMap(pos) && !UnitOnMapTile(pos, -1) &&
-					FindUnloadPosition(pos, &nullpos, LandUnitMask)) {
+		for (int i = addx; i--; ++pos.x) {
+			if (IsDropZonePossible(transporter, pos)) {
 				*resPos = pos;
-				return 1;
+				return true;
 			}
 		}
 		++addy;
-		for (i = addy; i--; --pos.y) {
-			if (Map.Info.IsPointOnMap(pos) &&
-					Map.CoastOnMap(pos) && !UnitOnMapTile(pos, -1) &&
-					FindUnloadPosition(pos, &nullpos, LandUnitMask)) {
+		for (int i = addy; i--; --pos.y) {
+			if (IsDropZonePossible(transporter, pos)) {
 				*resPos = pos;
-				return 1;
+				return true;
 			}
 		}
 		++addx;
-		for (i = addx; i--; --pos.x) {
-			if (Map.Info.IsPointOnMap(pos) &&
-					Map.CoastOnMap(pos) && !UnitOnMapTile(pos, -1) &&
-					FindUnloadPosition(pos, &nullpos, LandUnitMask)) {
+		for (int i = addx; i--; --pos.x) {
+			if (IsDropZonePossible(transporter, pos)) {
 				*resPos = pos;
-				return 1;
+				return true;
 			}
 		}
 		++addy;
 	}
 	DebugPrint("Try clicking closer to an actual coast.\n");
-	return 0;
+	return false;
 }
 
 /**
@@ -226,52 +215,31 @@ static int ClosestFreeCoast(const Vec2i &startPos, Vec2i *resPos)
 **
 **  @param  transporter  the transporter
 **  @param  startPos     start location for the search
-**  @param  resPos       coast position
+**	@param  maxRange     The maximum distance from initial position to search...
+**  @param  resPos       drop zone position
 **
 **  @return              1 if a location was found, 0 otherwise
-**
 */
-static int ClosestFreeDropZone(CUnit &transporter, const Vec2i& startPos, Vec2i *resPos)
+static int ClosestFreeDropZone(CUnit &transporter, const Vec2i &startPos, int maxRange, Vec2i *resPos)
 {
-	// Type (land/fly/naval) of the transporter
-	int transporterType;
-	// Type (land/fly/naval) of the units to unload
-	int loadedType;
-
 	// Check there are units onboard
 	if (!transporter.UnitInside) {
 		return 0;
 	}
+	const bool isTransporterRemoved = transporter.Removed;
 
-	transporterType = transporter.Type->UnitType;
-	// Take the type of the onboard unit
-	loadedType = transporter.UnitInside->Type->UnitType;
-
-	// Don't move in thoses cases
-	if ((transporterType == loadedType) || (loadedType == UnitTypeFly)) {
-		*resPos = startPos;
-		return 1;
+	if (!isTransporterRemoved) {
+		// Remove transporter to avoid "collision" with itself.
+		transporter.Remove(NULL);
 	}
-
-	switch (transporterType) {
-		case UnitTypeLand:
-			// in this case, loadedType == UnitTypeSea
-			return ClosestFreeCoast(startPos, resPos);
-		case UnitTypeNaval:
-			// Same ( but reversed... )
-			return ClosestFreeCoast(startPos, resPos);
-		case UnitTypeFly:
-			// Here we have loadedType in [ UnitTypeLand,UnitTypeNaval ]
-			if (loadedType == UnitTypeLand) {
-				return FindUnloadPosition(startPos, resPos, LandUnitMask);
-			} else {
-				return FindUnloadPosition(startPos, resPos, NavalUnitMask);
-			}
+	const bool res = ClosestFreeDropZone_internal(transporter, startPos, maxRange, resPos);
+	if (!isTransporterRemoved) {
+		transporter.Place(transporter.tilePos);
 	}
-	// Just to avoid a warning
-	return 0;
+	return res;
 }
 
+
 /**
 **  Move to dropzone.
 **
@@ -299,14 +267,10 @@ static int MoveToDropZone(CUnit &unit)
 **
 **  @param unit  Pointer to unit.
 */
-static void LeaveTransporter(CUnit &unit)
+static void LeaveTransporter(CUnit &transporter)
 {
-	int i;
-	int stillonboard;
-	CUnit *goal;
-
-	stillonboard = 0;
-	goal = unit.CurrentOrder()->GetGoal();
+	int stillonboard = 0;
+	CUnit *goal = transporter.CurrentOrder()->GetGoal();
 	//
 	// Goal is the specific unit unit that you want to unload.
 	// This can be NULL, in case you want to unload everything.
@@ -314,30 +278,30 @@ static void LeaveTransporter(CUnit &unit)
 	if (goal) {
 		if (goal->Destroyed) {
 			DebugPrint("destroyed unit unloading?\n");
-			unit.CurrentOrder()->ClearGoal();
+			transporter.CurrentOrder()->ClearGoal();
 			return;
 		}
-		unit.CurrentOrder()->ClearGoal();
-		goal->tilePos = unit.tilePos;
+		transporter.CurrentOrder()->ClearGoal();
+		goal->tilePos = transporter.tilePos;
 		// Try to unload the unit. If it doesn't work there is no problem.
-		if (UnloadUnit(*goal)) {
-			unit.BoardCount--;
+		if (UnloadUnit(transporter, *goal)) {
+			transporter.BoardCount--;
 		}
 	} else {
 		// Unload all units.
-		goal = unit.UnitInside;
-		for (i = unit.InsideCount; i; --i, goal = goal->NextContained) {
+		goal = transporter.UnitInside;
+		for (int i = transporter.InsideCount; i; --i, goal = goal->NextContained) {
 			if (goal->Boarded) {
-				goal->tilePos = unit.tilePos;
-				if (!UnloadUnit(*goal)) {
+				goal->tilePos = transporter.tilePos;
+				if (!UnloadUnit(transporter, *goal)) {
 					++stillonboard;
 				} else {
-					unit.BoardCount--;
+					transporter.BoardCount--;
 				}
 			}
 		}
 	}
-	if (IsOnlySelected(unit)) {
+	if (IsOnlySelected(transporter)) {
 		SelectedUnitChanged();
 	}
 
@@ -345,12 +309,12 @@ static void LeaveTransporter(CUnit &unit)
 	if (stillonboard) {
 		// We tell it to unload at it's current position. This can't be done,
 		// so it will search for a piece of free coast nearby.
-		unit.CurrentOrder()->Action = UnitActionUnload;
-		unit.CurrentOrder()->ClearGoal();
-		unit.CurrentOrder()->goalPos = unit.tilePos;
-		unit.SubAction = 0;
+		transporter.CurrentOrder()->Action = UnitActionUnload;
+		transporter.CurrentOrder()->ClearGoal();
+		transporter.CurrentOrder()->goalPos = transporter.tilePos;
+		transporter.SubAction = 0;
 	} else {
-		unit.ClearAction();
+		transporter.ClearAction();
 	}
 }
 
@@ -361,19 +325,17 @@ static void LeaveTransporter(CUnit &unit)
 */
 void HandleActionUnload(CUnit &unit)
 {
-	int i;
-	Vec2i pos;
+	const int maxSearchRange = 20;
 
 	if (!unit.CanMove()) {
 		unit.SubAction = 2;
 	}
 	switch (unit.SubAction) {
-		//
-		// Move the transporter
-		//
-		case 0:
+		case 0: // Choose destination
 			if (!unit.CurrentOrder()->HasGoal()) {
-				if (!ClosestFreeDropZone(unit, unit.CurrentOrder()->goalPos, &pos)) {
+				Vec2i pos;
+
+				if (!ClosestFreeDropZone(unit, unit.CurrentOrder()->goalPos, maxSearchRange, &pos)) {
 					// Sorry... I give up.
 					unit.ClearAction();
 					return;
@@ -383,12 +345,15 @@ void HandleActionUnload(CUnit &unit)
 
 			NewResetPath(unit);
 			unit.SubAction = 1;
-		case 1:
+			// follow on next case
+		case 1: // Move unit to destination
 			// The Goal is the unit that we have to unload.
 			if (!unit.CurrentOrder()->HasGoal()) {
+				const int moveResult = MoveToDropZone(unit);
+
 				// We have to unload everything
-				if ((i = MoveToDropZone(unit))) {
-					if (i == PF_REACHED) {
+				if (moveResult) {
+					if (moveResult == PF_REACHED) {
 						if (++unit.SubAction == 1) {
 							unit.ClearAction();
 						}
@@ -398,10 +363,7 @@ void HandleActionUnload(CUnit &unit)
 				}
 				break;
 			}
-		//
-		// Leave the transporter
-		//
-		case 2:
+		case 2: // Leave the transporter
 			// FIXME: show still animations ?
 			LeaveTransporter(unit);
 			if (unit.CanMove() && unit.CurrentAction() != UnitActionStill) {
diff --git a/src/ai/ai_force.cpp b/src/ai/ai_force.cpp
index 4b7f61c9f..7725f1e30 100644
--- a/src/ai/ai_force.cpp
+++ b/src/ai/ai_force.cpp
@@ -616,7 +616,7 @@ static void AiGroupAttackerForTransport(AiForce &aiForce)
 		}
 	}
 	if (transporterIndex == aiForce.Size()) {
-		aiForce.State = AiForceAttackingState_Attacking;
+		aiForce.State = AiForceAttackingState_AttackingWithTransporter;
 		return ;
 	}
 	for (unsigned int i = 0; i < aiForce.Size(); ++i) {
@@ -628,7 +628,7 @@ static void AiGroupAttackerForTransport(AiForce &aiForce)
 		}
 	}
 	if (goNext == true) {
-		aiForce.State = AiForceAttackingState_Attacking;
+		aiForce.State = AiForceAttackingState_AttackingWithTransporter;
 		return ;
 	}
 	for (unsigned int i = 0; i < aiForce.Size(); ++i) {
diff --git a/src/ai/ai_local.h b/src/ai/ai_local.h
index f3fdd4172..9340a0c6a 100644
--- a/src/ai/ai_local.h
+++ b/src/ai/ai_local.h
@@ -98,6 +98,7 @@ enum AiForceAttackingState
 	AiForceAttackingState_Free = -1,
 	AiForceAttackingState_Waiting = 0,
 	AiForceAttackingState_Boarding,
+	AiForceAttackingState_AttackingWithTransporter,
 	AiForceAttackingState_Attacking,
 };
 
diff --git a/src/include/actions.h b/src/include/actions.h
index 4e04c7a9d..ceec18c0c 100644
--- a/src/include/actions.h
+++ b/src/include/actions.h
@@ -208,8 +208,7 @@ extern int UnitShowAnimationScaled(CUnit &unit, const CAnimation *anim, int scal
 extern int UnitShowAnimation(CUnit &unit, const CAnimation *anim);
 	/// Handle the actions of all units each game cycle
 extern void UnitActions();
-	/// Unload a unit.
-extern int UnloadUnit(CUnit &unit);
+
 //@}
 
 #endif // !__ACTIONS_H__
diff --git a/src/include/unit.h b/src/include/unit.h
index ef55a2855..ba0148c84 100644
--- a/src/include/unit.h
+++ b/src/include/unit.h
@@ -1221,7 +1221,7 @@ struct CResourceDepositFinder {
 class CPreference {
 public:
 	CPreference() : ShowSightRange(false), ShowReactionRange(false),
-		ShowAttackRange(false), ShowMessages(false), 
+		ShowAttackRange(false), ShowMessages(false),
 		BigScreen(false),ShowOrders(0) {};
 
 	bool ShowSightRange;     /// Show sight range.
@@ -1311,9 +1311,9 @@ extern void UnitHeadingFromDeltaXY(CUnit &unit, const Vec2i &delta);
 
 
 	/// @todo more docu
-extern void DropOutOnSide(CUnit &unit, int heading, int addx, int addy);
+extern void DropOutOnSide(CUnit &unit, int heading, const CUnit *container);
 	/// @todo more docu
-extern void DropOutNearest(CUnit &unit, const Vec2i &goalPos, int addx, int addy);
+extern void DropOutNearest(CUnit &unit, const Vec2i &goalPos, const CUnit *container);
 
 	/// Drop out all units in the unit
 extern void DropOutAll(const CUnit &unit);
diff --git a/src/pathfinder/astar.cpp b/src/pathfinder/astar.cpp
index cd42a2d22..be90aad72 100644
--- a/src/pathfinder/astar.cpp
+++ b/src/pathfinder/astar.cpp
@@ -615,12 +615,13 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh,
 	bool goal_reachable = false;
 
 	const Vec2i goal = {gx, gy};
+	const Vec2i extratilesize = {tilesizex - 1, tilesizey - 1};
 
 	// top hemi cycle
-	const int miny = std::max(-maxrange, 0 - goal.y);
-	for (int offsety = miny; offsety < -minrange; ++offsety) {
+	const int miny = std::max(-maxrange - extratilesize.y, 0 - goal.y);
+	for (int offsety = miny; offsety < -minrange - extratilesize.y; ++offsety) {
 		const int offsetx = isqrt(square(maxrange + 1) - square(-offsety) - 1);
-		const int minx = std::max(0, goal.x - offsetx);
+		const int minx = std::max(0, goal.x - offsetx - extratilesize.x);
 		const int maxx = std::min(Map.Info.MapWidth, goal.x + gw + offsetx);
 		Vec2i mpos = {minx, goal.y + offsety};
 		const unsigned int offset = mpos.y * Map.Info.MapWidth;
@@ -635,8 +636,8 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh,
 	}
 	if (minrange == 0) {
 		// center
-		for (int offsety = 0; offsety < gh; ++offsety) {
-			const int minx = std::max(0, goal.x - maxrange);
+		for (int offsety = -extratilesize.y; offsety < gh; ++offsety) {
+			const int minx = std::max(0, goal.x - maxrange - extratilesize.x);
 			const int maxx = std::min(Map.Info.MapWidth, goal.x + gw + maxrange);
 			Vec2i mpos = {minx, goal.y + offsety};
 			const unsigned int offset = mpos.y * Map.Info.MapWidth;
@@ -651,12 +652,12 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh,
 		}
 	} else {
 		// top hemi cycle
-		const int miny = std::max(-minrange, 0 - goal.y);
+		const int miny = std::max(-minrange - extratilesize.y, 0 - goal.y);
 		for (int offsety = miny; offsety < 0; ++offsety) {
 			const int offsetx1 = isqrt(square(maxrange + 1) - square(-offsety) - 1);
 			const int offsetx2 = isqrt(square(minrange + 1) - square(-offsety) - 1);
-			const int minxs[2] = {std::max(0, goal.x - offsetx1), std::min(Map.Info.MapWidth, goal.x + gw + offsetx2)};
-			const int maxxs[2] = {std::max(0, goal.x - offsetx2), std::min(Map.Info.MapWidth, goal.x + gw + offsetx1)};
+			const int minxs[2] = {std::max(0, goal.x - offsetx1 - extratilesize.x), std::min(Map.Info.MapWidth, goal.x + gw + offsetx2)};
+			const int maxxs[2] = {std::max(0, goal.x - offsetx2 - extratilesize.x), std::min(Map.Info.MapWidth, goal.x + gw + offsetx1)};
 
 			for (int i = 0; i < 2; ++i)
 			{
@@ -676,11 +677,11 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh,
 		}
 
 		// center
-		const int mincenters[] = {std::max(0, goal.x - maxrange), 0, std::min(Map.Info.MapWidth, goal.x + gw + minrange)};
-		const int maxcenters[] = {std::max(0, goal.x - minrange), gw, std::min(Map.Info.MapWidth, goal.x + gw + maxrange)};
+		const int mincenters[] = {std::max(0, goal.x - maxrange - extratilesize.x), -extratilesize.x, std::min(Map.Info.MapWidth, goal.x + gw + minrange)};
+		const int maxcenters[] = {std::max(0, goal.x - minrange - extratilesize.x), gw, std::min(Map.Info.MapWidth, goal.x + gw + maxrange)};
 
 		for (int i = 0; i < 3; ++i) {
-			for (int offsety = 0; offsety < gh; ++offsety) {
+			for (int offsety = -extratilesize.y; offsety < gh; ++offsety) {
 				const int minx = mincenters[i];
 				const int maxx = maxcenters[i];
 				Vec2i mpos = {minx, goal.y + offsety};
@@ -701,8 +702,8 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh,
 		for (int offsety = 0; offsety < maxy; ++offsety) {
 			const int offsetx1 = isqrt(square(maxrange + 1) - square(offsety) - 1);
 			const int offsetx2 = isqrt(square(minrange + 1) - square(offsety) - 1);
-			const int minxs[2] = {std::max(0, goal.x - offsetx1), std::min(Map.Info.MapWidth, goal.x + gw + offsetx2)};
-			const int maxxs[2] = {std::max(0, goal.x - offsetx2), std::min(Map.Info.MapWidth, goal.x + gw + offsetx1)};
+			const int minxs[2] = {std::max(0, goal.x - offsetx1) - extratilesize.x, std::min(Map.Info.MapWidth, goal.x + gw + offsetx2)};
+			const int maxxs[2] = {std::max(0, goal.x - offsetx2) - extratilesize.x, std::min(Map.Info.MapWidth, goal.x + gw + offsetx1)};
 
 			for (int i = 0; i < 2; ++i)
 			{
@@ -726,7 +727,7 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh,
 	const int maxy = std::min(maxrange, Map.Info.MapHeight - goal.y - gh);
 	for (int offsety = minrange; offsety < maxy; ++offsety) {
 		const int offsetx = isqrt(square(maxrange + 1) - square(offsety) - 1);
-		const int minx = std::max(0, goal.x - offsetx);
+		const int minx = std::max(0, goal.x - offsetx - extratilesize.x);
 		const int maxx = std::min(Map.Info.MapWidth, goal.x + gw + offsetx);
 		Vec2i mpos = {minx, goal.y + gh + offsety};
 		const unsigned int offset = mpos.y * Map.Info.MapWidth;
diff --git a/src/stratagus/spells.cpp b/src/stratagus/spells.cpp
index 4790a60c2..295dd7b0c 100644
--- a/src/stratagus/spells.cpp
+++ b/src/stratagus/spells.cpp
@@ -676,10 +676,9 @@ int Summon::Cast(CUnit &caster, const SpellType *spell,
 		//
 		target = MakeUnit(unittype, caster.Player);
 		if (target != NoUnitP) {
-			// This is a hack to walk around behaviour of DropOutOnSide
-			target->tilePos.x = x + 1;
+			target->tilePos.x = x;
 			target->tilePos.y = y;
-			DropOutOnSide(*target, LookingW, 0, 0); // FIXME : 0,0) : good parameter ?
+			DropOutOnSide(*target, LookingW, NULL);
 			//
 			//  set life span. ttl=0 results in a permanent unit.
 			//
diff --git a/src/unit/script_unit.cpp b/src/unit/script_unit.cpp
index 9c691fe5d..82a8dfb7b 100644
--- a/src/unit/script_unit.cpp
+++ b/src/unit/script_unit.cpp
@@ -940,14 +940,12 @@ static int CclUnit(lua_State *l)
 */
 static int CclMoveUnit(lua_State *l)
 {
-	CUnit *unit;
-	int heading;
 	Vec2i ipos;
 
 	LuaCheckArgs(l, 2);
 
 	lua_pushvalue(l, 1);
-	unit = CclGetUnit(l);
+	CUnit *unit = CclGetUnit(l);
 	lua_pop(l, 1);
 
 	lua_rawgeti(l, 2, 1);
@@ -957,14 +955,14 @@ static int CclMoveUnit(lua_State *l)
 	ipos.y = LuaToNumber(l, -1);
 	lua_pop(l, 1);
 
-	heading = SyncRand() % 256;
 	if (UnitCanBeAt(*unit, ipos)) {
 		unit->Place(ipos);
 	} else {
+		const int heading = SyncRand() % 256;
+
 		unit->tilePos = ipos;
-		DropOutOnSide(*unit, heading, 1, 1);
+		DropOutOnSide(*unit, heading, NULL);
 	}
-//	PlaceUnit(unit, ipos.x, ipos.y);
 	lua_pushvalue(l, 1);
 	return 1;
 }
@@ -980,7 +978,6 @@ static int CclCreateUnit(lua_State *l)
 {
 	CUnitType *unittype;
 	CUnit *unit;
-	int heading;
 	int playerno;
 	Vec2i ipos;
 
@@ -999,7 +996,6 @@ static int CclCreateUnit(lua_State *l)
 	ipos.y = LuaToNumber(l, -1);
 	lua_pop(l, 1);
 
-	heading = SyncRand() % 256;
 	lua_pushvalue(l, 2);
 	playerno = TriggerGetPlayer(l);
 	lua_pop(l, 1);
@@ -1023,8 +1019,10 @@ static int CclCreateUnit(lua_State *l)
 				(unit->Type->Building && CanBuildUnitType(NULL, *unit->Type, ipos, 0))) {
 			unit->Place(ipos);
 		} else {
+			const int heading = SyncRand() % 256;
+
 			unit->tilePos = ipos;
-			DropOutOnSide(*unit, heading, 1, 1);
+			DropOutOnSide(*unit, heading, NULL);
 		}
 		UpdateForNewUnit(*unit, 0);
 
@@ -1344,9 +1342,9 @@ static int CclSetUnitVariable(lua_State *l)
 	if (!strcmp(name, "RegenerationRate"))
 	{
 		value = LuaToNumber(l, 3);
-		if (value > unit->Variable[HP_INDEX].Max) 
+		if (value > unit->Variable[HP_INDEX].Max)
 			unit->Stats->Variables[HP_INDEX].Increase = unit->Variable[HP_INDEX].Max;
-		else 
+		else
 			unit->Stats->Variables[HP_INDEX].Increase = value;
 	}
 	else
diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp
index 6aedda7e4..fe3290349 100644
--- a/src/unit/unit.cpp
+++ b/src/unit/unit.cpp
@@ -634,6 +634,10 @@ void MarkUnitFieldFlags(const CUnit &unit)
 	int w, h = unit.Type->TileHeight;          // Tile height of the unit.
 	const int width = unit.Type->TileWidth;          // Tile width of the unit.
 	unsigned int index = unit.Offset;
+
+	if (unit.Type->Vanishes) {
+		return ;
+	}
 	do {
 		mf = Map.Field(index);
 		w = width;
@@ -671,6 +675,10 @@ void UnmarkUnitFieldFlags(const CUnit &unit)
 	const int width = unit.Type->TileWidth;          // Tile width of the unit.
 	unsigned int index = unit.Offset;
 
+	if (unit.Type->Vanishes) {
+		return ;
+	}
+
 	_UnmarkUnitFieldFlags funct(unit);
 
 	do {
@@ -1814,62 +1822,74 @@ void UnitHeadingFromDeltaXY(CUnit &unit, const Vec2i &delta)
 **
 **  @param unit       Unit to drop out.
 **  @param heading    Direction in which the unit should appear.
-**  @param addx       Tile width of unit it's dropping out of.
-**  @param addy       Tile height of unit it's dropping out of.
+**  @param container  Unit "containing" unit to drop (may be different of unit.Container).
 */
-void DropOutOnSide(CUnit &unit, int heading, int addx, int addy)
+void DropOutOnSide(CUnit &unit, int heading, const CUnit *container)
 {
 	Vec2i pos;
-	int i;
+	int addx = 0;
+	int addy = 0;
 
-	if (unit.Container) {
-		pos = unit.Container->tilePos;
+	if (container) {
+		pos = container->tilePos;
+		pos.x -= unit.Type->TileWidth - 1;
+		pos.y -= unit.Type->TileHeight - 1;
+		addx = container->Type->TileWidth + unit.Type->TileWidth - 1;
+		addy = container->Type->TileHeight + unit.Type->TileHeight - 1;
+
+		if (heading < LookingNE || heading > LookingNW) {
+			pos.x += addx - 1;
+			--pos.y;
+			goto startn;
+		} else if (heading < LookingSE) {
+			pos.x += addx;
+			pos.y += addy - 1;
+			goto starte;
+		} else if (heading < LookingSW) {
+			pos.y += addy;
+			goto starts;
+		} else {
+			--pos.x;
+			goto startw;
+		}
 	} else {
 		pos = unit.tilePos;
-	}
 
-	if (heading < LookingNE || heading > LookingNW) {
-		pos.x += addx - 1;
-		--pos.y;
-		goto startn;
+		if (heading < LookingNE || heading > LookingNW) {
+			goto starts;
+		} else if (heading < LookingSE) {
+			goto startw;
+		} else if (heading < LookingSW) {
+			goto startn;
+		} else {
+			goto starte;
+		}
 	}
-	if (heading < LookingSE) {
-		pos.x += addx;
-		pos.y += addy - 1;
-		goto starte;
-	}
-	if (heading < LookingSW) {
-		pos.y += addy;
-		goto starts;
-	}
-	--pos.x;
-	goto startw;
-
 	// FIXME: don't search outside of the map
 	for (;;) {
 startw:
-		for (i = addy; i--; ++pos.y) {
+		for (int i = addy; i--; ++pos.y) {
 			if (UnitCanBeAt(unit, pos)) {
 				goto found;
 			}
 		}
 		++addx;
 starts:
-		for (i = addx; i--; ++pos.x) {
+		for (int i = addx; i--; ++pos.x) {
 			if (UnitCanBeAt(unit, pos)) {
 				goto found;
 			}
 		}
 		++addy;
 starte:
-		for (i = addy; i--; --pos.y) {
+		for (int i = addy; i--; --pos.y) {
 			if (UnitCanBeAt(unit, pos)) {
 				goto found;
 			}
 		}
 		++addx;
 startn:
-		for (i = addx; i--; --pos.x) {
+		for (int i = addx; i--; --pos.x) {
 			if (UnitCanBeAt(unit, pos)) {
 				goto found;
 			}
@@ -1882,29 +1902,34 @@ found:
 }
 
 /**
-**  Place a unit on the map nearest to x, y.
+**  Place a unit on the map nearest to goalPos.
 **
 **  @param unit  Unit to drop out.
 **  @param goalPos Goal map tile position.
 **  @param addx  Tile width of unit it's dropping out of.
 **  @param addy  Tile height of unit it's dropping out of.
 */
-void DropOutNearest(CUnit &unit, const Vec2i &goalPos, int addx, int addy)
+void DropOutNearest(CUnit &unit, const Vec2i &goalPos, const CUnit *container)
 {
 	Vec2i pos;
 	Vec2i bestPos = {0, 0};
 	int bestd = 99999;
-
+	int addx = 0;
+	int addy = 0;
 	Assert(unit.Removed);
 
-	if (unit.Container) {
-		pos = unit.Container->tilePos;
+	if (container) {
+		pos = container->tilePos;
+		pos.x -= unit.Type->TileWidth - 1;
+		pos.y -= unit.Type->TileHeight - 1;
+		addx = container->Type->TileWidth + unit.Type->TileWidth - 1;
+		addy = container->Type->TileHeight + unit.Type->TileHeight - 1;
+		--pos.x;
 	} else {
 		pos = unit.tilePos;
 	}
-
 	// FIXME: if we reach the map borders we can go fast up, left, ...
-	--pos.x;
+
 	for (;;) {
 		for (int i = addy; i--; ++pos.y) { // go down
 			if (UnitCanBeAt(unit, pos)) {
@@ -1964,18 +1989,14 @@ void DropOutNearest(CUnit &unit, const Vec2i &goalPos, int addx, int addy)
 */
 void DropOutAll(const CUnit &source)
 {
-	CUnit *unit;
-	int i;
+	CUnit *unit = source.UnitInside;
 
-	unit = source.UnitInside;
-	for (i = source.InsideCount; i; --i, unit = unit->NextContained) {
-		DropOutOnSide(*unit, LookingW,
-			source.Type->TileWidth, source.Type->TileHeight);
+	for (int i = source.InsideCount; i; --i, unit = unit->NextContained) {
+		DropOutOnSide(*unit, LookingW, &source);
 		Assert(!unit->CurrentOrder()->HasGoal());
 		unit->CurrentOrder()->Action = UnitActionStill;
 		unit->SubAction = 0;
 	}
-	DebugPrint("Drop out %d of %d\n" _C_ i _C_ source.Data.Resource.Active);
 }