Index: binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_UnitAI.js	(revision 11702)
+++ binaries/data/mods/public/simulation/components/tests/test_UnitAI.js	(working copy)
@@ -85,7 +85,10 @@
 	AddMock(unit, IID_Attack, {
 		GetRange: function() { return 10; },
 		GetBestAttack: function() { return "melee"; },
+		GetBestAttackAgainst: function(t) { return "melee"; },
 		GetTimers: function() { return { "prepare": 500, "repeat": 1000 }; },
+		CanAttack: function(v) { return true; },
+		CompareEntitiesByPreference: function(a, b) { return 0; },
 	});
 
 	unitAI.OnCreate();
Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js	(revision 11702)
+++ binaries/data/mods/public/simulation/components/UnitAI.js	(working copy)
@@ -249,7 +249,7 @@
 		}
 
 		// Work out how to attack the given target
-		var type = this.GetBestAttack();
+		var type = this.GetBestAttackAgainst(this.order.data.target);
 		if (!type)
 		{
 			// Oops, we can't attack at all
@@ -336,7 +336,7 @@
 		if (this.MustKillGatherTarget(this.order.data.target) && this.CheckTargetVisible(this.order.data.target))
 		{
 			// Make sure we can attack the target, else we'll get very stuck
-			if (!this.GetBestAttack())
+			if (!this.GetBestAttackAgainst(this.order.data.target))
 			{
 				// Oops, we can't attack at all - give up
 				// TODO: should do something so the player knows why this failed
@@ -644,7 +644,8 @@
 				if (this.GetStance().targetVisibleEnemies)
 				{
 					// Start attacking one of the newly-seen enemy (if any)
-					this.RespondToTargetedEntities(msg.data.added);
+					var ents = this.GetAttackableEntitiesByPreference(msg.data.added);
+					this.RespondToTargetedEntities(ents);
 				}
 			},
 			
@@ -2437,6 +2438,14 @@
 	return cmpAttack.GetBestAttack();
 };
 
+UnitAI.prototype.GetBestAttackAgainst = function(target)
+{
+	var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
+	if (!cmpAttack)
+		return undefined;
+	return cmpAttack.GetBestAttackAgainst(target);
+};
+
 /**
  * Try to find one of the given entities which can be attacked,
  * and start attacking it.
@@ -2462,13 +2471,16 @@
  */
 UnitAI.prototype.AttackEntityInZone = function(ents)
 {
-	var type = this.GetBestAttack();
 	for each (var target in ents)
 	{
-		if (this.CanAttack(target) && this.CheckTargetDistanceFromHeldPosition(target, IID_Attack, type))
+		if (this.CanAttack(target))
 		{
-			this.PushOrderFront("Attack", { "target": target, "force": false });
-			return true;
+			var type = this.GetBestAttackAgainst(target);
+			if (this.CheckTargetDistanceFromHeldPosition(target, IID_Attack, type))
+			{
+				this.PushOrderFront("Attack", { "target": target, "force": false });
+				return true;
+			}
 		}
 	}
 	return false;
@@ -2928,8 +2940,7 @@
 	if (!this.GetStance().targetVisibleEnemies)
 		return false;
 
-	SortEntitiesByPriority(ents);
-	return this.RespondToTargetedEntities(ents);
+	return this.RespondToTargetedEntities(this.GetAttackableEntitiesByPreference(ents));
 };
 
 /**
@@ -3023,6 +3034,9 @@
 	// Verify that we're able to respond to Attack commands
 	var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
 	if (!cmpAttack)
+		return false;    
+  
+  if (!cmpAttack.CanAttack(target))
 		return false;
 
 	// Verify that the target is alive
@@ -3266,4 +3280,15 @@
 	return false;
 };
 
+UnitAI.prototype.GetAttackableEntitiesByPreference = function(ents)
+{
+	var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
+	if (!cmpAttack)
+		return [];
+
+	return ents
+		.filter(function (v, i, a) { return cmpAttack.CanAttack(v); })
+		.sort(function (a, b) { return cmpAttack.CompareEntitiesByPreference(a, b); });
+};
+
 Engine.RegisterComponentType(IID_UnitAI, "UnitAI", UnitAI);
Index: binaries/data/mods/public/simulation/components/Attack.js
===================================================================
--- binaries/data/mods/public/simulation/components/Attack.js	(revision 11702)
+++ binaries/data/mods/public/simulation/components/Attack.js	(working copy)
@@ -18,6 +18,26 @@
 		"</element>" +
 	"</optional>";
 
+var preferredClassesSchema =
+	"<optional>" +
+		"<element name='PreferredClasses' a:help='Space delimited list of classes preferred for attacking. If an entity has any of theses classes, it is preferred.'>" +
+			"<attribute name='datatype'>" +
+				"<value>tokens</value>" +
+			"</attribute>" +
+			"<text/>" +
+		"</element>" +
+	"</optional>";
+
+var restrictedClassesSchema =
+	"<optional>" +
+		"<element name='RestrictedClasses' a:help='Space delimited list of classes that cannot be attacked by this entity. If target entity has any of these classes, it cannot be attacked'>" +
+			"<attribute name='datatype'>" +
+				"<value>tokens</value>" +
+			"</attribute>" +
+			"<text/>" +
+		"</element>" +
+	"</optional>";
+
 Attack.prototype.Schema =
 	"<a:help>Controls the attack abilities and strengths of the unit.</a:help>" +
 	"<a:example>" +
@@ -38,6 +58,8 @@
 					"<Multiplier>1.5</Multiplier>" +
 				"</BonusCavMelee>" +
 			"</Bonuses>" +
+			"<RestrictedClasses datatype=\"tokens\">Champion</RestrictedClasses>" +
+			"<PreferredClasses datatype=\"tokens\">Cavalry Infantry</PreferredClasses>" +
 		"</Melee>" +
 		"<Ranged>" +
 			"<Hack>0.0</Hack>" +
@@ -54,6 +76,7 @@
 					"<Multiplier>2</Multiplier>" +
 				"</Bonus1>" +
 			"</Bonuses>" +
+			"<RestrictedClasses datatype=\"tokens\">Champion</RestrictedClasses>" +
 		"</Ranged>" +
 		"<Charge>" +
 			"<Hack>10.0</Hack>" +
@@ -74,6 +97,8 @@
 					"<data type='positiveInteger'/>" +
 				"</element>" +
 				bonusesSchema +
+				preferredClassesSchema +
+				restrictedClassesSchema +
 			"</interleave>" +
 		"</element>" +
 	"</optional>" +
@@ -95,6 +120,8 @@
 					"<ref name='nonNegativeDecimal'/>" +
 				"</element>" +
 				bonusesSchema +
+				preferredClassesSchema +
+				restrictedClassesSchema +
 			"</interleave>" +
 		"</element>" +
 	"</optional>" +
@@ -107,6 +134,8 @@
 				"<element name='MaxRange'><ref name='nonNegativeDecimal'/></element>" + // TODO: how do these work?
 				"<element name='MinRange'><ref name='nonNegativeDecimal'/></element>" +
 				bonusesSchema +
+				preferredClassesSchema +
+				restrictedClassesSchema +
 			"</interleave>" +
 		"</element>" +
 	"</optional>";
@@ -117,6 +146,87 @@
 
 Attack.prototype.Serialize = null; // we have no dynamic state to save
 
+Attack.prototype.GetAttackTypes = function()
+{
+	var ret = [];
+	if (this.template.Charge) ret.push("Charge");
+	if (this.template.Melee) ret.push("Melee");
+	if (this.template.Ranged) ret.push("Ranged");
+	return ret;
+};
+
+Attack.prototype.GetPreferredClasses = function(type)
+{
+	if (this.template[type] && this.template[type].PreferredClasses)
+	{
+		return this.template[type].PreferredClasses["_string"].split(/\s+/);
+	}
+	return [];
+};
+
+Attack.prototype.GetRestrictedClasses = function(type)
+{
+	if (this.template[type] && this.template[type].RestrictedClasses)
+	{
+		return this.template[type].RestrictedClasses["_string"].split(/\s+/);
+	}
+	return [];
+};
+
+Attack.prototype.CanAttack = function(target)
+{
+	const cmpIdentity = Engine.QueryInterface(target, IID_Identity);
+	if (!cmpIdentity) 
+		return undefined;
+
+	const targetClasses = cmpIdentity.GetClassesList();
+
+	for each (var type in this.GetAttackTypes())
+	{
+		var canAttack = true;
+		var restrictedClasses = this.GetRestrictedClasses(type);
+
+		for each (var targetClass in targetClasses)
+		{
+			if (restrictedClasses.indexOf(targetClass) != -1)
+			{
+				canAttack = false;
+				break;
+			}
+		}
+		if (canAttack)
+		{
+			return true;
+		}
+	}
+
+	return false;
+};
+
+Attack.prototype.GetPreference = function(target)
+{
+	const cmpIdentity = Engine.QueryInterface(target, IID_Identity);
+	if (!cmpIdentity) 
+		return undefined;
+
+	const targetClasses = cmpIdentity.GetClassesList();
+	
+	var minPref = null;
+	for each (var type in this.GetAttackTypes())
+	{
+		for each (var targetClass in targetClasses)
+		{
+			var pref = this.GetPreferredClasses(type).indexOf(targetClass);
+			if (pref != -1 && (minPref === null || minPref > pref))
+			{
+				error("preference for target " + target + " with type " + type + " " +pref);
+				minPref = pref;
+			}
+		}
+	}
+	return minPref;
+};
+
 /**
  * Return the type of the best attack.
  * TODO: this should probably depend on range, target, etc,
@@ -124,16 +234,37 @@
  */
 Attack.prototype.GetBestAttack = function()
 {
-	if (this.template.Ranged)
-		return "Ranged";
-	else if (this.template.Melee)
-		return "Melee";
-	else if (this.template.Charge)
-		return "Charge";
-	else
+	return this.GetAttackTypes().pop();
+};
+
+Attack.prototype.GetBestAttackAgainst = function(target)
+{
+	const cmpIdentity = Engine.QueryInterface(target, IID_Identity);
+	if (!cmpIdentity) 
 		return undefined;
+
+	const targetClasses = cmpIdentity.GetClassesList();
+	const isTargetClass = function (value, i, a) { return targetClasses.indexOf(value) != -1; };
+	const types = this.GetAttackTypes();
+	const attack = this;
+	const isAllowed = function (value, i, a) { return !attack.GetRestrictedClasses(value).some(isTargetClass); }
+	const isPreferred = function (value, i, a) { return attack.GetPreferredClasses(value).some(isTargetClass); }
+	const byPreference = function (a, b) { return (types.indexOf(a) + (isPreferred(a) ? types.length : 0) ) - (types.indexOf(b) + (isPreferred(b) ? types.length : 0) ); }
+
+	return types.filter(isAllowed).sort(byPreference).pop();
 };
 
+Attack.prototype.CompareEntitiesByPreference = function(a, b)
+{
+	var aPreference = this.GetPreference(a);
+	var bPreference = this.GetPreference(b);
+
+	if (aPreference === null && bPreference === null) return 0;
+	if (aPreference === null) return bPreference;
+	if (bPreference === null) return aPreference;
+	return aPreference - bPreference; // 0 is most preferred
+};
+
 Attack.prototype.GetTimers = function(type)
 {
 	var cmpTechMan = QueryOwnerInterface(this.entity, IID_TechnologyManager);
Index: binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_aggressive.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_aggressive.xml	(revision 11702)
+++ binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_aggressive.xml	(working copy)
@@ -7,6 +7,7 @@
       <Crush>0.0</Crush>
       <MaxRange>4.0</MaxRange>
       <RepeatTime>1000</RepeatTime>
+      <RestrictedClasses datatype="tokens">Structure</RestrictedClasses>
     </Melee>
   </Attack>
   <Identity>
Index: binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_violent.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_violent.xml	(revision 11702)
+++ binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_violent.xml	(working copy)
@@ -7,6 +7,7 @@
       <Crush>0.0</Crush>
       <MaxRange>4.0</MaxRange>
       <RepeatTime>1000</RepeatTime>
+      <RestrictedClasses datatype="tokens">Structure</RestrictedClasses>
     </Melee>
   </Attack>
   <Identity>
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fishing.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fishing.xml	(revision 11702)
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fishing.xml	(working copy)
@@ -12,6 +12,7 @@
       <Crush>0.0</Crush>
       <MaxRange>5.0</MaxRange>
       <RepeatTime>1000</RepeatTime>
+      <RestrictedClasses datatype="tokens">Ship</RestrictedClasses>
     </Melee>
   </Attack>
   <Footprint>
Index: binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_aggressive.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_aggressive.xml	(revision 11702)
+++ binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_aggressive.xml	(working copy)
@@ -7,6 +7,7 @@
       <Crush>0.0</Crush>
       <MaxRange>4.0</MaxRange>
       <RepeatTime>1000</RepeatTime>
+      <RestrictedClasses datatype="tokens">Structure</RestrictedClasses>
     </Melee>
   </Attack>
   <Identity>
Index: binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_violent.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_violent.xml	(revision 11702)
+++ binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_violent.xml	(working copy)
@@ -7,6 +7,7 @@
       <Crush>0.0</Crush>
       <MaxRange>4.0</MaxRange>
       <RepeatTime>1000</RepeatTime>
+      <RestrictedClasses datatype="tokens">Structure</RestrictedClasses>
     </Melee>
   </Attack>
   <Identity>
Index: binaries/data/mods/public/simulation/templates/template_unit_support_female_citizen.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_support_female_citizen.xml	(revision 11702)
+++ binaries/data/mods/public/simulation/templates/template_unit_support_female_citizen.xml	(working copy)
@@ -12,6 +12,7 @@
       <Crush>0.0</Crush>
       <MaxRange>4.0</MaxRange>
       <RepeatTime>1000</RepeatTime>
+      <RestrictedClasses datatype="tokens">Infantry Cavalry Champion CitizenSoldier</RestrictedClasses>
     </Melee>
   </Attack>
   <Auras>
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_ram.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_ram.xml	(revision 11702)
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_ram.xml	(working copy)
@@ -21,6 +21,8 @@
 		  <Multiplier>3.0</Multiplier>
 		</BonusGates>
 	  </Bonuses>
+    <RestrictedClasses datatype="tokens">Organic</RestrictedClasses>
+    <PreferredClasses datatype="tokens">Gates Structure</PreferredClasses>
     </Melee>
     <Charge>
       <Hack>0.0</Hack>
@@ -38,6 +40,8 @@
 		  <Multiplier>3.0</Multiplier>
 		</BonusGates>
 	  </Bonuses>
+    <RestrictedClasses datatype="tokens">Organic</RestrictedClasses>
+    <PreferredClasses datatype="tokens">Gates Structure</PreferredClasses>
     </Charge>
   </Attack>
   <Cost>
