From 82eed1ab1f2e08633c5f7a7fc09824b6e8037b37 Mon Sep 17 00:00:00 2001
From: cybermind <iddqd_mail@mail.ru>
Date: Sun, 10 Feb 2013 13:47:03 +0600
Subject: [PATCH] [+] Added autocast by variable priority (heal the wounded
 ones, bloodlust for strong ones...)

---
 src/include/spells.h       |  8 ++++++-
 src/spell/script_spell.cpp | 24 +++++++++++++++++++
 src/spell/spells.cpp       | 48 +++++++++++++++++++++++++++-----------
 3 files changed, 66 insertions(+), 14 deletions(-)

diff --git a/src/include/spells.h b/src/include/spells.h
index c934c6e59..6a792ccdc 100644
--- a/src/include/spells.h
+++ b/src/include/spells.h
@@ -164,11 +164,17 @@ public:
 class AutoCastInfo
 {
 public:
-	AutoCastInfo() : Range(0), Condition(0), Combat(0) {};
+	// Special flags for priority sorting
+#define ACP_NOVALUE -1
+#define ACP_DISTANCE -2
+	AutoCastInfo() : Range(0), Condition(0), Combat(0), PriorytyVar(ACP_NOVALUE), ReverseSort(false) {};
 	~AutoCastInfo() { delete Condition; };
 	/// @todo this below is SQUARE!!!
 	int Range;                   /// Max range of the target.
 
+	int PriorytyVar;             /// Variable to sort autocast targets by priority.
+	bool ReverseSort;            /// If true, small values have the highest priority.
+
 	ConditionInfo *Condition;    /// Conditions to cast the spell.
 
 	/// Detalied generic conditions (not per-target, where Condition is evaluated.)
diff --git a/src/spell/script_spell.cpp b/src/spell/script_spell.cpp
index 01a848629..60b04f351 100644
--- a/src/spell/script_spell.cpp
+++ b/src/spell/script_spell.cpp
@@ -242,6 +242,30 @@ static void CclSpellAutocast(lua_State *l, AutoCastInfo *autocast)
 			lua_rawgeti(l, -1, j + 1);
 			autocast->Combat = Ccl2Condition(l, LuaToString(l, -1));
 			lua_pop(l, 1);
+		} else if (!strcmp(value, "priority")) {
+			lua_rawgeti(l, -1, j + 1);
+			if (!lua_istable(l, -1) || lua_rawlen(l, -1) != 2) {
+				LuaError(l, "incorrect argument");
+			}
+			lua_rawgeti(l, -1, 1);
+			std::string var = LuaToString(l, -1);
+			int index = UnitTypeVar.VariableNameLookup[var.c_str()];// User variables
+			if (index == -1) {
+				if (!strcmp(var.c_str(), "Distance")) {
+					index = ACP_DISTANCE;
+				} else {
+					fprintf(stderr, "Bad variable name '%s'\n", var.c_str());
+					Exit(1);
+				}
+			}
+			autocast->PriorytyVar = index;
+			lua_pop(l, 1);
+			lua_rawgeti(l, -1, 2);
+			autocast->ReverseSort = LuaToBoolean(l, -1);
+			lua_pop(l, 1);
+
+			lua_pop(l, 1);
+			
 		} else if (!strcmp(value, "condition")) {
 			if (!autocast->Condition) {
 				autocast->Condition = new ConditionInfo;
diff --git a/src/spell/spells.cpp b/src/spell/spells.cpp
index 69693fc7b..9c4173fbd 100644
--- a/src/spell/spells.cpp
+++ b/src/spell/spells.cpp
@@ -187,6 +187,32 @@ static bool PassCondition(const CUnit &caster, const SpellType &spell, const CUn
 	return true;
 }
 
+class AutoCastPrioritySort
+{
+public:
+	explicit AutoCastPrioritySort(const CUnit &caster, const int var, const bool reverse) :
+		caster(caster), variable(var), reverse(reverse) {}
+	bool operator()(const CUnit *lhs, const CUnit *rhs) const {
+		if (variable == ACP_DISTANCE) {
+			if (reverse) {
+				return lhs->MapDistanceTo(caster) > rhs->MapDistanceTo(caster);
+			} else {
+				return lhs->MapDistanceTo(caster) < rhs->MapDistanceTo(caster);
+			}
+		} else {
+			if (reverse) {
+				return lhs->Variable[variable].Value > rhs->Variable[variable].Value;
+			} else {
+				return lhs->Variable[variable].Value < rhs->Variable[variable].Value;
+			}
+		}
+	}
+private:
+	const CUnit &caster;
+	const int variable;
+	const bool reverse;
+};
+
 /**
 **  Select the target for the autocast.
 **
@@ -263,21 +289,17 @@ static Target *SelectTargetUnitsOfAutoCast(CUnit &caster, const SpellType &spell
 				}
 			}
 			// Now select the best unit to target.
-			// FIXME: Some really smart way to do this.
-			// FIXME: Heal the unit with the lowest hit-points
-			// FIXME: Bloodlust the unit with the highest hit-point
-			// FIMXE: it will survive more
 			if (n != 0) {
-#if 0
 				// For the best target???
-				sort(table, n, spell.autocast->f_order);
-				return NewTargetUnit(*table[0]);
-#else
-				// Best unit, random unit, oh well, same stuff.
-				int index = SyncRand() % n;
-				return NewTargetUnit(*table[index]);
-#endif
-			}
+				if (autocast->PriorytyVar != ACP_NOVALUE) {
+					std::sort(table.begin(), table.begin() + n,
+						AutoCastPrioritySort(caster, autocast->PriorytyVar, autocast->ReverseSort));
+					return NewTargetUnit(*table[0]);
+				} else { // Use the old behavior
+					return NewTargetUnit(*table[SyncRand() % n]);
+				}
+				
+			} 
 			break;
 		}
 		default: