From d3c2041d2737ba61c9d52be5112b356250b5a4e5 Mon Sep 17 00:00:00 2001
From: joris <joris.dauphin@gmail.com>
Date: Sat, 25 Feb 2012 12:41:53 +0100
Subject: [PATCH] COrder_Attack COrder is now abstract

---
 src/action/action_attack.cpp | 282 ++++++++++++++++++-----------------
 src/action/actions.cpp       | 162 +-------------------
 src/include/actions.h        |  58 +++----
 src/unit/script_unit.cpp     |  67 ++++-----
 src/unit/unit_draw.cpp       |   7 +-
 src/unit/unit_save.cpp       |  66 --------
 6 files changed, 212 insertions(+), 430 deletions(-)

diff --git a/src/action/action_attack.cpp b/src/action/action_attack.cpp
index 28f7617ae..23a623cd7 100644
--- a/src/action/action_attack.cpp
+++ b/src/action/action_attack.cpp
@@ -52,6 +52,8 @@
 #include "sound.h"
 #include "map.h"
 #include "pathfinder.h"
+#include "script.h"
+#include "iolib.h"
 
 /*----------------------------------------------------------------------------
 --  Defines
@@ -85,6 +87,59 @@ void AnimateActionAttack(CUnit &unit)
 	UnitShowAnimation(unit, unit.Type->Animations->Attack);
 }
 
+
+/* virtual */ void COrder_Attack::Save(CFile &file, const CUnit &unit) const
+{
+	Assert(Action == UnitActionAttack || Action == UnitActionAttackGround);
+
+	if (Action == UnitActionAttack) {
+		file.printf("{\"action-attack\",");
+	} else {
+		file.printf("{\"action-attack-ground\",");
+	}
+	file.printf(" \"range\", %d,", this->Range);
+	file.printf(" \"min-range\", %d,", this->MinRange);
+
+	if (this->Finished) {
+		file.printf(" \"finished\", ");
+	}
+	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,\n  ", this->State);
+	SaveDataMove(file);
+	file.printf("}");
+}
+
+
+/* virtual */ bool COrder_Attack::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit)
+{
+	if (this->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;
+}
+
+bool COrder_Attack::IsWeakTargetSelected() const
+{
+	return (this->State & WEAK_TARGET) != 0;
+}
+
 /**
 **  Check for dead goal.
 **
@@ -95,34 +150,29 @@ void AnimateActionAttack(CUnit &unit)
 **
 **  @param unit  Unit using the goal.
 **
-**  @return      1 if order have changed, 0 else.
+**  @return      true if order have changed, false else.
 */
-static int CheckForDeadGoal(CUnit &unit)
+bool COrder_Attack::CheckForDeadGoal(CUnit &unit)
 {
-	COrderPtr order = unit.CurrentOrder();
-	CUnit *goal = order->GetGoal();
+	CUnit *goal = this->GetGoal();
 
 	// Position or valid target, it is ok.
 	if (!goal || goal->IsVisibleAsGoal(*unit.Player)) {
-		return 0;
+		return false;
 	}
 
 	// Goal could be destroyed or unseen
 	// So, cannot use type.
-	order->goalPos = goal->tilePos;
-	order->MinRange = 0;
-	order->Range = 0;
-	order->ClearGoal();
+	this->goalPos = goal->tilePos;
+	this->MinRange = 0;
+	this->Range = 0;
+	this->ClearGoal();
 
-	//
 	// If we have a saved order continue this saved order.
-	//
 	if (unit.RestoreOrder()) {
-		//unit.ClearAction();
-		return 1;
+		return true;
 	}
-//	NewResetPath(unit); // Should be useless.
-	return 0;
+	return false;
 }
 
 /**
@@ -130,58 +180,55 @@ static int CheckForDeadGoal(CUnit &unit)
 **
 **  @param unit  Unit to check if goal is in range
 **
-**  @return      1 if order(action) have changed, 0 else (if goal change retrun 0).
+**  @return      true if order(action) have changed, false else (if goal change return false).
 */
-static int CheckForTargetInRange(CUnit &unit)
+bool COrder_Attack::CheckForTargetInRange(CUnit &unit)
 {
 	// Target is dead?
 	if (CheckForDeadGoal(unit)) {
-		return 1;
+		return true;
 	}
-	COrderPtr order = unit.CurrentOrder();
 
 	// No goal: if meeting enemy attack it.
-	if (!order->HasGoal() &&
-		order->Action != UnitActionAttackGround &&
-		!Map.WallOnMap(order->goalPos)) {
+	if (!this->HasGoal()
+		&& this->Action != UnitActionAttackGround
+		&& !Map.WallOnMap(this->goalPos)) {
 		CUnit *goal = AttackUnitsInReactRange(unit);
 
 		if (goal) {
-			COrder *savedOrder = COrder::NewActionAttack(unit, order->goalPos);
+			COrder *savedOrder = COrder::NewActionAttack(unit, this->goalPos);
 
 			if (unit.StoreOrder(savedOrder) == false) {
 				delete savedOrder;
 				savedOrder = NULL;
 			}
-			order->SetGoal(goal);
-			order->MinRange = unit.Type->MinAttackRange;
-			order->Range = unit.Stats->Variables[ATTACKRANGE_INDEX].Max;
-			order->goalPos.x = order->goalPos.y = -1;
-			order->SubAction.Attack |= WEAK_TARGET; // weak target
-			order->NewResetPath();
+			this->SetGoal(goal);
+			this->MinRange = unit.Type->MinAttackRange;
+			this->Range = unit.Stats->Variables[ATTACKRANGE_INDEX].Max;
+			this->goalPos.x = this->goalPos.y = -1;
+			this->State |= WEAK_TARGET; // weak target
+			this->NewResetPath();
 		}
 	// Have a weak target, try a better target.
-	} else if (order->HasGoal() && (order->SubAction.Attack & WEAK_TARGET)) {
-		CUnit *goal = order->GetGoal();
-		CUnit *temp = AttackUnitsInReactRange(unit);
+	} else if (this->HasGoal() && (this->State & WEAK_TARGET)) {
+		CUnit *goal = this->GetGoal();
+		CUnit *newTarget = AttackUnitsInReactRange(unit);
 
-		if (temp && temp->Type->Priority > goal->Type->Priority) {
-			COrder *savedOrder = order->Clone();
+		if (newTarget && newTarget->Type->Priority > goal->Type->Priority) {
+			COrder *savedOrder = this->Clone();
 
 			if (unit.StoreOrder(savedOrder) == false) {
 				delete savedOrder;
 				savedOrder = NULL;
 			}
-			order->SetGoal(temp);
-			order->goalPos.x = order->goalPos.y = -1;
-			order->NewResetPath();
+			this->SetGoal(newTarget);
+			this->goalPos.x = this->goalPos.y = -1;
+			this->NewResetPath();
 		}
 	}
 
 	Assert(!unit.Type->Vanishes && !unit.Destroyed && !unit.Removed);
-	Assert(order->Action == UnitActionAttack || order->Action == UnitActionAttackGround);
-
-	return 0;
+	return false;
 }
 
 /**
@@ -189,13 +236,12 @@ static int CheckForTargetInRange(CUnit &unit)
 **
 **  @param unit  Unit that is attacking and moving
 */
-static void MoveToTarget(CUnit &unit)
+void COrder_Attack::MoveToTarget(CUnit &unit)
 {
 	Assert(!unit.Type->Vanishes && !unit.Destroyed && !unit.Removed);
-	Assert(unit.CurrentAction() == UnitActionAttack || unit.CurrentAction() == UnitActionAttackGround);
+	Assert(unit.CurrentOrder() == this);
 	Assert(unit.CanMove());
-	Assert(unit.CurrentOrder()->HasGoal()
-		 || (unit.CurrentOrder()->goalPos.x != -1 && unit.CurrentOrder()->goalPos.y != -1));
+	Assert(this->HasGoal() || Map.Info.IsPointOnMap(this->goalPos));
 
 	int err = DoActionMove(unit);
 
@@ -203,14 +249,10 @@ static void MoveToTarget(CUnit &unit)
 		return;
 	}
 
-	//
 	// Look if we have reached the target.
-	//
-	COrderPtr order = unit.CurrentOrder();
-	if (err == 0 && !order->HasGoal()) {
+	if (err == 0 && !this->HasGoal()) {
 		// Check if we're in range when attacking a location and we are waiting
-		if (unit.MapDistanceTo(order->goalPos.x, order->goalPos.y) <=
-				unit.Stats->Variables[ATTACKRANGE_INDEX].Max) {
+		if (unit.MapDistanceTo(this->goalPos.x, this->goalPos.y) <= unit.Stats->Variables[ATTACKRANGE_INDEX].Max) {
 			err = PF_REACHED;
 		}
 	}
@@ -221,58 +263,48 @@ static void MoveToTarget(CUnit &unit)
 		return;
 	}
 	if (err == PF_REACHED) {
-		CUnit *goal = order->GetGoal();
-		//
+		CUnit *goal = this->GetGoal();
 		// Have reached target? FIXME: could use the new return code?
-		//
 		if (goal
 		&& unit.MapDistanceTo(*goal) <= unit.Stats->Variables[ATTACKRANGE_INDEX].Max) {
 			// Reached another unit, now attacking it
 			unit.State = 0;
 			const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos;
 			UnitHeadingFromDeltaXY(unit, dir);
-			order->SubAction.Attack++;
+			this->State++;
 			return;
 		}
-		//
 		// Attacking wall or ground.
-		//
-		if (!goal && (Map.WallOnMap(order->goalPos) ||
-					order->Action == UnitActionAttackGround) &&
-				unit.MapDistanceTo(order->goalPos.x, order->goalPos.y) <=
-					unit.Stats->Variables[ATTACKRANGE_INDEX].Max) {
+		if (!goal && (Map.WallOnMap(this->goalPos) || this->Action == UnitActionAttackGround)
+			&& unit.MapDistanceTo(this->goalPos.x, this->goalPos.y) <= unit.Stats->Variables[ATTACKRANGE_INDEX].Max) {
 			// Reached wall or ground, now attacking it
 			unit.State = 0;
-			UnitHeadingFromDeltaXY(unit, order->goalPos - unit.tilePos);
-			order->SubAction.Attack &= WEAK_TARGET;
-			order->SubAction.Attack |= ATTACK_TARGET;
+			UnitHeadingFromDeltaXY(unit, this->goalPos - unit.tilePos);
+			this->State &= WEAK_TARGET;
+			this->State |= ATTACK_TARGET;
 			return;
 		}
 	}
-	//
 	// Unreachable.
-	//
+
 	if (err == PF_UNREACHABLE) {
 		unit.State = 0;
-		if (!order->HasGoal()) {
-			//
+		if (!this->HasGoal()) {
 			// When attack-moving we have to allow a bigger range
-			//
-			if (order->CheckRange()) {
+			if (this->CheckRange()) {
 				// Try again with more range
-				order->Range++;
+				this->Range++;
 				unit.Wait = 5;
 				return;
 			}
 		} else {
-			order->ClearGoal();
+			this->ClearGoal();
 		}
 	}
-	//
+
 	// Return to old task?
-	//
 	if (!unit.RestoreOrder()) {
-		unit.ClearAction();
+		this->Finished = true;
 		unit.State = 0;
 	}
 }
@@ -282,34 +314,26 @@ static void MoveToTarget(CUnit &unit)
 **
 **  @param unit  Unit, for that the attack is handled.
 */
-static void AttackTarget(CUnit &unit)
+void COrder_Attack::AttackTarget(CUnit &unit)
 {
-	Assert(unit.CurrentOrder()->HasGoal() ||
-		(unit.CurrentOrder()->goalPos.x != -1 && unit.CurrentOrder()->goalPos.y != -1));
+	Assert(this->HasGoal() || Map.Info.IsPointOnMap(this->goalPos));
 
 	AnimateActionAttack(unit);
 	if (unit.Anim.Unbreakable) {
 		return;
 	}
-	//
-	// Goal is "weak" or a wall.
-	//
-	COrderPtr order = unit.CurrentOrder();
-	if (!order->HasGoal() && (order->Action == UnitActionAttackGround || Map.WallOnMap(order->goalPos))) {
+
+	if (!this->HasGoal() && (this->Action == UnitActionAttackGround || Map.WallOnMap(this->goalPos))) {
 		return;
 	}
 
-	//
 	// Target is dead ? Change order ?
-	//
 	if (CheckForDeadGoal(unit)) {
 		return;
 	}
-	CUnit *goal = order->GetGoal();
+	CUnit *goal = this->GetGoal();
 
-	//
 	// No target choose one.
-	//
 	if (!goal) {
 		unit.State = 0;
 		goal = AttackUnitsInReactRange(unit);
@@ -320,71 +344,65 @@ static void AttackTarget(CUnit &unit)
 			if (unit.RestoreOrder()) {
 				return;
 			}
-			order->SubAction.Attack = MOVE_TO_TARGET;
+			this->State = MOVE_TO_TARGET;
 			return;
 		}
 		// Save current command to come back.
-		COrder *savedOrder = order->Clone();
+		COrder *savedOrder = this->Clone();
 
 		if (unit.StoreOrder(savedOrder) == false) {
 			delete savedOrder;
 			savedOrder = NULL;
 		}
-		order->SetGoal(goal);
-		order->goalPos.x = order->goalPos.y = -1;
-		order->MinRange = unit.Type->MinAttackRange;
-		order->Range = unit.Stats->Variables[ATTACKRANGE_INDEX].Max;
-		order->NewResetPath();
-		order->SubAction.Attack |= WEAK_TARGET;
+		this->SetGoal(goal);
+		this->goalPos.x = this->goalPos.y = -1;
+		this->MinRange = unit.Type->MinAttackRange;
+		this->Range = unit.Stats->Variables[ATTACKRANGE_INDEX].Max;
+		this->NewResetPath();
+		this->State |= WEAK_TARGET;
 
-	//
 	// Have a weak target, try a better target.
 	// FIXME: if out of range also try another target quick
-	//
 	} else {
-		if ((order->SubAction.Attack & WEAK_TARGET)) {
-			CUnit *temp = AttackUnitsInReactRange(unit);
-			if (temp && temp->Type->Priority > goal->Type->Priority) {
-				COrder *savedOrder = order->Clone();
+		if ((this->State & WEAK_TARGET)) {
+			CUnit *newTarget = AttackUnitsInReactRange(unit);
+			if (newTarget && newTarget->Type->Priority > goal->Type->Priority) {
+				COrder *savedOrder = this->Clone();
 
 				if (unit.StoreOrder(savedOrder) == false) {
 					delete savedOrder;
 					savedOrder = NULL;
 				}
-				goal = temp;
-				order->SetGoal(temp);
-				order->goalPos.x = order->goalPos.y = -1;
-				order->MinRange = unit.Type->MinAttackRange;
-				order->SubAction.Attack = MOVE_TO_TARGET;
-				order->NewResetPath();
+				goal = newTarget;
+				this->SetGoal(newTarget);
+				this->goalPos.x = this->goalPos.y = -1;
+				this->MinRange = unit.Type->MinAttackRange;
+				this->State = MOVE_TO_TARGET;
+				this->NewResetPath();
 			}
 		}
 	}
 
-	//
 	// Still near to target, if not goto target.
-	//
-	int dist = unit.MapDistanceTo(*goal);
+	const int dist = unit.MapDistanceTo(*goal);
 	if (dist > unit.Stats->Variables[ATTACKRANGE_INDEX].Max) {
-		COrder *savedOrder = order->Clone();
+		COrder *savedOrder = this->Clone();
 
 		if (unit.StoreOrder(savedOrder) == false) {
 			delete savedOrder;
 			savedOrder = NULL;
 		}
-		order->NewResetPath();
+		this->NewResetPath();
 		unit.Frame = 0;
 		unit.State = 0;
-		order->SubAction.Attack &= WEAK_TARGET;
-		order->SubAction.Attack |= MOVE_TO_TARGET;
+		this->State &= WEAK_TARGET;
+		this->State |= MOVE_TO_TARGET;
 	}
 	if (dist < unit.Type->MinAttackRange) {
-		order->SubAction.Attack = MOVE_TO_TARGET;
+		this->State = MOVE_TO_TARGET;
 	}
 
-	//
 	// Turn always to target
-	//
 	if (goal) {
 		const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos;
 		UnitHeadingFromDeltaXY(unit, dir);
@@ -394,9 +412,8 @@ static void AttackTarget(CUnit &unit)
 /**
 **  Unit attacks!
 **
-**  I added a little trick, if SubAction&WEAK_TARGET is true the goal is
-**  a weak goal.  This means the unit AI (little AI) could choose a new
-**  better goal.
+**  if (SubAction & WEAK_TARGET) is true the goal is a weak goal.
+**  This means the unit AI (little AI) could choose a new better goal.
 **
 **  @todo  Lets do some tries to reach the target.
 **         If target place is not reachable, choose better goal to reduce
@@ -404,26 +421,24 @@ static void AttackTarget(CUnit &unit)
 **
 **  @param unit  Unit, for that the attack is handled.
 */
-void HandleActionAttack(COrder& order, CUnit &unit)
+/* virtual */ void COrder_Attack::Execute(CUnit &unit)
 {
-	Assert(order.Action == UnitActionAttackGround || order.Action == UnitActionAttack);
-	Assert(order.HasGoal() || Map.Info.IsPointOnMap(order.goalPos));
+	Assert(this->HasGoal() || Map.Info.IsPointOnMap(this->goalPos));
 
 	if (unit.Wait) {
 		unit.Wait--;
 		return;
 	}
 
-	switch (order.SubAction.Attack) {
-		case 0: // First entry
-		{
+	switch (this->State) {
+		case 0: { // First entry
 			// did Order change ?
 			if (CheckForTargetInRange(unit)) {
 				return;
 			}
 			// Can we already attack ?
-			if (order.HasGoal()) {
-				CUnit &goal = *order.GetGoal();
+			if (this->HasGoal()) {
+				CUnit &goal = *this->GetGoal();
 				const int dist = goal.MapDistanceTo(unit);
 
 				if (unit.Type->MinAttackRange < dist &&
@@ -431,13 +446,13 @@ void HandleActionAttack(COrder& order, CUnit &unit)
 					const Vec2i dir = goal.tilePos + goal.Type->GetHalfTileSize() - unit.tilePos;
 
 					UnitHeadingFromDeltaXY(unit, dir);
-					order.SubAction.Attack = ATTACK_TARGET;
+					this->State = ATTACK_TARGET;
 					AttackTarget(unit);
 					return;
 				}
 			}
-			order.SubAction.Attack = MOVE_TO_TARGET;
-			order.NewResetPath();
+			this->State = MOVE_TO_TARGET;
+			this->NewResetPath();
 			// FIXME: should use a reachable place to reduce pathfinder time.
 			Assert(unit.State == 0);
 		}
@@ -446,7 +461,7 @@ void HandleActionAttack(COrder& order, CUnit &unit)
 		case MOVE_TO_TARGET + WEAK_TARGET:
 			if (!unit.CanMove()) {
 				if (!unit.RestoreOrder()) {
-					unit.ClearAction();
+					this->Finished = true;
 					unit.State = 0;
 				}
 				return;
@@ -454,9 +469,6 @@ void HandleActionAttack(COrder& order, CUnit &unit)
 			MoveToTarget(unit);
 			break;
 
-		//
-		// Attack the target.
-		//
 		case ATTACK_TARGET:
 		case ATTACK_TARGET + WEAK_TARGET:
 			AttackTarget(unit);
diff --git a/src/action/actions.cpp b/src/action/actions.cpp
index a699e67c4..0384458bb 100644
--- a/src/action/actions.cpp
+++ b/src/action/actions.cpp
@@ -88,7 +88,7 @@ unsigned SyncHash; /// Hash calculated to find sync failures
 
 /* static */ COrder* COrder::NewActionAttack(const CUnit &attacker, CUnit &target)
 {
-	COrder *order = new COrder(UnitActionAttack);
+	COrder_Attack *order = new COrder_Attack(false);
 
 	if (target.Destroyed) {
 		order->goalPos = target.tilePos + target.Type->GetHalfTileSize();
@@ -105,7 +105,7 @@ unsigned SyncHash; /// Hash calculated to find sync failures
 {
 	Assert(Map.Info.IsPointOnMap(dest));
 
-	COrder *order = new COrder(UnitActionAttack);
+	COrder_Attack *order = new COrder_Attack(false);
 
 	if (Map.WallOnMap(dest) && Map.IsFieldExplored(*attacker.Player, dest)) {
 		// FIXME: look into action_attack.cpp about this ugly problem
@@ -120,7 +120,7 @@ unsigned SyncHash; /// Hash calculated to find sync failures
 
 /* static */ COrder* COrder::NewActionAttackGround(const CUnit &attacker, const Vec2i &dest)
 {
-	COrder *order = new COrder(UnitActionAttackGround);
+	COrder_Attack *order = new COrder_Attack(true);
 
 	order->goalPos = dest;
 	order->Range = attacker.Stats->Variables[ATTACKRANGE_INDEX].Max;
@@ -409,20 +409,6 @@ unsigned SyncHash; /// Hash calculated to find sync failures
 	return order;
 }
 
-COrder* COrder::Clone() const
-{
-	COrder *clone = new COrder(this->Action);
-
-	clone->Range = this->Range;
-	clone->MinRange = this->MinRange;
-	clone->Width = this->Width;
-	clone->Height = this->Height;
-	clone->SetGoal(this->Goal);
-	clone->goalPos = this->goalPos;
-	memcpy(&clone->Data, &this->Data, sizeof (clone->Data));
-	return clone;
-}
-
 COrder::~COrder()
 {
 	if (Goal) {
@@ -1254,140 +1240,6 @@ int UnitShowAnimationScaled(CUnit &unit, const CAnimation *anim, int scale)
 --  Actions
 ----------------------------------------------------------------------------*/
 
-/**
-**  Unit does nothing!
-**
-**  @param unit  Unit pointer for none action.
-*/
-static void HandleActionNone(COrder&, CUnit &unit)
-{
-	DebugPrint("FIXME: Should not happen!\n");
-	DebugPrint("FIXME: Unit (%d) %s has action none.!\n" _C_
-		UnitNumber(unit) _C_ unit.Type->Ident.c_str());
-}
-
-/**
-**  Unit has not written function.
-**
-**  @param unit  Unit pointer for not written action.
-*/
-static void HandleActionNotWritten(COrder&, CUnit &unit)
-{
-	DebugPrint("FIXME: Not written!\n");
-	DebugPrint("FIXME: Unit (%d) %s has action %d.!\n" _C_
-		UnitNumber(unit) _C_ unit.Type->Ident.c_str() _C_ unit.CurrentAction());
-}
-
-/**
-**  Jump table for actions.
-**
-**  @note can move function into unit structure.
-*/
-static void (*HandleActionTable[256])(COrder&, CUnit &) = {
-	HandleActionNone,
-	HandleActionNone, // HandleActionStill,
-	HandleActionNone, // HandleActionStandGround,
-	HandleActionNone, // HandleActionFollow,
-	HandleActionNone, // HandleActionMove,
-	HandleActionAttack,
-	HandleActionAttack, // HandleActionAttackGround,
-	HandleActionNone, // HandleActionDie,
-	HandleActionNone, // HandleActionSpellCast,
-	HandleActionNone, // HandleActionTrain,
-	HandleActionNone, // HandleActionUpgradeTo,
-	HandleActionNone, // HandleActionResearch,
-	HandleActionNone, // HandleActionBuilt,
-	HandleActionNone, // HandleActionBoard,
-	HandleActionNone, // HandleActionUnload,
-	HandleActionNone, // HandleActionPatrol,
-	HandleActionNone, // HandleActionBuild,
-	HandleActionNone, // HandleActionRepair,
-	HandleActionNone, // HandleActionResource,
-	HandleActionNone, // HandleActionReturnGoods,
-	HandleActionNone, // HandleActionTransformInto,
-	HandleActionNotWritten,
-
-	// Enough for the future ?
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-	HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
-};
-
 /**
 **  Increment a unit's health
 **
@@ -1469,14 +1321,6 @@ static void HandleBuffs(CUnit &unit, int amount)
 }
 
 
-
-
-void COrder::Execute(CUnit &unit)
-{
-	HandleActionTable[Action](*this, unit);
-}
-
-
 /**
 **  Handle the action of a unit.
 **
diff --git a/src/include/actions.h b/src/include/actions.h
index 77ce35a92..3ce0adc57 100644
--- a/src/include/actions.h
+++ b/src/include/actions.h
@@ -83,7 +83,6 @@ enum UnitAction {
 
 	UnitActionRepair,       /// unit repairing
 	UnitActionResource,     /// unit harvesting resources
-	UnitActionDummy, // UnitActionReturnGoods,  /// unit returning any resource
 	UnitActionTransformInto /// unit transform into type.
 };
 
@@ -101,26 +100,24 @@ struct lua_State;
 */
 class COrder
 {
-
 public:
 	COrder(int action) : Goal(NULL), Range(0), MinRange(0), Width(0),
 		Height(0), Action(action), Finished(false)
 	{
 		goalPos.x = -1;
 		goalPos.y = -1;
-		memset(&SubAction, 0, sizeof (SubAction));
 		memset(&Data, 0, sizeof (Data));
 	}
 	virtual ~COrder();
 
-	virtual COrder *Clone() const;
-	virtual void Execute(CUnit &unit);
+	virtual COrder *Clone() const = 0;
+	virtual void Execute(CUnit &unit) = 0;
 	virtual void Cancel(CUnit &unit) {}
 	virtual void OnAnimationAttack(CUnit &unit);
 
-	virtual void Save(CFile &file, const CUnit &unit) const;
+	virtual void Save(CFile &file, const CUnit &unit) const = 0;
 	bool ParseGenericData(lua_State *l, int &j, const char *value);
-	virtual bool ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit);
+	virtual bool ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit) = 0;
 
 	virtual void UpdateUnitVariables(CUnit &unit) const {}
 	virtual void FillSeenValues(CUnit &unit) const;
@@ -143,7 +140,6 @@ public:
 
 	bool OnAiHitUnit(CUnit &unit, CUnit *attacker, int /*damage*/);
 
-
 	static COrder* NewActionAttack(const CUnit &attacker, CUnit &target);
 	static COrder* NewActionAttack(const CUnit &attacker, const Vec2i &dest);
 	static COrder* NewActionAttackGround(const CUnit &attacker, const Vec2i &dest);
@@ -168,13 +164,7 @@ public:
 	static COrder* NewActionUnload(const Vec2i &pos, CUnit *what);
 	static COrder* NewActionUpgradeTo(CUnit &unit, CUnitType &type);
 
-#if 1 // currently needed for parsing
-	static COrder* NewActionAttack() { return new COrder(UnitActionAttack); }
-	static COrder* NewActionAttackGround() { return new COrder(UnitActionAttackGround); }
-#endif
-
 private:
-
 	CUnit *Goal;
 public:
 	int Range;              /// How far away
@@ -186,12 +176,7 @@ public:
 
 	Vec2i goalPos;          /// or tile coordinate of destination
 
-	union {
-		int Attack;
-	} SubAction;
-
-
-	union _order_data_ {
+	struct _order_data_ {
 	struct _order_move_ {
 		unsigned short int Cycles;          /// how much Cycles we move.
 		char Fast;                  /// Flag fast move (one step)
@@ -202,6 +187,33 @@ public:
 	} Data; /// Storage room for different commands
 };
 
+class COrder_Attack : public COrder
+{
+	friend COrder* COrder::NewActionAttack(const CUnit &attacker, CUnit &target);
+	friend COrder* COrder::NewActionAttack(const CUnit &attacker, const Vec2i &dest);
+	friend COrder* COrder::NewActionAttackGround(const CUnit &attacker, const Vec2i &dest);
+public:
+	COrder_Attack(bool ground) : COrder(ground ? UnitActionAttackGround : UnitActionAttack), State(0)
+	{}
+
+	virtual COrder_Attack* Clone() const { return new COrder_Attack(*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 void Execute(CUnit &unit);
+
+	bool IsWeakTargetSelected() const;
+
+private:
+	bool CheckForDeadGoal(CUnit &unit);
+	bool CheckForTargetInRange(CUnit &unit);
+	void MoveToTarget(CUnit &unit);
+	void AttackTarget(CUnit &unit);
+
+private:
+	int State;
+};
 
 class COrder_Board : public COrder
 {
@@ -667,12 +679,6 @@ extern int DoActionMove(CUnit &unit);
 	/// Show attack animation
 extern void AnimateActionAttack(CUnit &unit);
 
-
-typedef void HandleActionFunc(COrder& order, CUnit &unit);
-
-	/// Handle command attack
-extern HandleActionFunc HandleActionAttack;
-
 /*----------------------------------------------------------------------------
 --  Actions: actions.c
 ----------------------------------------------------------------------------*/
diff --git a/src/unit/script_unit.cpp b/src/unit/script_unit.cpp
index d45c27b74..1fc72382a 100644
--- a/src/unit/script_unit.cpp
+++ b/src/unit/script_unit.cpp
@@ -221,21 +221,6 @@ bool COrder::ParseMoveData(lua_State *l, int &j, const char *value)
 	return true;
 }
 
-bool COrder::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit)
-{
-	if (this->ParseMoveData(l, j, value)) {
-		return true;
-	} else if (!strcmp(value, "subaction")) {
-		++j;
-		lua_rawgeti(l, -1, j + 1);
-		this->SubAction.Attack = LuaToNumber(l, -1);
-		lua_pop(l, 1);
-	} else {
-		return false;
-	}
-	return true;
-}
-
 /**
 **  Parse order
 **
@@ -250,44 +235,44 @@ void CclParseOrder(lua_State *l, CUnit &unit, COrderPtr *orderPtr)
 	const char *actiontype = LuaToString(l, -1);
 	lua_pop(l, 1);
 
-	if (!strcmp(actiontype, "action-still")) {
-		*orderPtr = new COrder_Still(false);
-	} else if (!strcmp(actiontype, "action-stand-ground")) {
-		*orderPtr = new COrder_Still(true);
+	if (!strcmp(actiontype, "action-attack")) {
+		*orderPtr = new COrder_Attack(false);
+	} else if (!strcmp(actiontype, "action-attack-ground")) {
+		*orderPtr = new COrder_Attack(true);
+	} else if (!strcmp(actiontype, "action-board")) {
+		*orderPtr = new COrder_Board;
+	} else if (!strcmp(actiontype, "action-build")) {
+		*orderPtr = new COrder_Build;
+	} else if (!strcmp(actiontype, "action-built")) {
+		*orderPtr = new COrder_Built;
+	} else if (!strcmp(actiontype, "action-die")) {
+		*orderPtr = new COrder_Die;
 	} else if (!strcmp(actiontype, "action-follow")) {
 		*orderPtr = new COrder_Follow;
 	} else if (!strcmp(actiontype, "action-move")) {
 		*orderPtr = new COrder_Move;
-	} else if (!strcmp(actiontype, "action-attack")) {
-		*orderPtr = COrder::NewActionAttack();
-	} else if (!strcmp(actiontype, "action-attack-ground")) {
-		*orderPtr = COrder::NewActionAttackGround();
-	} else if (!strcmp(actiontype, "action-die")) {
-		*orderPtr = new COrder_Die;
-	} else if (!strcmp(actiontype, "action-spell-cast")) {
-		*orderPtr = new COrder_SpellCast;
-	} else if (!strcmp(actiontype, "action-train")) {
-		*orderPtr = new COrder_Train;
-	} else if (!strcmp(actiontype, "action-upgrade-to")) {
-		*orderPtr = new COrder_UpgradeTo;
-	} else if (!strcmp(actiontype, "action-research")) {
-		*orderPtr = new COrder_Research;
-	} else if (!strcmp(actiontype, "action-built")) {
-		*orderPtr = new COrder_Built;
-	} else if (!strcmp(actiontype, "action-board")) {
-		*orderPtr = new COrder_Board;
-	} else if (!strcmp(actiontype, "action-unload")) {
-		*orderPtr = new COrder_Unload;
 	} else if (!strcmp(actiontype, "action-patrol")) {
 		*orderPtr = new COrder_Patrol;
-	} else if (!strcmp(actiontype, "action-build")) {
-		*orderPtr = new COrder_Build;
 	} else if (!strcmp(actiontype, "action-repair")) {
 		*orderPtr = new COrder_Repair;
+	} else if (!strcmp(actiontype, "action-research")) {
+		*orderPtr = new COrder_Research;
 	} else if (!strcmp(actiontype, "action-resource")) {
 		*orderPtr = new COrder_Resource(unit);
+	} else if (!strcmp(actiontype, "action-spell-cast")) {
+		*orderPtr = new COrder_SpellCast;
+	} else if (!strcmp(actiontype, "action-stand-ground")) {
+		*orderPtr = new COrder_Still(true);
+	} else if (!strcmp(actiontype, "action-still")) {
+		*orderPtr = new COrder_Still(false);
+	} else if (!strcmp(actiontype, "action-train")) {
+		*orderPtr = new COrder_Train;
 	} else if (!strcmp(actiontype, "action-transform-into")) {
 		*orderPtr = new COrder_TransformInto;
+	} else if (!strcmp(actiontype, "action-upgrade-to")) {
+		*orderPtr = new COrder_UpgradeTo;
+	} else if (!strcmp(actiontype, "action-unload")) {
+		*orderPtr = new COrder_Unload;
 	} else {
 		LuaError(l, "ParseOrder: Unsupported type: %s" _C_ actiontype);
 	}
diff --git a/src/unit/unit_draw.cpp b/src/unit/unit_draw.cpp
index b56876ea1..0639f23c2 100644
--- a/src/unit/unit_draw.cpp
+++ b/src/unit/unit_draw.cpp
@@ -756,9 +756,10 @@ static void ShowSingleOrder(const CUnit &unit, const PixelPos &pos, const COrder
 		case UnitActionAttackGround:
 			pos2 = CurrentViewport->TilePosToScreen_Center(order.goalPos);
 			// FALL THROUGH
-		case UnitActionAttack:
-		{
-			if (order.SubAction.Attack & 2) { // Show weak targets.
+		case UnitActionAttack: {
+			const COrder_Attack &orderAttack = static_cast<const COrder_Attack&>(order);
+
+			if (orderAttack.IsWeakTargetSelected()) { // Show weak targets.
 				e_color = ColorBlue;
 			} else {
 				e_color = ColorRed;
diff --git a/src/unit/unit_save.cpp b/src/unit/unit_save.cpp
index d1e1db55b..60d0a2222 100644
--- a/src/unit/unit_save.cpp
+++ b/src/unit/unit_save.cpp
@@ -88,72 +88,6 @@ void SaveOrder(const COrder &order, const CUnit &unit, CFile *file)
 	order.Save(*file, unit);
 }
 
-/* virtual */ void COrder::Save(CFile &file, const CUnit &unit) const
-{
-	const COrder &order = *this;
-	file.printf("{");
-	switch (order.Action) {
-		case UnitActionNone:
-			file.printf("\"action-none\",");
-			break;
-		case UnitActionAttack:
-			file.printf("\"action-attack\",");
-			break;
-		case UnitActionAttackGround:
-			file.printf("\"action-attack-ground\",");
-			break;
-		default:
-			DebugPrint("Unknown action in order\n");
-	}
-
-	file.printf(" \"range\", %d,", order.Range);
-	file.printf(" \"width\", %d,", order.Width);
-	file.printf(" \"height\", %d,", order.Height);
-	file.printf(" \"min-range\", %d,", order.MinRange);
-
-	if (order.Finished) {
-		file.printf(" \"finished\", ");
-	}
-
-	if (order.HasGoal()) {
-		CUnit &goal = *order.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}", order.goalPos.x, order.goalPos.y);
-
-	switch (order.Action) {
-		case UnitActionAttack:
-		case UnitActionAttackGround:
-			file.printf(", \"subaction\", %d", order.SubAction.Attack);
-			break;
-		default:
-			break;
-	}
-
-	//
-	//  Order data part
-	//
-	switch (order.Action) {
-		case UnitActionStill:
-		case UnitActionResource:
-		case UnitActionBuilt:
-		case UnitActionResearch:
-		case UnitActionUpgradeTo:
-		case UnitActionTrain:
-			break;
-		default:
-			file.printf(",\n  ");
-			SaveDataMove(file);
-			break;
-	}
-	file.printf("}");
-}
-
 void COrder::SaveDataMove(CFile &file) const
 {
 	file.printf("\"data-move\", {");