From 77334d35331ce8c577f31fe8a9a00b00a7a3a40d Mon Sep 17 00:00:00 2001
From: joris <joris.dauphin@gmail.com>
Date: Thu, 23 Feb 2012 16:12:12 +0100
Subject: [PATCH] COrder_{Move, Follow} Fix typo with Unload.

---
 src/action/action_follow.cpp | 161 ++++++++++++++++++++---------------
 src/action/action_move.cpp   | 105 +++++++++++------------
 src/action/action_unload.cpp |   2 +-
 src/action/actions.cpp       |   6 +-
 src/include/actions.h        |  33 ++++++-
 src/unit/script_unit.cpp     |   8 +-
 src/unit/unit_save.cpp       |   9 --
 7 files changed, 182 insertions(+), 142 deletions(-)

diff --git a/src/action/action_follow.cpp b/src/action/action_follow.cpp
index 21408a57c..629499b9a 100644
--- a/src/action/action_follow.cpp
+++ b/src/action/action_follow.cpp
@@ -41,46 +41,72 @@
 #include "unit.h"
 #include "unittype.h"
 #include "pathfinder.h"
-#include "map.h"
 #include "actions.h"
+#include "iolib.h"
+#include "script.h"
 
 /*----------------------------------------------------------------------------
---  Variables
+--  Functions
 ----------------------------------------------------------------------------*/
 
-/*----------------------------------------------------------------------------
---  Function
-----------------------------------------------------------------------------*/
 
