From cba6378cc79d9f53d1dc2b1cb15071b513e8a1d9 Mon Sep 17 00:00:00 2001
From: cybermind <iddqd_mail@mail.ru>
Date: Sat, 19 May 2012 23:25:59 +0600
Subject: [PATCH] [+] Great improvement of AI: - The AI unit will prefer only
 those targets, which have specified set of flags, use new UnitType field
 PriorityTarget (same syntax as in CanTargetFlag) - AI workers prefer to find
 mines near to depots. - More effective control to defence forces, the idle
 units always try to attack back. - Fixed new goal finding to attacking AI
 forces. - Try to prefer buildings as a goal for attacking forces. - Special
 threshold counter which didn't allow AI to change target for some time. -
 Fixed AiHelpMe for non-building units. [-] Fixed incorrect CUnit::TTL
 countdown.

---
 src/action/action_attack.cpp |  49 +++++-----
 src/action/actions.cpp       |   7 +-
 src/ai/ai.cpp                |  15 ++--
 src/ai/ai_force.cpp          | 155 +++++++++++++++++++++++++-------
 src/ai/ai_local.h            |   2 +-
 src/ai/ai_resource.cpp       |   4 +-
 src/include/map.h            |  15 ++++
 src/include/unit.h           |  22 ++++-
 src/include/unittype.h       |   7 +-
 src/ui/mouse.cpp             |   3 +
 src/unit/build.cpp           |   2 +-
 src/unit/script_unit.cpp     |   5 ++
 src/unit/script_unittype.cpp |  30 ++++++-
 src/unit/unit.cpp            | 168 +++++++++++++++++++++++++++--------
 src/unit/unit_draw.cpp       |   2 +-
 src/unit/unit_find.cpp       |  64 +++++++++----
 src/unit/unit_save.cpp       |   1 +
 17 files changed, 419 insertions(+), 132 deletions(-)

diff --git a/src/action/action_attack.cpp b/src/action/action_attack.cpp
index 7bda9c0b8..801931ad0 100644
--- a/src/action/action_attack.cpp
+++ b/src/action/action_attack.cpp
@@ -56,6 +56,7 @@
 #include "ui.h"
 #include "unit.h"
 #include "unittype.h"