-/**
-**  Unit follow action:
-**
-**  @param unit  Pointer to unit.
-*/
-void HandleActionFollow(COrder& order, CUnit &unit)
+/* virtual */ void COrder_Follow::Save(CFile &file, const CUnit &unit) const
+{
+	file.printf("{\"action-follow\",");
+
+	file.printf(" \"range\", %d,", this->Range);
+	if (this->HasGoal()) {
+		CUnit &goal = *this->GetGoal();
+		if (goal.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 Goal - loading will fail.\n");
+		}
+		file.printf(" \"goal\", \"%s\",", UnitReference(goal).c_str());
+	}
+	file.printf(" \"tile\", {%d, %d},", this->goalPos.x, this->goalPos.y);
+
+	file.printf(" \"state\", %d,", this->State);
+	SaveDataMove(file);
+
+	file.printf("}");
+}
+
+/* virtual */ bool COrder_Follow::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit)
+{
+	if (ParseMoveData(l, j, value)) {
+		return true;
+	} else if (!strcmp(value, "state")) {
+		++j;
+		lua_rawgeti(l, -1, j + 1);
+		this->State = LuaToNumber(l, -1);
+		lua_pop(l, 1);
+	} else {
+		return false;
+	}
+	return true;
+}
+
+/* virtual */ bool COrder_Follow::Execute(CUnit &unit)
 {
 	if (unit.Wait) {
 		unit.Wait--;
-		return;
+		return false;
 	}
-	CUnit *goal = order.GetGoal();
+	CUnit *goal = this->GetGoal();
 
 	// Reached target
-	if (order.SubAction.Follow == 128) {
+	if (this->State == 128) {
 
 		if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) {
 			DebugPrint("Goal gone\n");
-			order.ClearGoal();
-			unit.ClearAction();
-			return;
+			return true;
 		}
 
-		if (goal->tilePos == order.goalPos) {
+		if (goal->tilePos == this->goalPos) {
 			// Move to the next order
 			if (unit.Orders.size() > 1) {
-				order.ClearGoal();
-				unit.ClearAction();
-				return;
+				return true;
 			}
 
 			// Reset frame to still frame while we wait
@@ -88,32 +114,30 @@ void HandleActionFollow(COrder& order, CUnit &unit)
 			unit.Frame = unit.Type->StillFrame;
 			UnitUpdateHeading(unit);
 			unit.Wait = 10;
-			if (order.Range > 1) {
-				order.Range = 1;
-				order.SubAction.Follow = 0;
+			if (this->Range > 1) {
+				this->Range = 1;
+				this->State = 0;
 			}
-			return;
+			return false;
 		}
-		order.SubAction.Follow = 0;
+		this->State = 0;
 	}
-	if (!order.SubAction.Follow) { // first entry
-		order.SubAction .Follow= 1;
-		order.NewResetPath();
-		Assert(unit.State == 0);
+	if (!this->State) { // first entry
+		this->State = 1;
+		this->NewResetPath();
 	}
 	switch (DoActionMove(unit)) { // reached end-point?
 		case PF_UNREACHABLE:
 			// Some tries to reach the goal
-			if (order.CheckRange()) {
-				order.Range++;
+			if (this->CheckRange()) {
+				this->Range++;
 				break;
 			}
 			// FALL THROUGH
 		case PF_REACHED:
 		{
 			if (!goal) { // goal has died
-				unit.ClearAction();
-				return;
+				return true;
 			}
 			// Handle Teleporter Units
 			// FIXME: BAD HACK
@@ -132,8 +156,6 @@ void HandleActionFollow(COrder& order, CUnit &unit)
 					unit.tilePos.x * PixelTileSize.x + PixelTileSize.x / 2,
 					unit.tilePos.y * PixelTileSize.y + PixelTileSize.y / 2);
 #endif
-				unit.ClearAction();
-
 				// FIXME: we must check if the units supports the new order.
 				CUnit &dest = *goal->Goal;
 
@@ -141,26 +163,24 @@ void HandleActionFollow(COrder& order, CUnit &unit)
 					|| (dest.NewOrder->Action == UnitActionResource && !unit.Type->Harvester)
 					|| (dest.NewOrder->Action == UnitActionAttack && !unit.Type->CanAttack)
 					|| (dest.NewOrder->Action == UnitActionBoard && unit.Type->UnitType != UnitTypeLand)) {
-					unit.ClearAction();
-					unit.CurrentOrder()->ClearGoal();
+					return true;
 				} else {
 					if (dest.NewOrder->HasGoal()) {
 						if (dest.NewOrder->GetGoal()->Destroyed) {
-							// FIXME: perhaps we should use another dest?
-							DebugPrint("Destroyed unit in teleport unit\n");
-							dest.NewOrder->ClearGoal();
-							dest.NewOrder->Action = UnitActionStill;
+							delete dest.NewOrder;
+							dest.NewOrder = NULL;
+							return true;
 						}
 					}
 
 					delete unit.CurrentOrder();
 					unit.Orders[0] = dest.NewOrder->Clone();
 					unit.CurrentResource = dest.CurrentResource;
+					return false;
 				}
-				return;
 			}
-			order.goalPos = goal->tilePos;
-			order.SubAction.Follow = 128;
+			this->goalPos = goal->tilePos;
+			this->State = 128;
 		}
 			// FALL THROUGH
 		default:
@@ -170,36 +190,43 @@ void HandleActionFollow(COrder& order, CUnit &unit)
 	// Target destroyed?
 	if (goal && !goal->IsVisibleAsGoal(*unit.Player)) {
 		DebugPrint("Goal gone\n");
-		order.goalPos = goal->tilePos + goal->Type->GetHalfTileSize();
-		order.ClearGoal();
+		this->goalPos = goal->tilePos + goal->Type->GetHalfTileSize();
+		this->ClearGoal();
 		goal = NoUnitP;
-		order.NewResetPath();
+		this->NewResetPath();
 	}
 
-	if (!unit.Anim.Unbreakable) {
-		// If our leader is dead or stops or attacks:
-		// Attack any enemy in reaction range.
-		// If don't set the goal, the unit can than choose a
-		//  better goal if moving nearer to enemy.
-		if (unit.Type->CanAttack
-			&& (!goal || goal->CurrentAction() == UnitActionAttack || goal->CurrentAction() == UnitActionStill)) {
-			goal = AttackUnitsInReactRange(unit);
-			if (goal) {
-				// Save current command to come back.
-				COrder *savedOrder = order.Clone();
+	if (unit.Anim.Unbreakable) {
+		return false;
+	}
+	// If our leader is dead or stops or attacks:
+	// Attack any enemy in reaction range.
+	// If don't set the goal, the unit can than choose a
+	//  better goal if moving nearer to enemy.
+	if (unit.Type->CanAttack
+		&& (!goal || goal->CurrentAction() == UnitActionAttack || goal->CurrentAction() == UnitActionStill)) {
+		CUnit *target = AttackUnitsInReactRange(unit);
+		if (target) {
+			// Save current command to come back.
+			COrder *savedOrder = this->Clone();
 
-				CommandAttack(unit, goal->tilePos, NULL, FlushCommands);
+			CommandAttack(unit, target->tilePos, NULL, FlushCommands);
 
-				if (unit.StoreOrder(savedOrder) == false) {
-					delete savedOrder;
-					savedOrder = NULL;
-				}
-				// This stops the follow command and the attack is executed
-				unit.CurrentOrder()->ClearGoal();
-				unit.ClearAction();
+			if (unit.StoreOrder(savedOrder) == false) {
+				delete savedOrder;
+				savedOrder = NULL;
 			}
+			return true;
 		}
 	}
+	return false;
+}
+
+void HandleActionFollow(COrder& order, CUnit &unit)
+{
+	if (order.Execute(unit)) {
+		unit.ClearAction();
+	}
 }
 
 //@}
diff --git a/src/action/action_move.cpp b/src/action/action_move.cpp
index 864fd5901..9c78f2f1e 100644
--- a/src/action/action_move.cpp
+++ b/src/action/action_move.cpp
@@ -37,27 +37,43 @@
 #include <stdlib.h>
 
 #include "stratagus.h"
-#include "video.h"
+
+#include "actions.h"
 #include "unittype.h"
 #include "animation.h"
-#include "player.h"
 #include "unit.h"
-#include "tileset.h"
-#include "map.h"
-#include "actions.h"
 #include "pathfinder.h"
 #include "sound.h"
 #include "interface.h"
 #include "map.h"
 #include "ai.h"
+#include "iolib.h"
+#include "script.h"
 
 /*----------------------------------------------------------------------------
---  Variables
+--  Functions
 ----------------------------------------------------------------------------*/
 
-/*----------------------------------------------------------------------------
---  Function
-----------------------------------------------------------------------------*/
+
+/* virtual */ void COrder_Move::Save(CFile &file, const CUnit &unit) const
+{
+	file.printf("{\"action-move\",");
+
+	file.printf(" \"range\", %d,", this->Range);
+	file.printf(" \"tile\", {%d, %d},", this->goalPos.x, this->goalPos.y);
+
+	SaveDataMove(file);
+	file.printf("}");
+}
+
+/* virtual */ bool COrder_Move::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit)
+{
+	if (ParseMoveData(l, j, value)) {
+		return true;
+	} else {
+		return false;
+	}
+}
 
 /**
 **  Unit moves! Generic function called from other actions.
@@ -72,16 +88,17 @@ int DoActionMove(CUnit &unit)
 	Vec2i posd; // movement in tile.
 	int d;
 	Vec2i pos;
-	int move;
-	int off;
 
 	Assert(unit.CanMove());
-	if (!unit.Moving &&
-			(unit.Type->Animations->Move != unit.Anim.CurrAnim || !unit.Anim.Wait)) {
+
+	COrder& order = *unit.CurrentOrder();
+
+	if (!unit.Moving && (unit.Type->Animations->Move != unit.Anim.CurrAnim || !unit.Anim.Wait)) {
 		Assert(!unit.Anim.Unbreakable);
 
 		// FIXME: So units flying up and down are not affected.
-		unit.IX = unit.IY = 0;
+		unit.IX = 0;
+		unit.IY = 0;
 
 		UnmarkUnitFieldFlags(unit);
 		d = NextPathElement(unit, &posd.x, &posd.y);
@@ -110,14 +127,14 @@ int DoActionMove(CUnit &unit)
 				break;
 		}
 		pos = unit.tilePos;
-		off = unit.Offset;
+		int off = unit.Offset;
 		//
 		// Transporter (un)docking?
 		//
 		// FIXME: This is an ugly hack
-		if (unit.Type->CanTransport() &&
-				((Map.WaterOnMap(off) && Map.CoastOnMap(pos + posd)) ||
-				(Map.CoastOnMap(off) && Map.WaterOnMap(pos + posd)))) {
+		if (unit.Type->CanTransport()
+			&& ((Map.WaterOnMap(off) && Map.CoastOnMap(pos + posd))
+				|| (Map.CoastOnMap(off) && Map.WaterOnMap(pos + posd)))) {
 			PlayUnitSound(unit, VoiceDocking);
 		}
 
@@ -142,12 +159,11 @@ int DoActionMove(CUnit &unit)
 	} else {
 		posd.x = Heading2X[unit.Direction / NextDirection];
 		posd.y = Heading2Y[unit.Direction / NextDirection];
-		d = unit.CurrentOrder()->Data.Move.Length + 1;
+		d = order.Data.Move.Length + 1;
 	}
 
-	unit.CurrentOrder()->Data.Move.Cycles++;//reset have to be manualy controled by caller.
-	move = UnitShowAnimationScaled(unit, unit.Type->Animations->Move,
-			Map.Field(unit.Offset)->Cost);
+	order.Data.Move.Cycles++;//reset have to be manualy controled by caller.
+	int move = UnitShowAnimationScaled(unit, unit.Type->Animations->Move, Map.Field(unit.Offset)->Cost);
 
 	unit.IX += posd.x * move;
 	unit.IY += posd.y * move;
@@ -161,58 +177,37 @@ int DoActionMove(CUnit &unit)
 	return d;
 }
 
-/**
-**  Unit move action:
-**
-**  Move to a place or to a unit (can move).
-**  Tries 10x to reach the target, note this are the complete tries.
-**  If the target entered another unit, move to it's position.
-**  If the target unit is destroyed, continue to move to it's last position.
-**
-**  @param unit  Pointer to unit.
-*/
-void HandleActionMove(COrder& order, CUnit &unit)
-{
-	CUnit *goal;
 
+/* virtual */ bool COrder_Move::Execute(CUnit &unit)
+{
 	Assert(unit.CanMove());
 
 	if (unit.Wait) {
 		unit.Wait--;
-		return;
+		return false;
 	}
-
 	// FIXME: (mr-russ) Make a reachable goal here with GoalReachable ...
 
 	switch (DoActionMove(unit)) { // reached end-point?
 		case PF_UNREACHABLE:
-			//
 			// Some tries to reach the goal
-			//
-			if (order.CheckRange()) {
-				order.Range++;
+			if (this->CheckRange()) {
+				this->Range++;
 				break;
 			}
 			// FALL THROUGH
 		case PF_REACHED:
-			// Release target, if any.
-			order.ClearGoal();
-			unit.ClearAction();
-			return;
-
+			return true;
 		default:
 			break;
 	}
+	return false;
+}
 
-	//
-	// Target destroyed?
-	//
-	goal = order.GetGoal();
-	if (goal && goal->Destroyed) {
-		DebugPrint("Goal dead\n");
-		order.goalPos = goal->tilePos + goal->Type->GetHalfTileSize();
-		order.ClearGoal();
-		order.NewResetPath();
+void HandleActionMove(COrder& order, CUnit &unit)
+{
+	if (order.Execute(unit)) {
+		unit.ClearAction();
 	}
 }
 
diff --git a/src/action/action_unload.cpp b/src/action/action_unload.cpp
index cd52b1f84..611181ea1 100644
--- a/src/action/action_unload.cpp
+++ b/src/action/action_unload.cpp
@@ -63,7 +63,7 @@
 		file.printf(" \"goal\", \"%s\",", UnitReference(goal).c_str());
 	}
 	file.printf(" \"tile\", {%d, %d}, ", this->goalPos.x, this->goalPos.y);
-	file.printf("\"state\", %d,\n  ");
+	file.printf("\"state\", %d,\n  ", this->State);
 	SaveDataMove(file);
 	file.printf("}");
 }
diff --git a/src/action/actions.cpp b/src/action/actions.cpp
index 15159991a..a66f221a4 100644
--- a/src/action/actions.cpp
+++ b/src/action/actions.cpp
@@ -192,7 +192,7 @@ unsigned SyncHash; /// Hash calculated to find sync failures
 
 /* static */ COrder* COrder::NewActionFollow(CUnit &dest)
 {
-	COrder *order = new COrder(UnitActionFollow);
+	COrder_Follow *order = new COrder_Follow;
 
 	// Destination could be killed.
 	// Should be handled in action, but is not possible!
@@ -210,7 +210,9 @@ unsigned SyncHash; /// Hash calculated to find sync failures
 
 /* static */ COrder* COrder::NewActionMove(const Vec2i &pos)
 {
-	COrder *order = new COrder(UnitActionMove);
+	Assert(Map.Info.IsPointOnMap(pos));
+
+	COrder_Move *order = new COrder_Move;
 
 	order->goalPos = pos;
 
diff --git a/src/include/actions.h b/src/include/actions.h
index f6abac63f..3c793a224 100644
--- a/src/include/actions.h
+++ b/src/include/actions.h
@@ -171,11 +171,8 @@ public:
 #if 1 // currently needed for parsing
 	static COrder* NewActionAttack() { return new COrder(UnitActionAttack); }
 	static COrder* NewActionAttackGround() { return new COrder(UnitActionAttackGround); }
-	static COrder* NewActionFollow() { return new COrder(UnitActionFollow); }
-	static COrder* NewActionMove() { return new COrder(UnitActionMove); }
 	static COrder* NewActionResource() { return new COrder(UnitActionResource); }
 	static COrder* NewActionReturnGoods() { return new COrder(UnitActionReturnGoods); }
-	static COrder* NewActionUnload() { return new COrder(UnitActionUnload); }
 #endif
 
 private:
@@ -200,7 +197,6 @@ public:
 
 	union {
 		int Attack;
-		int Follow;
 		int Res;
 	} SubAction;
 
@@ -319,6 +315,35 @@ public:
 	virtual bool Execute(CUnit &unit);
 };
 
+class COrder_Follow : public COrder
+{
+public:
+	COrder_Follow() : COrder(UnitActionFollow), State(0) {}
+
+	virtual COrder_Follow *Clone() const { return new COrder_Follow(*this); }
+
+	virtual void Save(CFile &file, const CUnit &unit) const;
+	virtual bool ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit);
+
+	virtual bool Execute(CUnit &unit);
+private:
+	unsigned int State;
+};
+
+class COrder_Move : public COrder
+{
+public:
+	COrder_Move() : COrder(UnitActionMove) {}
+
+	virtual COrder_Move *Clone() const { return new COrder_Move(*this); }
+
+	virtual void Save(CFile &file, const CUnit &unit) const;
+	virtual bool ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit);
+
+	virtual bool Execute(CUnit &unit);
+};
+
+
 
 class COrder_Patrol : public COrder
 {
diff --git a/src/unit/script_unit.cpp b/src/unit/script_unit.cpp
index fea37faaf..c5202b91a 100644
--- a/src/unit/script_unit.cpp
+++ b/src/unit/script_unit.cpp
@@ -257,7 +257,7 @@ bool COrder::ParseSpecificData(lua_State *l, int &j, const char *value, const CU
 	} else if (!strcmp(value, "subaction")) {
 		++j;
 		lua_rawgeti(l, -1, j + 1);
-		this->SubAction.Attack = this->SubAction.Follow = this->SubAction.Res = LuaToNumber(l, -1);
+		this->SubAction.Attack = this->SubAction.Res = LuaToNumber(l, -1);
 		lua_pop(l, 1);
 	} else if (!strcmp(value, "current-resource")) {
 		++j;
@@ -335,9 +335,9 @@ void CclParseOrder(lua_State *l, const CUnit &unit, COrderPtr *orderPtr)
 	} else if (!strcmp(actiontype, "action-stand-ground")) {
 		*orderPtr = new COrder_Still(true);
 	} else if (!strcmp(actiontype, "action-follow")) {
-		*orderPtr = COrder::NewActionFollow();
+		*orderPtr = new COrder_Follow;
 	} else if (!strcmp(actiontype, "action-move")) {
-		*orderPtr = COrder::NewActionMove();
+		*orderPtr = new COrder_Move;
 	} else if (!strcmp(actiontype, "action-attack")) {
 		*orderPtr = COrder::NewActionAttack();
 	} else if (!strcmp(actiontype, "action-attack-ground")) {
@@ -357,7 +357,7 @@ void CclParseOrder(lua_State *l, const CUnit &unit, COrderPtr *orderPtr)
 	} else if (!strcmp(actiontype, "action-board")) {
 		*orderPtr = new COrder_Board;
 	} else if (!strcmp(actiontype, "action-unload")) {
-		*orderPtr = COrder::NewActionUnload();
+		*orderPtr = new COrder_Unload;
 	} else if (!strcmp(actiontype, "action-patrol")) {
 		*orderPtr = new COrder_Patrol;
 	} else if (!strcmp(actiontype, "action-build")) {
diff --git a/src/unit/unit_save.cpp b/src/unit/unit_save.cpp
index 15eb222aa..5dee3daa6 100644
--- a/src/unit/unit_save.cpp
+++ b/src/unit/unit_save.cpp
@@ -96,12 +96,6 @@ void SaveOrder(const COrder &order, const CUnit &unit, CFile *file)
 		case UnitActionNone:
 			file.printf("\"action-none\",");
 			break;
-		case UnitActionFollow:
-			file.printf("\"action-follow\",");
-			break;
-		case UnitActionMove:
-			file.printf("\"action-move\",");
-			break;
 		case UnitActionAttack:
 			file.printf("\"action-attack\",");
 			break;
@@ -138,9 +132,6 @@ void SaveOrder(const COrder &order, const CUnit &unit, CFile *file)
 		case UnitActionAttackGround:
 			file.printf(", \"subaction\", %d", order.SubAction.Attack);
 			break;
-		case UnitActionFollow:
-			file.printf(", \"subaction\", %d", order.SubAction.Follow);
-			break;
 		case UnitActionResource:
 		case UnitActionReturnGoods:
 			file.printf(", \"subaction\", %d", order.SubAction.Res);