+#include "spells.h"
 
 /*----------------------------------------------------------------------------
 --  Defines
@@ -189,7 +190,7 @@ void AnimateActionAttack(CUnit &unit, COrder &order)
 {
 	if (Action == UnitActionAttack) {
 		if (this->HasGoal()) {
-			return this->GetGoal()->IsAlive();
+			return this->GetGoal()->IsAliveOnMap();
 		} else {
 			return Map.Info.IsPointOnMap(this->goalPos);
 		}
@@ -336,23 +337,24 @@ bool COrder_Attack::CheckForTargetInRange(CUnit &unit)
 			this->SetGoal(goal);
 			this->MinRange = unit.Type->MinAttackRange;
 			this->Range = unit.Stats->Variables[ATTACKRANGE_INDEX].Max;
-			this->goalPos.x = this->goalPos.y = -1;
+			this->goalPos = goal->tilePos;
 			this->State |= WEAK_TARGET; // weak target
 		}
 		// Have a weak target, try a better target.
-	} else if (this->HasGoal() && (this->State & WEAK_TARGET)) {
+	} else if (this->HasGoal() && (this->State & WEAK_TARGET || unit.Player->AiEnabled)) {
 		CUnit *goal = this->GetGoal();
 		CUnit *newTarget = AttackUnitsInReactRange(unit);
 
-		if (newTarget && newTarget->Type->Priority > goal->Type->Priority) {
-			COrder *savedOrder = this->Clone();
+		if (newTarget && newTarget->IsAgressive()
+			&& ThreatCalculate(unit, newTarget) < ThreatCalculate(unit, goal)) {
+				COrder *savedOrder = this->Clone();
 
-			if (unit.StoreOrder(savedOrder) == false) {
-				delete savedOrder;
-				savedOrder = NULL;
-			}
-			this->SetGoal(newTarget);
-			this->goalPos.x = this->goalPos.y = -1;
+				if (unit.StoreOrder(savedOrder) == false) {
+					delete savedOrder;
+					savedOrder = NULL;
+				}
+				this->SetGoal(newTarget);
+				this->goalPos = newTarget->tilePos;
 		}
 	}
 
@@ -482,7 +484,7 @@ void COrder_Attack::AttackTarget(CUnit &unit)
 			savedOrder = NULL;
 		}
 		this->SetGoal(goal);
-		this->goalPos.x = this->goalPos.y = -1;
+		this->goalPos = goal->tilePos;
 		this->MinRange = unit.Type->MinAttackRange;
 		this->Range = unit.Stats->Variables[ATTACKRANGE_INDEX].Max;
 		this->State |= WEAK_TARGET;
@@ -492,18 +494,19 @@ void COrder_Attack::AttackTarget(CUnit &unit)
 	} else {
 		if ((this->State & WEAK_TARGET)) {
 			CUnit *newTarget = AttackUnitsInReactRange(unit);
-			if (newTarget && newTarget->Type->Priority > goal->Type->Priority) {
-				COrder *savedOrder = this->Clone();
+			if (newTarget && newTarget->IsAgressive()
+				&& ThreatCalculate(unit, newTarget) < ThreatCalculate(unit, goal)) {
+					COrder *savedOrder = this->Clone();
 
-				if (unit.StoreOrder(savedOrder) == false) {
-					delete savedOrder;
-					savedOrder = NULL;
-				}
-				goal = newTarget;
-				this->SetGoal(newTarget);
-				this->goalPos.x = this->goalPos.y = -1;
-				this->MinRange = unit.Type->MinAttackRange;
-				this->State = MOVE_TO_TARGET;
+					if (unit.StoreOrder(savedOrder) == false) {
+						delete savedOrder;
+						savedOrder = NULL;
+					}
+					goal = newTarget;
+					this->SetGoal(newTarget);
+					this->goalPos = newTarget->tilePos;
+					this->MinRange = unit.Type->MinAttackRange;
+					this->State = MOVE_TO_TARGET;
 			}
 		}
 	}
diff --git a/src/action/actions.cpp b/src/action/actions.cpp
index 404398009..93565cf24 100644
--- a/src/action/actions.cpp
+++ b/src/action/actions.cpp
@@ -274,7 +274,7 @@ static void HandleBuffs(CUnit &unit, int amount)
 {
 	// Look if the time to live is over.
 
-	if (unit.TTL && (int)unit.TTL < ((int)GameCycle - unit.Variable[HP_INDEX].Value)) {
+	if (unit.TTL && unit.TTL < GameCycle) {
 		DebugPrint("Unit must die %lu %lu!\n" _C_ unit.TTL _C_ GameCycle);
 
 		// Hit unit does some funky stuff...
@@ -284,6 +284,11 @@ static void HandleBuffs(CUnit &unit, int amount)
 		}
 	}
 
+	unit.Threshold -= amount;
+	if (unit.Threshold < 0) {
+		unit.Threshold = 0;
+	}
+
 	//  decrease spells effects time.
 	unit.Variable[BLOODLUST_INDEX].Increase = -amount;
 	unit.Variable[HASTE_INDEX].Increase = -amount;
diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp
index bdb160a06..014e27a05 100644
--- a/src/ai/ai.cpp
+++ b/src/ai/ai.cpp
@@ -703,20 +703,19 @@ void AiHelpMe(const CUnit *attacker, CUnit &defender)
 				if (aiunit.CurrentAction() == UnitActionAttack) {
 					COrder_Attack &orderAttack = *static_cast<COrder_Attack *>(aiunit.CurrentOrder());
 
-					if (orderAttack.GetGoal() == NULL || orderAttack.GetGoal()->IsAgressive() == false) {
-						shouldAttack = true;
+					if (orderAttack.GetGoal() == NULL || orderAttack.GetGoal()->IsAgressive() == false
+						|| ThreatCalculate(defender, attacker) < ThreatCalculate(defender, orderAttack.GetGoal())) {
+							shouldAttack = true;
 					}
 				}
 
 				if (shouldAttack) {
 					CommandAttack(aiunit, attacker->tilePos, const_cast<CUnit *>(attacker), FlushCommands);
-					if (aiunit.SavedOrder == NULL) {
-						COrder *savedOrder = COrder::NewActionAttack(aiunit, aiunit.tilePos);
+					COrder *savedOrder = COrder::NewActionAttack(aiunit, attacker->tilePos);
 
-						if (aiunit.StoreOrder(savedOrder) == false) {
-							delete savedOrder;
-							savedOrder = NULL;
-						}
+					if (aiunit.StoreOrder(savedOrder) == false) {
+						delete savedOrder;
+						savedOrder = NULL;
 					}
 				}
 			}
diff --git a/src/ai/ai_force.cpp b/src/ai/ai_force.cpp
index 675357e1d..999ac3f92 100644
--- a/src/ai/ai_force.cpp
+++ b/src/ai/ai_force.cpp
@@ -52,8 +52,11 @@
 /*----------------------------------------------------------------------------
 --  Types
 ----------------------------------------------------------------------------*/
+#define AIATTACK_RANGE 0
+#define AIATTACK_ALLMAP 1
+#define AIATTACK_BUILDING 2
 
-template <const bool IN_REACT_RANGE>
+template <const int FIND_TYPE>
 class AiForceEnemyFinder
 {
 public:
@@ -75,10 +78,16 @@ public:
 		if (unit->Type->CanAttack == false) {
 			return *enemy == NULL;
 		}
-		if (IN_REACT_RANGE) {
+		if (FIND_TYPE == AIATTACK_RANGE) {
 			*enemy = AttackUnitsInReactRange(*unit);
-		} else {
+		} else if (FIND_TYPE == AIATTACK_ALLMAP) {
 			*enemy = AttackUnitsInDistance(*unit, MaxMapWidth);
+		} else if (FIND_TYPE == AIATTACK_BUILDING) {
+			*enemy = AttackUnitsInDistance(*unit, MaxMapWidth, true);
+			Assert(!*enemy || (*enemy)->Type->Building);
+			if (*enemy == NULL) {
+				*enemy = AttackUnitsInDistance(*unit, MaxMapWidth);
+			}
 		}
 		return *enemy == NULL;
 	}
@@ -285,7 +294,7 @@ void AiForce::Attack(const Vec2i &pos)
 	if (Map.Info.IsPointOnMap(goalPos) == false) {
 		/* Search in entire map */
 		const CUnit *enemy = NULL;
-		AiForceEnemyFinder<false>(*this, &enemy);
+		AiForceEnemyFinder<AIATTACK_BUILDING>(*this, &enemy);
 		if (enemy) {
 			goalPos = enemy->tilePos;
 		}
@@ -639,6 +648,8 @@ static void AiGroupAttackerForTransport(AiForce &aiForce)
 */
 void AiForce::Update()
 {
+	bool isTransporterAttack = false;
+
 	if (Size() == 0) {
 		Attacking = false;
 		if (!Defending && State > AiForceAttackingState_Waiting) {
@@ -651,6 +662,9 @@ void AiForce::Update()
 	Attacking = false;
 	for (unsigned int i = 0; i < Size(); ++i) {
 		CUnit *aiunit = Units[i];
+		if (!isTransporterAttack && aiunit->Type->CanTransport()) {
+			isTransporterAttack = true;
+		}
 		if (aiunit->Type->CanAttack) {
 			Attacking = true;
 			break;
@@ -681,39 +695,91 @@ void AiForce::Update()
 		return ;
 	}
 
-	std::vector<CUnit *> idleUnits;
-	const CUnit *leader = NULL;
-	for (unsigned int i = 0; i != Size(); ++i) {
-		CUnit &aiunit = *Units[i];
+	Assert(Map.Info.IsPointOnMap(GoalPos));
+	std::vector<CUnit *> attackUnits;
+	CUnit *leader = NULL;
+	int distance = 0;
+	Vec2i pos = GoalPos;
 
-		if (aiunit.IsIdle()) {
-			if (aiunit.IsAliveOnMap()) {
-				idleUnits.push_back(&aiunit);
+	if (isTransporterAttack) {
+		for (unsigned int i = 0; i != Size(); ++i) {
+			CUnit &aiunit = *Units[i];
+
+			if (aiunit.IsIdle()) {
+				if (aiunit.IsAliveOnMap()) {
+					attackUnits.push_back(&aiunit);
+				}
+			} else if (aiunit.CurrentAction() == UnitActionAttack) {
+				leader = &aiunit;
+			}
+		}
+	} else {
+		attackUnits = Units.Units;
+		for (unsigned int i = 0; i < Size(); ++i) {
+			CUnit &aiunit = *Units[i];
+			if (aiunit.CurrentAction() == UnitActionAttack) {
+				leader = &aiunit;
+				if (leader->CurrentOrder()->HasGoal() && leader->CurrentOrder()->GetGoal()->IsAliveOnMap()) {
+					pos = leader->CurrentOrder()->GetGoal()->tilePos;
+				}
+				break;
 			}
-		} else if (aiunit.CurrentAction() == UnitActionAttack) {
-			leader = &aiunit;
-		} else if (leader == NULL) {
-			leader = &aiunit;
 		}
 	}
-	const Vec2i pos = leader != NULL ? leader->tilePos : this->GoalPos;
-	for (size_t i = 0; i != idleUnits.size(); ++i) {
-		CUnit &aiunit = *idleUnits[i];
-		const int delay = i / 5; // To avoid lot of CPU consuption, send them with a small time difference.
 
-		aiunit.Wait = delay;
-		if (aiunit.Type->CanAttack) {
-			CommandAttack(aiunit, pos, NULL, FlushCommands);
-		} else if (aiunit.Type->CanTransport()) {
-			if (aiunit.BoardCount != 0) {
-				CommandUnload(aiunit, pos, NULL, FlushCommands);
-			} else {
-				// FIXME : Retrieve unit blocked (transport previously full)
-				CommandMove(aiunit, aiunit.Player->StartPos, FlushCommands);
-				this->Remove(aiunit);
+	if (leader != NULL) {
+		for (size_t i = 0; i != attackUnits.size(); ++i) {
+			CUnit &aiunit = *attackUnits[i];
+
+			if (aiunit.CurrentAction() == UnitActionAttack) {
+				continue;
+			}
+
+			const int delay = i / 5; // To avoid lot of CPU consuption, send them with a small time difference.
+
+			aiunit.Wait = delay;
+			if (aiunit.Type->CanAttack) {
+				CommandAttack(aiunit, pos, NULL, FlushCommands);
+			} else if (isTransporterAttack && aiunit.Type->CanTransport()) {
+				if (aiunit.BoardCount != 0) {
+					CommandUnload(aiunit, pos, NULL, FlushCommands);
+				} else {
+					// FIXME : Retrieve unit blocked (transport previously full)
+					CommandMove(aiunit, aiunit.Player->StartPos, FlushCommands);
+					this->Remove(aiunit);
+				}
+			} else {
+				CommandMove(aiunit, pos, FlushCommands);
+			}
+		}
+	} else { // Everyone is idle, find a new target
+		const CUnit *unit = NULL;
+
+		AiForceEnemyFinder<AIATTACK_BUILDING>(*this, &unit);
+
+		if (!unit) {
+			// No enemy found, give up
+			// FIXME: should the force go home or keep trying to attack?
+			DebugPrint("%d: Attack force #%lu can't find a target, giving up\n"
+					   _C_ AiPlayer->Player->Index _C_(long unsigned int)(this  - & (AiPlayer->Force[0])));
+			Attacking = false;
+			return;
+		}
+		GoalPos = unit->tilePos;
+		this->State = AiForceAttackingState_Attacking;
+		for (size_t i = 0; i != this->Units.size(); ++i) {
+			CUnit *const unit = this->Units[i];
+
+			if (unit->Container == NULL) {
+				const int delay = i / 5; // To avoid lot of CPU consuption, send them with a small time difference.
+
+				unit->Wait = delay;
+				if (unit->Type->CanAttack) {
+					CommandAttack(*unit, GoalPos,  NULL, FlushCommands);
+				} else {
+					CommandMove(*unit, GoalPos, FlushCommands);
+				}
 			}
-		} else {
-			CommandMove(aiunit, pos, FlushCommands);
 		}
 	}
 }
@@ -738,7 +804,7 @@ void AiForceManager::Update()
 				&& force.Units[0]->MapDistanceTo(force.GoalPos) <= nearDist) {
 				//  Look if still enemies in attack range.
 				const CUnit *dummy = NULL;
-				if (!AiForceEnemyFinder<true>(force, &dummy).found()) {
+				if (!AiForceEnemyFinder<AIATTACK_RANGE>(force, &dummy).found()) {
 					if (Map.Info.IsPointOnMap(force.HomePos)) {
 						for (size_t i = 0; i != force.Units.size(); ++i) {
 							CUnit &unit = *force.Units[i];
@@ -751,6 +817,31 @@ void AiForceManager::Update()
 					force.Defending = false;
 					force.Attacking = false;
 				}
+			} else { // Find idle units and order them to defend
+				std::vector<CUnit *> idleUnits;
+				for (unsigned int i = 0; i != Size(); ++i) {
+					CUnit &aiunit = *Units[i];
+
+					if (aiunit.IsIdle()) {
+						if (aiunit.IsAliveOnMap()) {
+							idleUnits.push_back(&aiunit);
+						}
+					}
+				}
+				for (unsigned int i = 0; i != idleUnits.size(); ++i) {
+					CUnit *const unit = idleUnits[i];
+
+					if (unit->Container == NULL) {
+						const int delay = i / 5; // To avoid lot of CPU consuption, send them with a small time difference.
+
+						unit->Wait = delay;
+						if (unit->Type->CanAttack) {
+							CommandAttack(*unit, force.GoalPos,  NULL, FlushCommands);
+						} else {
+							CommandMove(*unit, force.GoalPos, FlushCommands);
+						}
+					}
+				}
 			}
 		} else if (force.Attacking) {
 			force.RemoveDeadUnit();
diff --git a/src/ai/ai_local.h b/src/ai/ai_local.h
index ddc6db80c..93f1cde01 100644
--- a/src/ai/ai_local.h
+++ b/src/ai/ai_local.h
@@ -141,7 +141,7 @@ public:
 		}
 		Units.for_each(InternalRemoveUnit);
 		Units.clear();
-		GoalPos.x = GoalPos.y = -1;
+		HomePos.x = HomePos.y = GoalPos.x = GoalPos.y = -1;
 	}
 	inline size_t Size() const { return Units.size(); }
 
diff --git a/src/ai/ai_resource.cpp b/src/ai/ai_resource.cpp
index a6c5a222c..3abc022b0 100644
--- a/src/ai/ai_resource.cpp
+++ b/src/ai/ai_resource.cpp
@@ -856,8 +856,10 @@ static int AiAssignHarvesterFromTerrain(CUnit &unit, int resource)
 */
 static int AiAssignHarvesterFromUnit(CUnit &unit, int resource)
 {
+	// Try to find the nearest depot first.
+	CUnit *depot = FindDeposit(unit, 1000, resource);
 	// Find a resource to harvest from.
-	CUnit *mine = UnitFindResource(unit, unit.tilePos, 1000, resource, true);
+	CUnit *mine = UnitFindResource(unit, depot ? depot->tilePos : unit.tilePos, 1000, resource, true);
 
 	if (mine) {
 		CommandResource(unit, *mine, FlushCommands);
diff --git a/src/include/map.h b/src/include/map.h
index a6bf697e3..0daad4454 100644
--- a/src/include/map.h
+++ b/src/include/map.h
@@ -197,6 +197,15 @@ private:
 	const CPlayer *player;
 };
 
+class HasNotSamePlayerAs
+{
+public:
+	explicit HasNotSamePlayerAs(const CPlayer &_player) : player(&_player) {}
+	bool operator()(const CUnit *unit) const { return unit->Player != player; }
+private:
+	const CPlayer *player;
+};
+
 class IsAlliedWith
 {
 public:
@@ -243,6 +252,12 @@ private:
 	const CUnit *forbidden;
 };
 
+class IsBuildingType
+{
+public:
+	bool operator()(const CUnit *unit) const { return unit->Type->Building; }
+};
+
 
 template <typename Pred>
 class NotPredicate
diff --git a/src/include/unit.h b/src/include/unit.h
index 15517feaa..215623629 100644
--- a/src/include/unit.h
+++ b/src/include/unit.h
@@ -365,6 +365,17 @@ struct lua_State;
 
 typedef COrder *COrderPtr;
 
+/*
+** Configuration of the small (unit) AI.
+*/
+#define PRIORITY_FACTOR   0x00001000
+#define HEALTH_FACTOR     0x00000001
+#define DISTANCE_FACTOR   0x00010000
+#define INRANGE_FACTOR    0x00001000
+#define INRANGE_BONUS     0x00100000
+#define CANATTACK_BONUS   0x00010000
+#define AIPRIORITY_BONUS  0x01000000
+
 
 /// Called whenever the selected unit was updated
 extern void SelectedUnitChanged();
@@ -543,8 +554,8 @@ public:
 		signed char IY;                     /// seen Y image displacement to map position
 		unsigned    Constructed : 1;        /// Unit seen construction
 		unsigned    State : 3;              /// Unit seen build/upgrade state
-		unsigned    Destroyed : PlayerMax;  /// Unit seen destroyed or not
-		unsigned    ByPlayer : PlayerMax;   /// Track unit seen by player
+unsigned    Destroyed : PlayerMax;  /// Unit seen destroyed or not
+unsigned    ByPlayer : PlayerMax;   /// Track unit seen by player
 	} Seen;
 
 	CVariable *Variable; /// array of User Defined variables.
@@ -555,6 +566,7 @@ public:
 	int LastGroup;      /// unit belongs to this last group
 
 	unsigned int Wait;          /// action counter
+	int Threshold;              /// The counter while ai unit couldn't change target.
 
 	struct _unit_anim_ {
 		const CAnimation *Anim;      /// Anim
@@ -943,8 +955,10 @@ extern CUnit *UnitOnScreen(CUnit *unit, int x, int y);
 
 /// Let a unit die
 extern void LetUnitDie(CUnit &unit);
-/// Destory all units inside another unit
+/// Destroy all units inside another unit
 extern void DestroyAllInside(CUnit &source);
+/// Calculate some value to measure the unit's priority for AI
+extern int ThreatCalculate(const CUnit &unit, const CUnit *dest);
 /// Hit unit with damage, if destroyed give attacker the points
 extern void HitUnit(CUnit *attacker, CUnit &target, int damage);
 
@@ -1020,7 +1034,7 @@ extern CUnit *ResourceOnMap(const Vec2i &pos, int resource, bool mine_on_top = t
 extern CUnit *ResourceDepositOnMap(const Vec2i &pos, int resource);
 
 /// Find best enemy in numeric range to attack
-extern CUnit *AttackUnitsInDistance(const CUnit &unit, int range);
+extern CUnit *AttackUnitsInDistance(const CUnit &unit, int range, bool onlyBuildings = false);
 /// Find best enemy in attack range to attack
 extern CUnit *AttackUnitsInRange(const CUnit &unit);
 /// Find best enemy in reaction range to attack
diff --git a/src/include/unittype.h b/src/include/unittype.h
index d4aeae9cd..acb55556e 100644
--- a/src/include/unittype.h
+++ b/src/include/unittype.h
@@ -1021,9 +1021,10 @@ public:
 
 	CUnitStats DefaultStat;
 	struct BoolFlags {
-		bool value;          /// User defined flag. Used for (dis)allow target.
-		char CanTransport;   /// Can transport units with this flag.
-		char CanTargetFlag;  /// Flag needed to target with missile.
+		bool value;             /// User defined flag. Used for (dis)allow target.
+		char CanTransport;      /// Can transport units with this flag.
+		char CanTargetFlag;     /// Flag needed to target with missile.
+		char AiPriorityTarget;  /// Attack this units first.
 	};
 	std::vector<BoolFlags> BoolFlag;
 
diff --git a/src/ui/mouse.cpp b/src/ui/mouse.cpp
index 3b8b21c69..907b57413 100644
--- a/src/ui/mouse.cpp
+++ b/src/ui/mouse.cpp
@@ -1479,6 +1479,9 @@ void UIHandleButtonDown(unsigned button)
 	CUnit *uins;
 	int i;
 
+	// Reset the ShowNameDelay counters
+	ShowNameDelay = ShowNameTime = GameCycle;
+
 	if (LongLeftButton) {
 		if (!OldValid) {
 			OldShowSightRange = Preference.ShowSightRange;
diff --git a/src/unit/build.cpp b/src/unit/build.cpp
index 28302c49e..ee3d9682e 100644
--- a/src/unit/build.cpp
+++ b/src/unit/build.cpp
@@ -305,7 +305,7 @@ CUnit *CanBuildHere(const CUnit *unit, const CUnitType &type, const Vec2i &pos)
 	}
 
 	// Check special rules for AI players
-	if (unit->Player->AiEnabled) {
+	if (unit && unit->Player->AiEnabled) {
 		size_t count = type.AiBuildingRules.size();
 		if (count > 0) {
 			ontoptarget = NULL;
diff --git a/src/unit/script_unit.cpp b/src/unit/script_unit.cpp
index c44735f1f..88a61ffba 100644
--- a/src/unit/script_unit.cpp
+++ b/src/unit/script_unit.cpp
@@ -475,6 +475,11 @@ static int CclUnit(lua_State *l)
 			// FIXME : unsigned long should be better handled
 			unit->TTL = LuaToNumber(l, -1);
 			lua_pop(l, 1);
+		} else if (!strcmp(value, "threshold")) {
+			lua_rawgeti(l, 2, j + 1);
+			// FIXME : unsigned long should be better handled
+			unit->Threshold = LuaToNumber(l, -1);
+			lua_pop(l, 1);
 		} else if (!strcmp(value, "group-id")) {
 			lua_rawgeti(l, 2, j + 1);
 			unit->GroupId = LuaToNumber(l, -1);
diff --git a/src/unit/script_unittype.cpp b/src/unit/script_unittype.cpp
index 65497c59a..4b86d90c8 100644
--- a/src/unit/script_unittype.cpp
+++ b/src/unit/script_unittype.cpp
@@ -126,6 +126,7 @@ static const char SLOT_KEY[] = "Slot";
 static const char SHIELD_KEY[] = "ShieldPoints";
 static const char POINTS_KEY[] = "Points";
 static const char MAXHARVESTERS_KEY[] = "MaxHarvesters";
+static const char AITHRESHOLD_KEY[] = "AiThreshold";
 
 /*----------------------------------------------------------------------------
 --  Functions
@@ -162,7 +163,7 @@ CUnitTypeVar::CVariableKeys::CVariableKeys()
 							   BASICDAMAGE_KEY, POSX_KEY, POSY_KEY, RADARRANGE_KEY,
 							   RADARJAMMERRANGE_KEY, AUTOREPAIRRANGE_KEY, BLOODLUST_KEY, HASTE_KEY,
 							   SLOW_KEY, INVISIBLE_KEY, UNHOLYARMOR_KEY, SLOT_KEY, SHIELD_KEY, POINTS_KEY,
-							   MAXHARVESTERS_KEY
+							   MAXHARVESTERS_KEY, AITHRESHOLD_KEY
 							  };
 
 	for (int i = 0; i < NVARALREADYDEFINED; ++i) {
@@ -1005,6 +1006,33 @@ static int CclDefineUnitType(lua_State *l)
 				}
 				LuaError(l, "Unsupported flag tag for can-target-flag: %s" _C_ value);
 			}
+		} else if (!strcmp(value, "PriorityTarget")) {
+			//
+			// Warning: ai-priority-target should only be used AFTER all bool flags
+			// have been defined.
+			//
+			if (!lua_istable(l, -1)) {
+				LuaError(l, "incorrect argument");
+			}
+			if (type->BoolFlag.size() < UnitTypeVar.GetNumberBoolFlag()) {
+				type->BoolFlag.resize(UnitTypeVar.GetNumberBoolFlag());
+			}
+			subargs = lua_rawlen(l, -1);
+			for (k = 0; k < subargs; ++k) {
+				lua_rawgeti(l, -1, k + 1);
+				value = LuaToString(l, -1);
+				lua_pop(l, 1);
+				++k;
+				int index = UnitTypeVar.BoolFlagNameLookup[value];
+				if (index != -1) {
+					lua_rawgeti(l, -1, k + 1);
+					value = LuaToString(l, -1);
+					lua_pop(l, 1);
+					type->BoolFlag[index].AiPriorityTarget = Ccl2Condition(l, value);
+					continue;
+				}
+				LuaError(l, "Unsupported flag tag for ai-priority-target: %s" _C_ value);
+			}
 		} else if (!strcmp(value, "IsNotSelectable")) {
 			type->IsNotSelectable = LuaToBoolean(l, -1);
 		} else if (!strcmp(value, "SelectableByRectangle")) {
diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp
index 2bdd9cb5a..339bd0a97 100644
--- a/src/unit/unit.cpp
+++ b/src/unit/unit.cpp
@@ -172,6 +172,7 @@ void CUnit::Init()
 	memset(&Seen, 0, sizeof(Seen));
 	Variable = NULL;
 	TTL = 0;
+	Threshold = 0;
 	GroupId = 0;
 	LastGroup = 0;
 	ResourcesHeld = 0;
@@ -369,7 +370,11 @@ bool CUnit::RestoreOrder()
 {
 	COrder *savedOrder = this->SavedOrder;
 
-	if (savedOrder == NULL || savedOrder->IsValid() == false) {
+	if (savedOrder == NULL) {
+		return false;
+	}
+
+	if (savedOrder->IsValid() == false) {
 		delete this->SavedOrder;
 		this->SavedOrder = NULL;
 		return false;
@@ -400,6 +405,10 @@ bool CUnit::StoreOrder(COrder *order)
 	Assert(order);
 
 	if (this->SavedOrder != NULL) {
+		if (this->SavedOrder->IsValid() == false) {
+			delete this->SavedOrder;
+			this->SavedOrder = NULL;
+		}
 		return false;
 	}
 	if (order && order->Finished == true) {
@@ -2580,6 +2589,11 @@ CUnit *UnitOnScreen(CUnit *ounit, int x, int y)
 				continue;
 			}
 		}
+		// Check if better units are present on this location
+		if (unit->Type->IsNotSelectable) {
+			funit = unit;
+			continue;
+		}
 		//
 		// This could be taken.
 		//
@@ -2735,6 +2749,47 @@ void DestroyAllInside(CUnit &source)
   -- Unit AI
   ----------------------------------------------------------------------------*/
 
+int ThreatCalculate(const CUnit &unit, const CUnit *dest)
+{
+	int cost = 0;
+
+	const CUnitType &type = *unit.Type;
+	const CUnitType &dtype = *dest->Type;
+
+	// Priority 0-255
+	cost -= dtype.Priority * PRIORITY_FACTOR;
+	// Remaining HP (Health) 0-65535
+	cost += dest->Variable[HP_INDEX].Value * 100 / dest->Variable[HP_INDEX].Max * HEALTH_FACTOR;
+
+	int d = unit.MapDistanceTo(*dest);
+
+	if (d <= unit.Stats->Variables[ATTACKRANGE_INDEX].Max && d >= type.MinAttackRange) {
+		cost += d * INRANGE_FACTOR;
+		cost -= INRANGE_BONUS;
+	} else {
+		cost += d * DISTANCE_FACTOR;
+	}
+
+	for (unsigned int i = 0; i < UnitTypeVar.GetNumberBoolFlag(); i++) {
+		if (type.BoolFlag[i].AiPriorityTarget != CONDITION_TRUE) {
+			if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_ONLY) &
+				(dtype.BoolFlag[i].value)) {
+					cost -= AIPRIORITY_BONUS;
+			}
+			if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_FALSE) &
+				(dtype.BoolFlag[i].value)) {
+					cost += AIPRIORITY_BONUS;
+			}
+		}
+	}
+
+	// Unit can attack back.
+	if (CanTarget(&dtype, &type)) {
+		cost -= CANATTACK_BONUS;
+	}
+	return cost;
+}
+
 /**
 **  Unit is hit by missile or other damage.
 **
@@ -2807,7 +2862,7 @@ void HitUnit(CUnit *attacker, CUnit &target, int damage)
 		}
 	}
 
-	if (attacker && !target.Type->Wall && target.Type->Building && target.Player->AiEnabled) {
+	if (attacker && !target.Type->Wall && target.Player->AiEnabled) {
 		AiHelpMe(attacker, target);
 	}
 
@@ -2816,6 +2871,9 @@ void HitUnit(CUnit *attacker, CUnit &target, int damage)
 		(target.Variable[HP_INDEX].Value == 0)) { // unit is killed or destroyed
 		//  increase scores of the attacker, but not if attacking it's own units.
 		//  prevents cheating by killing your own units.
+
+		//  Setting ai threshold counter to 0 so it can target other units
+		attacker->Threshold = 0;
 		if (attacker && target.IsEnemy(*attacker)) {
 			attacker->Player->Score += target.Variable[POINTS_INDEX].Value;
 			if (type->Building) {
@@ -2909,47 +2967,14 @@ void HitUnit(CUnit *attacker, CUnit &target, int damage)
 		}
 	}
 
-	// Attack units in range (which or the attacker?)
-	if (attacker && target.IsAgressive() && target.CanMove()) {
-		if (target.CurrentAction() != UnitActionStill) {
-			return;
-		}
-		CUnit *goal;
-
-		if (RevealAttacker && CanTarget(target.Type, attacker->Type)) {
-			// Reveal Unit that is attacking
-			goal = attacker;
-		} else {
-			if (target.CurrentAction() == UnitActionStandGround) {
-				goal = AttackUnitsInRange(target);
-			} else {
-				// Check for any other units in range
-				goal = AttackUnitsInReactRange(target);
-			}
-		}
-		if (goal) {
-			COrder *savedOrder = target.CurrentOrder()->Clone();
-
-			CommandAttack(target, goal->tilePos, NoUnitP, FlushCommands);
-			if (target.StoreOrder(savedOrder) == false) {
-				delete savedOrder;
-				savedOrder = NULL;
-			}
-			return;
-		}
+	if (!attacker) {
+		return;
 	}
 
-	/*
-		What should we do with workers on :
-		case UnitActionRepair:
-
-		Drop orders and run away or return after escape?
-	*/
-
 	//
 	// Can't attack run away.
 	//
-	if (attacker && target.CanMove() && target.CurrentAction() == UnitActionStill) {
+	if (!target.IsAgressive() && target.CanMove() && target.CurrentAction() == UnitActionStill) {
 		Vec2i pos = target.tilePos - attacker->tilePos;
 		int d = isqrt(pos.x * pos.x + pos.y * pos.y);
 
@@ -2962,6 +2987,73 @@ void HitUnit(CUnit *attacker, CUnit &target, int damage)
 		CommandStopUnit(target);
 		CommandMove(target, pos, 0);
 	}
+
+	// The rest instructions is only for AI units.
+	if (!target.Player->AiEnabled) {
+		return;
+	}
+
+	const int threshold = 30;
+
+	if (target.Threshold && target.CurrentOrder()->HasGoal() && target.CurrentOrder()->GetGoal() == attacker) {
+		target.Threshold = threshold;
+		return;
+	}
+
+	// If the threshold counter is not zero, ignoring
+	if (target.Threshold) {
+		return;
+	}
+
+	// Attack units in range (which or the attacker?)
+	// Don't bother unit if it casting repeatable spell
+	if (target.IsAgressive() && target.CanMove() && target.CurrentAction() != UnitActionSpellCast) {
+		COrder *savedOrder = target.CurrentOrder()->Clone();
+		CUnit *oldgoal = target.CurrentOrder()->GetGoal();
+		CUnit *goal, *best = oldgoal;
+
+		if (RevealAttacker && CanTarget(target.Type, attacker->Type)) {
+			// Reveal Unit that is attacking
+			goal = attacker;
+		} else {
+			if (target.CurrentAction() == UnitActionStandGround) {
+				goal = AttackUnitsInRange(target);
+			} else {
+				// Check for any other units in range
+				goal = AttackUnitsInReactRange(target);
+			}
+		}
+
+		// Calculate the best target we could attack
+		best = oldgoal ? oldgoal : (goal ? goal : attacker);
+		if (best && best != attacker) {
+			if (goal && ((goal->IsAgressive() && best->IsAgressive() == false)
+				|| (ThreatCalculate(target, goal) < ThreatCalculate(target, best)))) {
+				best = goal;
+			}
+			if (!RevealAttacker && (best->IsAgressive() == false || ThreatCalculate(target, attacker) < ThreatCalculate(target, best))) {
+				best = attacker;
+			}
+		}
+		CommandAttack(target, best->tilePos, NoUnitP, FlushCommands);
+
+		// Set threshold value only for agressive units
+		if (best->IsAgressive()) {
+			target.Threshold = threshold;
+		}
+		if (target.StoreOrder(savedOrder) == false) {
+			delete savedOrder;
+			savedOrder = NULL;
+		}
+		return;
+	}
+
+	/*
+		What should we do with workers on :
+		case UnitActionRepair:
+
+		Drop orders and run away or return after escape?
+	*/
 }
 
 /*----------------------------------------------------------------------------
diff --git a/src/unit/unit_draw.cpp b/src/unit/unit_draw.cpp
index 811e389c9..8cc7cb618 100644
--- a/src/unit/unit_draw.cpp
+++ b/src/unit/unit_draw.cpp
@@ -666,7 +666,7 @@ void ShowOrder(const CUnit &unit)
 	if (unit.Destroyed) {
 		return;
 	}
-	if (unit.Player != ThisPlayer && !ThisPlayer->IsAllied(unit)) {
+	if (!DEBUG && unit.Player != ThisPlayer && !ThisPlayer->IsAllied(unit)) {
 		return;
 	}
 	// Get current position
diff --git a/src/unit/unit_find.cpp b/src/unit/unit_find.cpp
index 6376ab52d..dbff76a55 100644
--- a/src/unit/unit_find.cpp
+++ b/src/unit/unit_find.cpp
@@ -51,21 +51,12 @@
 #include "interface.h"
 #include "tileset.h"
 #include "pathfinder.h"
+#include "spells.h"
 
 /*----------------------------------------------------------------------------
 --  Defines
 ----------------------------------------------------------------------------*/
 
-/*
-** Configuration of the small (unit) AI.
-*/
-#define PRIORITY_FACTOR   0x00010000
-#define HEALTH_FACTOR     0x00000001
-#define DISTANCE_FACTOR   0x00100000
-#define INRANGE_FACTOR    0x00010000
-#define INRANGE_BONUS     0x01000000
-#define CANATTACK_BONUS   0x00100000
-
 /*----------------------------------------------------------------------------
 --  Local Data
 ----------------------------------------------------------------------------*/
@@ -308,7 +299,7 @@ private:
 		// Priority 0-255
 		cost -= dtype.Priority * PRIORITY_FACTOR;
 		// Remaining HP (Health) 0-65535
-		cost += dest->Variable[HP_INDEX].Value * HEALTH_FACTOR;
+		cost += dest->Variable[HP_INDEX].Value * 100 / dest->Variable[HP_INDEX].Max * HEALTH_FACTOR;
 
 		if (d <= attackrange && d >= type.MinAttackRange) {
 			cost += d * INRANGE_FACTOR;
@@ -317,6 +308,19 @@ private:
 			cost += d * DISTANCE_FACTOR;
 		}
 
+		for (unsigned int i = 0; i < UnitTypeVar.GetNumberBoolFlag(); i++) {
+			if (type.BoolFlag[i].AiPriorityTarget != CONDITION_TRUE) {
+				if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_ONLY) &
+					(dtype.BoolFlag[i].value)) {
+						cost -= AIPRIORITY_BONUS;
+				}
+				if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_FALSE) &
+					(dtype.BoolFlag[i].value)) {
+						cost += AIPRIORITY_BONUS;
+				}
+			}
+		}
+
 		// Unit can attack back.
 		if (CanTarget(&dtype, &type)) {
 			cost -= CANATTACK_BONUS;
@@ -417,6 +421,19 @@ public:
 			} else {
 				//  Priority 0-255
 				cost += dtype.Priority * PRIORITY_FACTOR;
+
+				for (unsigned int i = 0; i < UnitTypeVar.GetNumberBoolFlag(); i++) {
+					if (type.BoolFlag[i].AiPriorityTarget != CONDITION_TRUE) {
+						if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_ONLY) &
+							(dtype.BoolFlag[i].value)) {
+								cost -= AIPRIORITY_BONUS;
+						} else if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_FALSE) &
+							(dtype.BoolFlag[i].value)) {
+								cost += AIPRIORITY_BONUS;
+						}
+					}
+				}
+
 				//  Remaining HP (Health) 0-65535
 				// Give a boost to unit we can kill in one shoot only
 
@@ -612,12 +629,13 @@ struct CompareUnitDistance {
 **  If the unit can attack must be handled by caller.
 **  Choose the best target, that can be attacked.
 **
-**  @param unit   Find in distance for this unit.
-**  @param range  Distance range to look.
+**  @param unit           Find in distance for this unit.
+**  @param range          Distance range to look.
+**  @param onlyBuildings  Search only buildings (useful when attacking with AI force)
 **
 **  @return       Unit to be attacked.
 */
-CUnit *AttackUnitsInDistance(const CUnit &unit, int range)
+CUnit *AttackUnitsInDistance(const CUnit &unit, int range, bool onlyBuildings)
 {
 	// if necessary, take possible damage on allied units into account...
 	if (unit.Type->Missile.Missile->Range > 1
@@ -632,8 +650,13 @@ CUnit *AttackUnitsInDistance(const CUnit &unit, int range)
 		// If unit is removed, use containers x and y
 		const CUnit *firstContainer = unit.Container ? unit.Container : &unit;
 		std::vector<CUnit *> table;
-		Map.SelectAroundUnit(*firstContainer, missile_range, table,
-							 MakeNotPredicate(HasSamePlayerAs(Players[PlayerNumNeutral])));
+		if (onlyBuildings) {
+			Map.SelectAroundUnit(*firstContainer, missile_range, table,
+				MakeAndPredicate(HasNotSamePlayerAs(Players[PlayerNumNeutral]), IsBuildingType()));
+		} else {
+			Map.SelectAroundUnit(*firstContainer, missile_range, table,
+								 MakeNotPredicate(HasSamePlayerAs(Players[PlayerNumNeutral])));
+		}
 
 		if (table.empty() == false) {
 			return BestRangeTargetFinder(unit, range).Find(table);
@@ -642,8 +665,13 @@ CUnit *AttackUnitsInDistance(const CUnit &unit, int range)
 	} else {
 		std::vector<CUnit *> table;
 
-		Map.SelectAroundUnit(unit, range, table,
-							 MakeNotPredicate(HasSamePlayerAs(Players[PlayerNumNeutral])));
+		if (onlyBuildings) {
+			Map.SelectAroundUnit(unit, range, table,
+				MakeAndPredicate(HasNotSamePlayerAs(Players[PlayerNumNeutral]), IsBuildingType()));
+		} else {
+			Map.SelectAroundUnit(unit, range, table,
+								 MakeNotPredicate(HasSamePlayerAs(Players[PlayerNumNeutral])));
+		}
 
 		const int n = static_cast<int>(table.size());
 		if (range > 25 && table.size() > 9) {
diff --git a/src/unit/unit_save.cpp b/src/unit/unit_save.cpp
index 3f73095b6..08454f1c8 100644
--- a/src/unit/unit_save.cpp
+++ b/src/unit/unit_save.cpp
@@ -214,6 +214,7 @@ void SaveUnit(const CUnit &unit, CFile &file)
 		file.printf(" \"active\",");
 	}
 	file.printf("\"ttl\", %lu,\n  ", unit.TTL);
+	file.printf("\"threshold\", %lu,\n  ", unit.Threshold);
 
 	for (size_t i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) {
 		if (unit.Variable[i] != unit.Type->DefaultStat.Variables[i]) {