Index: binaries/data/config/default.cfg
===================================================================
--- binaries/data/config/default.cfg	(revision 18612)
+++ binaries/data/config/default.cfg	(working copy)
@@ -280,11 +280,13 @@
 kill = Delete                ; Destroy selected units
 stop = "H"                   ; Stop the current action
 unload = "U"                 ; Unload garrisoned units when a building/mechanical unit is selected
-attack = Ctrl                ; Modifier to attack instead of another action (eg capture)
-attackmove = Ctrl            ; Modifier to attackmove when clicking on a point
-attackmoveUnit = "Ctrl+Q"    ; Modifier to attackmove targeting only units when clicking on a point (should contain the attackmove keys)
+attack = Ctrl                ; Modifier to primary attack instead of another action (eg capture)
+attackmove = Ctrl            ; Modifier to primary attackmove when clicking on a point
+attackmoveUnit = "Ctrl+Q"    ; Modifier to primary attackmove targeting only units when clicking on a point (should contain the attackmove keys)
 garrison = Ctrl              ; Modifier to garrison when clicking on building
 autorallypoint = Ctrl        ; Modifier to set the rally point on the building itself
+meleeattack = Alt            ; Modifier to melee attack instead of another action
+rangedattack = Space         ; Modifier to ranged attack instead of another action
 guard = "G"                  ; Modifier to escort/guard when clicking on unit/building
 repair = "J"                 ; Modifier to repair when clicking on building/mechanical unit
 queue = Shift                ; Modifier to queue unit orders instead of replacing
Index: binaries/data/mods/public/art/actors/units/persians/champion_unit_1.xml
===================================================================
--- binaries/data/mods/public/art/actors/units/persians/champion_unit_1.xml	(revision 18612)
+++ binaries/data/mods/public/art/actors/units/persians/champion_unit_1.xml	(working copy)
@@ -42,6 +42,12 @@
   <group>
     <variant frequency="1" name="Idle"/>
     <variant file="biped/attack_capture.xml"/>
+    <variant file="biped/attack_ranged_archer.xml">
+      <props>
+        <prop attachpoint="r_hand"/>
+        <prop actor="props/units/weapons/bow_recurve.xml" attachpoint="l_hand"/>
+      </props>
+    </variant>
   </group>
   <material>player_trans.xml</material>
 </actor>
Index: binaries/data/mods/public/art/actors/units/persians/infantry_archer_a.xml
===================================================================
--- binaries/data/mods/public/art/actors/units/persians/infantry_archer_a.xml	(revision 18612)
+++ binaries/data/mods/public/art/actors/units/persians/infantry_archer_a.xml	(working copy)
@@ -36,6 +36,7 @@
   <group>
     <variant frequency="100" name="Idle"/>
     <variant file="biped/attack_ranged_archer.xml"/>
+    <variant file="biped/attack_melee_ranged.xml"/>
     <variant file="biped/attack_capture.xml"/>
     <variant file="biped/attack_slaughter.xml"/>
     <variant file="biped/gather_tree.xml"/>
Index: binaries/data/mods/public/art/actors/units/persians/infantry_archer_b.xml
===================================================================
--- binaries/data/mods/public/art/actors/units/persians/infantry_archer_b.xml	(revision 18612)
+++ binaries/data/mods/public/art/actors/units/persians/infantry_archer_b.xml	(working copy)
@@ -24,6 +24,7 @@
   <group>
     <variant frequency="100" name="Idle"/>
     <variant file="biped/attack_ranged_archer.xml"/>
+    <variant file="biped/attack_melee_ranged.xml"/>
     <variant file="biped/attack_capture.xml"/>
     <variant file="biped/attack_slaughter.xml"/>
     <variant file="biped/gather_tree.xml"/>
Index: binaries/data/mods/public/art/actors/units/persians/infantry_archer_e.xml
===================================================================
--- binaries/data/mods/public/art/actors/units/persians/infantry_archer_e.xml	(revision 18612)
+++ binaries/data/mods/public/art/actors/units/persians/infantry_archer_e.xml	(working copy)
@@ -37,6 +37,7 @@
   <group>
     <variant frequency="100" name="Idle"/>
     <variant file="biped/attack_ranged_archer.xml"/>
+    <variant file="biped/attack_melee_ranged.xml"/>
     <variant file="biped/attack_capture.xml"/>
     <variant file="biped/attack_slaughter.xml"/>
     <variant file="biped/gather_tree.xml"/>
Index: binaries/data/mods/public/art/textures/cursors/action-melee-attack.txt
===================================================================
--- binaries/data/mods/public/art/textures/cursors/action-melee-attack.txt	(nonexistent)
+++ binaries/data/mods/public/art/textures/cursors/action-melee-attack.txt	(working copy)
@@ -0,0 +1 @@
+1 1
Index: binaries/data/mods/public/art/textures/cursors/action-ranged-attack.txt
===================================================================
--- binaries/data/mods/public/art/textures/cursors/action-ranged-attack.txt	(nonexistent)
+++ binaries/data/mods/public/art/textures/cursors/action-ranged-attack.txt	(working copy)
@@ -0,0 +1 @@
+1 1
Index: binaries/data/mods/public/art/variants/biped/attack_melee_ranged.xml
===================================================================
--- binaries/data/mods/public/art/variants/biped/attack_melee_ranged.xml	(nonexistent)
+++ binaries/data/mods/public/art/variants/biped/attack_melee_ranged.xml	(working copy)
@@ -0,0 +1,8 @@
+<variant name="attack_melee">
+  <animations>
+	<animation event="0.5" file="infantry/sword/attack/isw_s_off_05.psa" name="attack_melee" speed="100"/>
+  </animations>
+  <props>
+	<prop actor="props/units/weapons/knife.xml" attachpoint="r_hand"/>
+  </props>
+</variant>
Index: binaries/data/mods/public/globalscripts/Templates.js
===================================================================
--- binaries/data/mods/public/globalscripts/Templates.js	(revision 18612)
+++ binaries/data/mods/public/globalscripts/Templates.js	(working copy)
@@ -66,6 +66,24 @@
 }
 
 /**
+ * Check if entity has all needed attack types.
+ */
+function HasNeededAttackTypes(attack, neededAttackTypes)
+{
+	if (!neededAttackTypes)
+		return true;
+	// transform the string to an array
+	if (typeof neededAttackTypes == "string")
+		neededAttackTypes = neededAttackTypes.split(/\s+/);
+
+	for (let type of neededAttackTypes)
+		if (!attack || attack.constructor === Object && !attack[type] || 
+		    attack.constructor === Array && attack.indexOf(type) == -1)
+			return false;
+	return true; 
+}
+
+/**
  * Get information about a template with or without technology modifications.
  *
  * NOTICE: The data returned here should have the same structure as
Index: binaries/data/mods/public/gui/manual/intro.txt
===================================================================
--- binaries/data/mods/public/gui/manual/intro.txt	(revision 18612)
+++ binaries/data/mods/public/gui/manual/intro.txt	(working copy)
@@ -94,8 +94,10 @@
 Right Click with a building/buildings selected: sets a rally point for units created/ungarrisoned from that building.
 Ctrl + Right Click with units selected:
     - If the cursor is over an allied structure: Garrison
-    - If the cursor is over a non-allied unit or building: Attack (instead of capture or gather)
-    - Otherwise: Attack move (by default all enemy units and structures along the way are targeted, use Ctrl + Q + Right Click to target only units). 
+    - If the cursor is over a non-allied unit or building: Primary Attack (instead of capture, gather)
+    - Otherwise: Attack move (by default all enemy units and structures along the way are targeted, use Ctrl + Q + Right Click to target only units).
+Alt + Right Click on non-allied unit or building: Melee attack
+Space + Right Click on non-allied unit or building: Ranged attack
 
 [font="sans-bold-14"]Overlays
 [font="sans-14"]Alt + G: Hide/show the GUI
Index: binaries/data/mods/public/gui/session/input.js
===================================================================
--- binaries/data/mods/public/gui/session/input.js	(revision 18612)
+++ binaries/data/mods/public/gui/session/input.js	(working copy)
@@ -228,7 +228,7 @@
 		if (unitActions[action] && unitActions[action].getActionInfo)
 		{
 			var r = unitActions[action].getActionInfo(entState, targetState, simState);
-			if (r) // return true if it's possible for one of the entities
+			if (r && r.possible) // return true if it's possible for one of the entities
 				return r;
 		}
 	}
Index: binaries/data/mods/public/gui/session/unit_actions.js
===================================================================
--- binaries/data/mods/public/gui/session/unit_actions.js	(revision 18612)
+++ binaries/data/mods/public/gui/session/unit_actions.js	(working copy)
@@ -58,7 +58,7 @@
 
 			return { "type": "move" };
 		},
-		"specificness": 12,
+		"specificness": 13,
 	},
 
 	"attack-move":
@@ -77,7 +77,8 @@
 				"x": target.x,
 				"z": target.z,
 				"targetClasses": targetClasses,
-				"queued": queued
+				"queued": queued,
+				"prefAttackType": "noCapture"
 			});
 
 			Engine.GuiInterfaceCall("PlaySound", {
@@ -114,8 +115,8 @@
 				"type": "attack",
 				"entities": selection,
 				"target": action.target,
-				"allowCapture": true,
-				"queued": queued
+				"queued": queued,
+				"prefAttackType": "Capture"
 			});
 
 			Engine.GuiInterfaceCall("PlaySound", {
@@ -130,8 +131,7 @@
 			if (!entState.attack || !targetState.hitpoints)
 				return false;
 
-			return {
-				"possible": Engine.GuiInterfaceCall("CanCapture", {
+			return { "possible": Engine.GuiInterfaceCall("CanCapture", {
 					"entity": entState.id,
 					"target": targetState.id
 				})
@@ -148,7 +148,7 @@
 				"target": target
 			};
 		},
-		"specificness": 9,
+		"specificness": 7,
 	},
 
 	"attack":
@@ -160,7 +160,7 @@
 				"entities": selection,
 				"target": action.target,
 				"queued": queued,
-				"allowCapture": false
+				"prefAttackType": "noCapture"
 			});
 
 			Engine.GuiInterfaceCall("PlaySound", {
@@ -205,6 +205,116 @@
 				"target": target
 			};
 		},
+		"specificness": 8,
+	},
+
+	"melee-attack":
+	{
+		"execute": function(target, action, selection, queued)
+		{
+			Engine.PostNetworkCommand({
+				"type": "attack",
+				"entities": selection,
+				"target": action.target,
+				"queued": queued,
+				"prefAttackType": "Melee"
+			});
+
+			Engine.GuiInterfaceCall("PlaySound", {
+				"name": "order_attack",
+				"entity": selection[0]
+			});
+
+			return true;
+		},
+		"getActionInfo": function(entState, targetState)
+		{
+			if (!entState.attack || !targetState.hitpoints)
+				return false;
+
+			return { "possible": Engine.GuiInterfaceCall("CanAttack", {
+				"entity": entState.id,
+				"target": targetState.id,
+				"type": "Melee"
+			}) };
+		},
+		"hotkeyActionCheck": function(target)
+		{
+			if (!Engine.HotkeyIsPressed("session.meleeattack") || !getActionInfo("melee-attack", target).possible)
+				return false;
+
+			return {
+				"type": "melee-attack",
+				"cursor": "action-melee-attack",
+				"target": target
+			};
+		},
+		"actionCheck": function(target)
+		{
+			if (!getActionInfo("melee-attack", target).possible)
+				return false;
+
+			return {
+				"type": "melee-attack",
+				"cursor": "action-melee-attack",
+				"target": target
+			};
+		},
+		"specificness": 9,
+	},
+
+	"ranged-attack":
+	{
+		"execute": function(target, action, selection, queued)
+		{
+			Engine.PostNetworkCommand({
+				"type": "attack",
+				"entities": selection,
+				"target": action.target,
+				"queued": queued,
+				"prefAttackType": "Ranged"
+			});
+
+			Engine.GuiInterfaceCall("PlaySound", {
+				"name": "order_attack",
+				"entity": selection[0]
+			});
+
+			return true;
+		},
+		"getActionInfo": function(entState, targetState)
+		{
+			if (!entState.attack || !targetState.hitpoints)
+				return false;
+
+			return { "possible": Engine.GuiInterfaceCall("CanAttack", {
+				"entity": entState.id,
+				"target": targetState.id,
+				"type": "Ranged"
+			}) };
+		},
+		"hotkeyActionCheck": function(target, selection)
+		{
+			if (!Engine.HotkeyIsPressed("session.rangedattack") || !getActionInfo("ranged-attack", target).possible)
+				return false;
+
+			return {
+				"type": "ranged-attack",
+				"cursor": "action-ranged-attack",
+				"target": target
+			};
+		},
+		"actionCheck": function(target, selection)
+		{
+			if (!getActionInfo("ranged-attack", target).possible)
+				return false;
+
+			return {
+				"type": "ranged-attack",
+				"cursor": "action-ranged-attack",
+				"target": target
+			};
+		},
 		"specificness": 10,
 	},
 
@@ -255,7 +365,7 @@
 				"target": target
 			};
 		},
-		"specificness": 7,
+		"specificness": 6,
 	},
 
 	"build":
@@ -368,7 +478,7 @@
 				"target": target
 			};
 		},
-		"specificness": 11,
+		"specificness": 12,
 	},
 
 	"gather":
@@ -389,7 +499,7 @@
 
 			return true;
 		},
-		"getActionInfo":  function(entState, targetState)
+		"getActionInfo": function(entState, targetState)
 		{
 			if (!targetState.resourceSupply)
 				return false;
@@ -608,8 +718,9 @@
 			if (targetState.garrisonHolder.garrisonedEntitiesCount + extraCount >= targetState.garrisonHolder.capacity)
 				tooltip = "[color=\"orange\"]" + tooltip + "[/color]";
 
-			if (!MatchesClassList(entState.identity.classes, targetState.garrisonHolder.allowedClasses))
-				return false;
+			if (!MatchesClassList(entState.identity.classes, targetState.garrisonHolder.allowedClasses) ||
+			    !HasNeededAttackTypes(entState.attack, targetState.garrisonHolder.neededAttackTypes))
+				return { "possible": false };
 
 			return {
 				"possible": true,
@@ -922,7 +1033,7 @@
 				"position": actionInfo.position
 			};
 		},
-		"specificness": 6,
+		"specificness": 5,
 	},
 
 	"unset-rallypoint":
@@ -1005,6 +1116,7 @@
 			unloadAll();
 		},
 	},
+
 	"delete": {
 		"getInfo": function(entState)
 		{
@@ -1043,6 +1155,7 @@
 				openDeleteDialog(selection);
 		},
 	},
+
 	"stop": {
 		"getInfo": function(entState)
 		{
Index: binaries/data/mods/public/simulation/ai/common-api/entity.js
===================================================================
--- binaries/data/mods/public/simulation/ai/common-api/entity.js	(revision 18612)
+++ binaries/data/mods/public/simulation/ai/common-api/entity.js	(working copy)
@@ -742,6 +742,7 @@
 		return false;
 	},
 
+	isCapturable: function() { return this.get("Capturable") !== undefined; },
 	isGarrisonHolder: function() { return this.get("GarrisonHolder") !== undefined; },
 	garrisoned: function() { return this._entity.garrisoned; },
 	canGarrisonInside: function() { return this._entity.garrisoned.length < this.garrisonMax(); },
@@ -756,8 +757,8 @@
 		return this;
 	},
 
-	attackMove: function(x, z, targetClasses, queued = false) {
-		Engine.PostCommand(PlayerID,{"type": "attack-walk", "entities": [this.id()], "x": x, "z": z, "targetClasses": targetClasses, "queued": queued });
+	attackMove: function(x, z, targetClasses, prefAttackType = "undefined", queued = false) {
+		Engine.PostCommand(PlayerID,{ "type": "attack-walk", "entities": [this.id()], "x": x, "z": z, "targetClasses": targetClasses, "prefAttackType": prefAttackType, "queued": queued });
 		return this;
 	},
 
@@ -791,8 +792,8 @@
 		return this;
 	},
 
-	attack: function(unitId, allowCapture = true, queued = false) {
-		Engine.PostCommand(PlayerID,{"type": "attack", "entities": [this.id()], "target": unitId, "allowCapture": allowCapture, "queued": queued});
+	attack: function(unitId, prefAttackType = undefined, queued = false) {
+		Engine.PostCommand(PlayerID,{ "type": "attack", "entities": [this.id()], "target": unitId, "prefAttackType": prefAttackType, "queued": queued });
 		return this;
 	},
 
Index: binaries/data/mods/public/simulation/ai/common-api/entitycollection.js
===================================================================
--- binaries/data/mods/public/simulation/ai/common-api/entitycollection.js	(revision 18612)
+++ binaries/data/mods/public/simulation/ai/common-api/entitycollection.js	(working copy)
@@ -156,7 +156,7 @@
 m.EntityCollection.prototype.attackMove = function(x, z, targetClasses, queued)
 {
 	queued = queued || false;
-	Engine.PostCommand(PlayerID,{"type": "attack-walk", "entities": this.toIdArray(), "x": x, "z": z, "targetClasses": targetClasses, "queued": queued});
+	Engine.PostCommand(PlayerID, { "type": "attack-walk", "entities": this.toIdArray(), "x": x, "z": z, "targetClasses": targetClasses, "queued": queued, "prefAttackType": undefined });
 	return this;
 };
 
@@ -182,7 +182,7 @@
 
 m.EntityCollection.prototype.attack = function(unitId)
 {
-	Engine.PostCommand(PlayerID,{"type": "attack", "entities": this.toIdArray(), "target": unitId, "queued": false});
+	Engine.PostCommand(PlayerID,{ "type": "attack", "entities": this.toIdArray(), "target": unitId, "queued": false, "prefAttackType": undefined });
 	return this;
 };
 
Index: binaries/data/mods/public/simulation/ai/petra/attackPlan.js
===================================================================
--- binaries/data/mods/public/simulation/ai/petra/attackPlan.js	(revision 18612)
+++ binaries/data/mods/public/simulation/ai/petra/attackPlan.js	(working copy)
@@ -1237,13 +1237,13 @@
 				{
 					if (this.isSiegeUnit(gameState, ent))	// needed as mauryan elephants are not filtered out
 						continue;
-					ent.attack(attacker.id(), !this.noCapture.has(attacker.id()));
+					ent.attack(attacker.id(), m.getPrefAttackType(ent, attacker, this.noCapture));
 					ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time);
 				}
 				// And if this attacker is a non-ranged siege unit and our unit also, attack it
 				if (this.isSiegeUnit(gameState, attacker) && attacker.hasClass("Melee") && ourUnit.hasClass("Melee"))
 				{
-					ourUnit.attack(attacker.id(), false);
+					ourUnit.attack(attacker.id(), "Melee");
 					ourUnit.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time);
 				}
 			}
@@ -1260,7 +1260,7 @@
 					let collec = this.unitCollection.filter(API3.Filters.byClass("Melee")).filterNearest(ourUnit.position(), 5);
 					for (let ent of collec.values())
 					{
-						ent.attack(attacker.id(), false);
+						ent.attack(attacker.id(), m.getPrefAttackType(ent, attacker, this.noCapture));
 						ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time);
 					}
 				}
@@ -1279,7 +1279,7 @@
 								continue;
 						}
 					}
-					ourUnit.attack(attacker.id(), !this.noCapture.has(attacker.id()));
+					ourUnit.attack(attacker.id(), m.getPrefAttackType(ourUnit, attacker, this.noCapture));
 					ourUnit.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time);
 				}
 			}
@@ -1459,12 +1459,12 @@
 						return valb - vala;
 					});
 					if (mStruct[0].hasClass("Gates"))
-						ent.attack(mStruct[0].id(), false);
+						ent.attack(mStruct[0].id(), m.getPrefAttackType(ent, mStruct[0], this.noCapture));
 					else
 					{
 						let rand = Math.floor(Math.random() * mStruct.length * 0.2);
-						let newTargetId = mStruct[rand].id();
-						ent.attack(newTargetId, !this.noCapture.has(newTargetId));
+						let newTarget = mStruct[rand];
+						ent.attack(newTarget.id(), m.getPrefAttackType(ent, newTarget, this.noCapture));
 					}
 				}
 				else
@@ -1522,8 +1522,8 @@
 						return valb - vala;
 					});
 					let rand = Math.floor(Math.random() * mUnit.length * 0.1);
-					let newTargetId = mUnit[rand].id();
-					ent.attack(newTargetId, !this.noCapture.has(newTargetId));
+					let newTarget = mUnit[rand];
+					ent.attack(newTarget.id(), m.getPrefAttackType(ent, newTarget, this.noCapture));
 				}
 				else if (this.isBlocked)
 					ent.attack(this.target.id(), false);
@@ -1570,12 +1570,12 @@
 							return valb - vala;
 						});
 						if (mStruct[0].hasClass("Gates"))
-							ent.attack(mStruct[0].id(), false);
+							ent.attack(mStruct[0].id(), m.getPrefAttackType(ent, mStruct[0], this.noCapture));
 						else
 						{
 							let rand = Math.floor(Math.random() * mStruct.length * 0.2);
-							let newTargetId = mStruct[rand].id();
-							ent.attack(newTargetId, !this.noCapture.has(newTargetId));
+							let newTarget = mStruct[rand];
+							ent.attack(newTarget.id(), m.getPrefAttackType(ent, newTarget, this.noCapture));
 						}
 					}
 					else if (needsUpdate)  // really nothing   let's try to help our nearest unit
@@ -1582,6 +1582,7 @@
 					{
 						let distmin = Math.min();
 						let attackerId;
+						let attacker;
 						this.unitCollection.forEach( function (unit) {
 							if (!unit.position())
 								return;
@@ -1591,11 +1592,14 @@
 							let dist = API3.SquareVectorDistance(unit.position(), ent.position());
 							if (dist > distmin)
 								return;
+							if (!gameState.getEntityById(unit.unitAIOrderData()[0].target)) 
+								return;
 							distmin = dist;
 							attackerId = unit.unitAIOrderData()[0].target;
+							attacker = gameState.getEntityById(attackerId);
 						});
 						if (attackerId)
-							ent.attack(attackerId, !this.noCapture.has(attackerId));
+							ent.attack(attackerId, m.getPrefAttackType(ent, attacker, this.noCapture));
 					}
 				}
 			}
@@ -1648,7 +1652,7 @@
 				continue;
 			if (!ent.isIdle())
 				continue;
-			ent.attack(attacker.id(), !this.noCapture.has(attacker.id()));
+			ent.attack(attacker.id(), !this.noCapture.has(attacker.id(), this.noCapture));
 		}
 		break;
 	}
@@ -2006,7 +2010,7 @@
 
 	if (this.noCapture.has(targetId))
 	{
-		ent.attack(targetId, false);
+		ent.attack(targetId, m.getPrefAttackType(ent, target, this.noCapture));
 		return true;
 	}
 
@@ -2018,7 +2022,7 @@
 	if (target.hasClass("Siege") && target.hasClass("Melee"))
 	{
 		this.noCapture.add(targetId);
-		ent.attack(targetId, false);
+		ent.attack(targetId, m.getPrefAttackType(ent, target, this.noCapture));
 		return true;
 	}
 
@@ -2035,7 +2039,7 @@
 	if (antiCapture >= this.captureStrength)
 	{
 		this.noCapture.add(targetId);
-		ent.attack(targetId, false);
+		ent.attack(targetId, "noCapture");
 		return true;
 	}
 
@@ -2044,7 +2048,7 @@
 		this.unitCollection.length < 2*target.garrisoned().length)
 	{
 		this.noCapture.add(targetId);
-		ent.attack(targetId, false);
+		ent.attack(targetId, "noCapture");
 		return true;
 	}
 
Index: binaries/data/mods/public/simulation/ai/petra/defenseArmy.js
===================================================================
--- binaries/data/mods/public/simulation/ai/petra/defenseArmy.js	(revision 18612)
+++ binaries/data/mods/public/simulation/ai/petra/defenseArmy.js	(working copy)
@@ -77,7 +77,7 @@
 	{
 		this.assignedTo[entID] = idFoe;
 		this.assignedAgainst[idFoe].push(entID);
-		ent.attack(idFoe, m.allowCapture(ent, foeEnt), queued);
+		ent.attack(idFoe, m.getPrefAttackType(ent, foeEnt), queued);
 	}
 	else
 		gameState.ai.HQ.navalManager.requireTransport(gameState, ent, ownIndex, foeIndex, foePosition);
@@ -115,8 +115,12 @@
 		else if (orderData.length && orderData[0].target && orderData[0].attackType && orderData[0].attackType === "Capture")
 		{
 			let target = gameState.getEntityById(orderData[0].target);
-			if (target && !m.allowCapture(ent, target))
-				ent.attack(orderData[0].target, false);
+			if (target)
+			{
+				let prefAttackType = m.getPrefAttackType(ent, target);
+				if (prefAttackType !== "Capture")
+					ent.attack(orderData[0].target, prefAttackType);
+			}
 		}
 	}
 
Index: binaries/data/mods/public/simulation/ai/petra/entityExtend.js
===================================================================
--- binaries/data/mods/public/simulation/ai/petra/entityExtend.js	(revision 18612)
+++ binaries/data/mods/public/simulation/ai/petra/entityExtend.js	(working copy)
@@ -81,11 +81,16 @@
 	return strength * ent.maxHitpoints() / 100.0;
 };
 
-/** Decide if we should try to capture or destroy */
-m.allowCapture = function(ent, target)
+// Decide if we should try to capture, melee or range attack
+// TODO make this function less hacky
+m.getPrefAttackType = function(ent, target, noCapture)
 {
-	return !target.hasClass("Siege") || !ent.hasClass("Melee") ||
-		!target.isGarrisonHolder() || !target.garrisoned().length;
+	if (target.hasClass("Siege") || (noCapture && noCapture.has(target.id())))
+		return ent.hasClass("Melee") ? "Melee" : "Range"; // Don't capture for now
+
+	if (target.isCapturable() && (!target.isGarrisonHolder() || !target.garrisoned().length))
+		return "Capture";
+	return "noCapture";
 };
 
 /** Makes the worker deposit the currently carried resources at the closest accessible dropsite */
Index: binaries/data/mods/public/simulation/components/Armour.js
===================================================================
--- binaries/data/mods/public/simulation/components/Armour.js	(revision 18612)
+++ binaries/data/mods/public/simulation/components/Armour.js	(working copy)
@@ -51,26 +51,18 @@
 	if (this.invulnerable) 
 		return { "killed": false, "change": 0 };
 
-	// Adjust damage values based on armour; exponential armour: damage = attack * 0.9^armour
-	var armourStrengths = this.GetArmourStrengths();
-	var adjHack = hack * Math.pow(0.9, armourStrengths.hack);
-	var adjPierce = pierce * Math.pow(0.9, armourStrengths.pierce);
-	var adjCrush = crush * Math.pow(0.9, armourStrengths.crush);
+	let damage = this.GetDamage(hack, pierce, crush);
 
-	// Total is sum of individual damages
-	// Don't bother rounding, since HP is no longer integral.
-	var total = adjHack + adjPierce + adjCrush;
-
 	// Reduce health
-	var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
-	return cmpHealth.Reduce(total);
+	let cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
+	return cmpHealth.Reduce(damage);
 };
 
 Armour.prototype.GetArmourStrengths = function()
 {
 	// Work out the armour values with technology effects
-	var applyMods = (type, foundation) => {
-		var strength;
+	let applyMods = (type, foundation) => {
+		let strength;
 		if (foundation) 
 		{
 			strength = +this.template.Foundation[type];
@@ -82,7 +74,7 @@
 		return ApplyValueModificationsToEntity("Armour/" + type, strength, this.entity);
 	};
 	
-	var foundation = Engine.QueryInterface(this.entity, IID_Foundation) && this.template.Foundation;
+	let foundation = Engine.QueryInterface(this.entity, IID_Foundation) && this.template.Foundation;
 
 	return {
 		"hack": applyMods("Hack", foundation),
@@ -91,4 +83,17 @@
 	};
 };
 
+Armour.prototype.GetDamage = function(hack, pierce, crush)
+{
+	// Adjust damage values based on armour; exponential armour: damage = attack * 0.9^armour
+	let armourStrengths = this.GetArmourStrengths();
+	let adjHack = hack * Math.pow(0.9, armourStrengths.hack);
+	let adjPierce = pierce * Math.pow(0.9, armourStrengths.pierce);
+	let adjCrush = crush * Math.pow(0.9, armourStrengths.crush);
+
+	// Total is sum of individual damages
+	// Don't bother rounding, since HP is no longer integral.
+	return adjHack + adjPierce + adjCrush;
+};
+
 Engine.RegisterComponentType(IID_DamageReceiver, "Armour", Armour);
Index: binaries/data/mods/public/simulation/components/Attack.js
===================================================================
--- binaries/data/mods/public/simulation/components/Attack.js	(revision 18612)
+++ binaries/data/mods/public/simulation/components/Attack.js	(working copy)
@@ -208,33 +208,48 @@
 	return [];
 };
 
-Attack.prototype.CanAttack = function(target)
+Attack.prototype.CanAttack = function(target, wantedType)
 {
 	let cmpFormation = Engine.QueryInterface(target, IID_Formation);
 	if (cmpFormation)
 		return true;
 
+	if (wantedType && !this.template[wantedType])
+		return false;
+
 	let cmpThisPosition = Engine.QueryInterface(this.entity, IID_Position);
 	let cmpTargetPosition = Engine.QueryInterface(target, IID_Position);
 	if (!cmpThisPosition || !cmpTargetPosition || !cmpThisPosition.IsInWorld() || !cmpTargetPosition.IsInWorld())
 		return false;
 
-	// Check if the relative height difference is larger than the attack range
-	// If the relative height is bigger, it means they will never be able to
-	// reach each other, no matter how close they come.
-	let heightDiff = Math.abs(cmpThisPosition.GetHeightOffset() - cmpTargetPosition.GetHeightOffset());
-
 	const cmpIdentity = Engine.QueryInterface(target, IID_Identity);
 	if (!cmpIdentity)
 		return undefined;
 
+	let cmpEntityPlayer = QueryOwnerInterface(this.entity);
+	let cmpTargetPlayer = QueryOwnerInterface(target);
+	if (!cmpTargetPlayer || !cmpEntityPlayer)
+		return false;
+
 	const targetClasses = cmpIdentity.GetClassesList();
+	let cmpCapturable = QueryMiragedInterface(target, IID_Capturable);
+	if (targetClasses.indexOf("Domestic") == -1 && !cmpEntityPlayer.IsEnemy(cmpTargetPlayer.GetPlayerID()) &&
+	   (!cmpCapturable || !cmpCapturable.CanCapture(cmpEntityPlayer.GetPlayerID())))
+		return false;
 
+	// Check if the relative height difference is larger than the attack range
+	// If the relative height is bigger, it means they will never be able to
+	// reach each other, no matter how close they come.
+	let heightDiff = Math.abs(cmpThisPosition.GetHeightOffset() - cmpTargetPosition.GetHeightOffset());
+
 	for (let type of this.GetAttackTypes())
 	{
-		if (type == "Capture" && !QueryMiragedInterface(target, IID_Capturable))
+		if (wantedType && type != wantedType)
 			continue;
 
+		if (type == "Capture" && !cmpCapturable)
+			continue;
+
 		if (heightDiff > this.GetRange(type).max)
 			continue;
 
@@ -291,7 +306,7 @@
 	return ret;
 };
 
-Attack.prototype.GetBestAttackAgainst = function(target, allowCapture)
+Attack.prototype.GetBestAttackAgainst = function(target, prefAttackType)
 {
 	let cmpFormation = Engine.QueryInterface(target, IID_Formation);
 	if (cmpFormation)
@@ -298,7 +313,8 @@
 	{
 		// TODO: Formation against formation needs review
 		let types = this.GetAttackTypes();
-		return ["Ranged", "Melee", "Capture"].find(attack => types.indexOf(attack) != -1);
+		return ["Ranged", "Melee", "Capture"].find(attack => types.indexOf(attack) != -1
+			&& (!prefAttackType || "no" + attack != prefAttackType));
 	}
 
 	let cmpIdentity = Engine.QueryInterface(target, IID_Identity);
@@ -305,6 +321,14 @@
 	if (!cmpIdentity)
 		return undefined;
 
+	let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
+	if (!cmpPosition || !cmpPosition.IsInWorld())
+		return undefined;
+
+	// If we are visisble garrisoned always do ranged attack if we can.
+	if (cmpPosition.GetTurretParent() != INVALID_ENTITY)
+		return this.template.Ranged && "Ranged";
+
 	let targetClasses = cmpIdentity.GetClassesList();
 	let isTargetClass = className => targetClasses.indexOf(className) != -1;
 
@@ -313,26 +337,94 @@
 		return "Slaughter";
 
 	let attack = this;
+	let isAllowed = function (type) { return !attack.GetRestrictedClasses(type).some(isTargetClass)
+		&& (!prefAttackType || "no" + type != prefAttackType); };
+
 	let types = this.GetAttackTypes().filter(type => !attack.GetRestrictedClasses(type).some(isTargetClass));
+	if (!types.length)
+		return undefined;
 
-	// check if the target is capturable
-	let captureIndex = types.indexOf("Capture");
-	if (captureIndex != -1)
+	if (prefAttackType && prefAttackType != "Capture" && types.indexOf(prefAttackType) != -1)
+		return prefAttackType;
+	else if (!prefAttackType || prefAttackType == "Capture")
 	{
-		let cmpCapturable = QueryMiragedInterface(target, IID_Capturable);
-
-		let cmpPlayer = QueryOwnerInterface(this.entity);
-		if (allowCapture && cmpPlayer && cmpCapturable && cmpCapturable.CanCapture(cmpPlayer.GetPlayerID()))
-			return "Capture";
-		// not captureable, so remove this attack
-		types.splice(captureIndex, 1);
+		// check if the target is capturable
+		let captureIndex = types.indexOf("Capture");
+		if (captureIndex != -1)
+		{
+			let cmpCapturable = QueryMiragedInterface(target, IID_Capturable);
+			let cmpPlayer = QueryOwnerInterface(this.entity);
+			if (cmpPlayer && cmpCapturable && cmpCapturable.CanCapture(cmpPlayer.GetPlayerID()))
+				return "Capture";
+			// not captureable, so remove this attack
+			types.splice(captureIndex, 1);
+		}
 	}
 
-	let isPreferred = className => attack.GetPreferredClasses(className).some(isTargetClass);
+	// ignore charges for now: TODO implement these
+	let chargeIndex = types.indexOf("Charge");
+	if (chargeIndex != -1)
+		types.splice(chargeIndex, 1);
 
-	return types.sort((a, b) =>
-		(types.indexOf(a) + (isPreferred(a) ? types.length : 0)) -
-		(types.indexOf(b) + (isPreferred(b) ? types.length : 0))).pop();
+	if (!types.length)
+		return undefined;
+
+	// assume ranged and/or melee attack left
+	// TODO stop assuming that?
+	let meleeIndex = types.indexOf("Melee");
+	let rangedIndex = types.indexOf("Ranged");
+	if (meleeIndex != -1 && rangedIndex != -1)
+	{
+		let selfPosition = cmpPosition.GetPosition2D();
+		let cmpTargetPosition = Engine.QueryInterface(target, IID_Position);
+		if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld())
+			return undefined;
+		let targetPosition = cmpTargetPosition.GetPosition2D();
+		let distanceToSquared = targetPosition.distanceToSquared(selfPosition);
+		if (distanceToSquared <= Math.pow(this.GetRange("Ranged").min, 2))
+			return "Melee";
+
+		let cmpDamageReceiver = Engine.QueryInterface(target, IID_DamageReceiver);
+		if (!cmpDamageReceiver)
+			return undefined;
+
+		let attackStrengthsMelee = this.GetAttackStrengths("Melee");
+		let attackBonusMelee = this.GetAttackBonus("Melee", target);
+		let DPSMelee = cmpDamageReceiver.GetDamage(
+			attackStrengthsMelee.hack * attackBonusMelee,
+			attackStrengthsMelee.pierce * attackBonusMelee,
+			attackStrengthsMelee.crush * attackBonusMelee
+			) / this.GetTimers("Melee").repeat;
+
+		let attackStrengthsRanged = this.GetAttackStrengths("Ranged");
+		let attackBonusRanged = this.GetAttackBonus("Ranged", target);
+		let DPSRanged = cmpDamageReceiver.GetDamage(
+			attackStrengthsRanged.hack * attackBonusRanged,
+			attackStrengthsRanged.pierce * attackBonusRanged,
+			attackStrengthsRanged.crush * attackBonusRanged
+			) / this.GetTimers("Ranged").repeat;
+
+		// When we are out of range do DPS only else take distance into account
+		let maxRange = this.GetFullAttackRange().max;
+		if (distanceToSquared > 1.2 * Math.pow(maxRange, 2))
+		{
+			if (DPSRanged > DPSMelee)
+				return "Ranged";
+			return "Melee";
+		}
+
+		let strenghtRanged = DPSRanged * distanceToSquared / Math.pow(maxRange, 2);
+		let strenghtMelee = DPSMelee * (1 - distanceToSquared / Math.pow(maxRange, 2));
+
+		if (strenghtRanged > strenghtMelee)
+			return "Ranged";
+		return "Melee";
+	}
+	else if (meleeIndex != -1)
+		return "Melee";
+	else if (rangedIndex != -1)
+		return "Ranged";
+	return types[0];
 };
 
 Attack.prototype.CompareEntitiesByPreference = function(a, b)
@@ -496,7 +588,7 @@
 
 		let horizDistance = targetPosition.horizDistanceTo(selfPosition);
 
-		// This is an approximation of the time ot the target, it assumes that the target has a constant radial
+		// This is an approximation of the time to the target, it assumes that the target has a constant radial
 		// velocity, but since units move in straight lines this is not true.  The exact value would be more
 		// difficult to calculate and I think this is sufficiently accurate.  (I tested and for cavalry it was
 		// about 5% of the units radius out in the worst case)
@@ -572,7 +664,7 @@
 			"target": target,
 			"attacker": this.entity,
 			"multiplier": this.GetAttackBonus(type, target),
-			"type":type
+			"type": type
 		});
 	}
 };
Index: binaries/data/mods/public/simulation/components/GarrisonHolder.js
===================================================================
--- binaries/data/mods/public/simulation/components/GarrisonHolder.js	(revision 18612)
+++ binaries/data/mods/public/simulation/components/GarrisonHolder.js	(working copy)
@@ -10,6 +10,14 @@
 		"</attribute>" +
 		"<text/>" +
 	"</element>" +
+	"<optional>" +
+		"<element name='NeededAttackTypes' a:help='AttackType which are needed to garrison inside this holder (from Identity)'>" +
+			"<attribute name='datatype'>" +
+				"<value>tokens</value>" +
+			"</attribute>" +
+			"<text/>" +
+		"</element>" +
+	"</optional>" +
 	"<element name='EjectHealth' a:help='Percentage of maximum health below which this holder no longer allows garrisoning'>" +
 		"<ref name='nonNegativeDecimal'/>" +
 	"</element>" +
@@ -119,6 +127,15 @@
 };
 
 /**
+ * Returns an array of attack types which the unit garrisoned inside this
+ * particualar entity needs to have. Obtained from the entity's template 
+ */
+GarrisonHolder.prototype.GetNeededAttackTypes = function()
+{
+	return this.template.NeededAttackTypes ? this.template.NeededAttackTypes._string : undefined;
+};
+
+/**
  * Get Maximum pop which can be garrisoned
  */
 GarrisonHolder.prototype.GetCapacity = function()
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js	(revision 18612)
+++ binaries/data/mods/public/simulation/components/GuiInterface.js	(working copy)
@@ -359,6 +359,7 @@
 			"entities": cmpGarrisonHolder.GetEntities(),
 			"buffHeal": cmpGarrisonHolder.GetHealRate(),
 			"allowedClasses": cmpGarrisonHolder.GetAllowedClasses(),
+			"neededAttackTypes": cmpGarrisonHolder.GetNeededAttackTypes(),
 			"capacity": cmpGarrisonHolder.GetCapacity(),
 			"garrisonedEntitiesCount": cmpGarrisonHolder.GetGarrisonedEntitiesCount()
 		};
@@ -440,6 +441,7 @@
 		let types = cmpAttack.GetAttackTypes();
 		if (types.length)
 			ret.attack = {};
+
 		for (let type of types)
 		{
 			ret.attack[type] = cmpAttack.GetAttackStrengths(type);
@@ -1829,16 +1831,8 @@
 	if (!cmpAttack)
 		return false;
 
-	let cmpEntityPlayer = QueryOwnerInterface(data.entity, IID_Player);
-	let cmpTargetPlayer = QueryOwnerInterface(data.target, IID_Player);
-	if (!cmpEntityPlayer || !cmpTargetPlayer)
-		return false;
-
-	// if the owner is an enemy, it's up to the attack component to decide
-	if (cmpEntityPlayer.IsEnemy(cmpTargetPlayer.GetPlayerID()))
-		return cmpAttack.CanAttack(data.target);
-
-	return false;
+	let attackType = data.type || undefined;
+	return cmpAttack.CanAttack(data.target, attackType);
 };
 
 /*
Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js	(revision 18612)
+++ binaries/data/mods/public/simulation/components/UnitAI.js	(working copy)
@@ -420,7 +420,7 @@
 		}
 
 		// Work out how to attack the given target
-		var type = this.GetBestAttackAgainst(this.order.data.target, this.order.data.allowCapture);
+		let type = this.GetBestAttackAgainst(this.order.data.target, this.order.data.prefAttackType);
 		if (!type)
 		{
 			// Oops, we can't attack at all
@@ -563,7 +563,7 @@
 		if (this.MustKillGatherTarget(this.order.data.target))
 		{
 			// Make sure we can attack the target, else we'll get very stuck
-			if (!this.GetBestAttackAgainst(this.order.data.target, false))
+			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
@@ -587,7 +587,7 @@
 				return;
 			}
 
-			this.PushOrderFront("Attack", { "target": this.order.data.target, "force": false, "hunting": true, "allowCapture": false });
+			this.PushOrderFront("Attack", { "target": this.order.data.target, "force": false, "hunting": true, "prefAttackType": undefined });
 			return;
 		}
 
@@ -842,9 +842,9 @@
 		},
 
 		"Order.Attack": function(msg) {
-			var target = msg.data.target;
-			var allowCapture = msg.data.allowCapture;
-			var cmpTargetUnitAI = Engine.QueryInterface(target, IID_UnitAI);
+			let target = msg.data.target;
+			let prefAttackType = msg.data.prefAttackType;
+			let cmpTargetUnitAI = Engine.QueryInterface(target, IID_UnitAI);
 			if (cmpTargetUnitAI && cmpTargetUnitAI.IsFormationMember())
 				target = cmpTargetUnitAI.GetFormationController();
 
@@ -863,7 +863,7 @@
 				this.FinishOrder();
 				return;
 			}
-			this.CallMemberFunction("Attack", [target, false, allowCapture]);
+			this.CallMemberFunction("Attack", [target, false, prefAttackType]);
 			if (cmpAttack.CanAttackAsFormation())
 				this.SetNextState("COMBAT.ATTACKING");
 			else
@@ -918,7 +918,7 @@
 					return;
 				}
 
-				this.PushOrderFront("Attack", { "target": msg.data.target, "hunting": true, "allowCapture": false });
+				this.PushOrderFront("Attack", { "target": msg.data.target, "hunting": true, "prefAttackType": undefined });
 				return;
 			}
 
@@ -1155,7 +1155,7 @@
 
 				"MoveCompleted": function(msg) {
 					var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
-					this.CallMemberFunction("Attack", [this.order.data.target, false, this.order.data.allowCapture]);
+					this.CallMemberFunction("Attack", [this.order.data.target, false, this.order.data.prefAttackType]);
 					if (cmpAttack.CanAttackAsFormation())
 						this.SetNextState("COMBAT.ATTACKING");
 					else
@@ -1166,8 +1166,8 @@
 			"ATTACKING": {
 				// Wait for individual members to finish
 				"enter": function(msg) {
-					var target = this.order.data.target;
-					var allowCapture = this.order.data.allowCapture;
+					let target = this.order.data.target;
+					let prefAttackType = this.order.data.prefAttackType;
 					// Check if we are already in range, otherwise walk there
 					if (!this.CheckTargetAttackRange(target, target))
 					{
@@ -1174,7 +1174,7 @@
 						if (this.TargetIsAlive(target) && this.CheckTargetVisible(target))
 						{
 							this.FinishOrder();
-							this.PushOrderFront("Attack", { "target": target, "force": false, "allowCapture": allowCapture });
+							this.PushOrderFront("Attack", { "target": target, "force": false, "prefAttackType": prefAttackType });
 							return true;
 						}
 						this.FinishOrder();
@@ -1190,8 +1190,8 @@
 				},
 
 				"Timer": function(msg) {
-					var target = this.order.data.target;
-					var allowCapture = this.order.data.allowCapture;
+					let target = this.order.data.target;
+					let prefAttackType = this.order.data.prefAttackType;
 					// Check if we are already in range, otherwise walk there
 					if (!this.CheckTargetAttackRange(target, target))
 					{
@@ -1198,7 +1198,7 @@
 						if (this.TargetIsAlive(target) && this.CheckTargetVisible(target))
 						{
 							this.FinishOrder();
-							this.PushOrderFront("Attack", { "target": target, "force": false, "allowCapture": allowCapture });
+							this.PushOrderFront("Attack", { "target": target, "force": false, "prefAttackType": prefAttackType });
 							return;
 						}
 						this.FinishOrder();
@@ -1410,7 +1410,7 @@
 
 			// target the unit
 			if (this.CheckTargetVisible(msg.data.attacker))
-				this.PushOrderFront("Attack", { "target": msg.data.attacker, "force": false, "allowCapture": true });
+				this.PushOrderFront("Attack", { "target": msg.data.attacker, "force": false, "prefAttackType": undefined });
 			else
 			{
 				var cmpPosition = Engine.QueryInterface(msg.data.attacker, IID_Position);
@@ -1805,6 +1805,8 @@
 						// Can't reach it - try to chase after it
 						if (this.ShouldChaseTargetedEntity(target, this.order.data.force))
 						{
+							if (!this.order.data.force)
+								this.order.data.attackType = this.GetBestAttackAgainst(target);
 							if (this.MoveToTargetAttackRange(target, this.order.data.attackType))
 							{
 								this.SetNextState("COMBAT.CHASING");
@@ -1916,6 +1918,12 @@
 						// Can't reach it - try to chase after it
 						if (this.ShouldChaseTargetedEntity(target, this.order.data.force))
 						{
+							if (!this.order.data.force)
+							{
+								let type = this.GetBestAttackAgainst(target);
+								if (type)
+									this.order.data.attackType = type;
+							}
 							if (this.MoveToTargetRange(target, IID_Attack, this.order.data.attackType))
 							{
 								this.SetNextState("COMBAT.CHASING");
@@ -4549,12 +4557,12 @@
 	return distance < range;
 };
 
-UnitAI.prototype.GetBestAttackAgainst = function(target, allowCapture)
+UnitAI.prototype.GetBestAttackAgainst = function(target, prefAttackType)
 {
 	var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
 	if (!cmpAttack)
 		return undefined;
-	return cmpAttack.GetBestAttackAgainst(target, allowCapture);
+	return cmpAttack.GetBestAttackAgainst(target, prefAttackType);
 };
 
 UnitAI.prototype.GetAttackBonus = function(type, target)
@@ -4572,11 +4580,12 @@
  */
 UnitAI.prototype.AttackVisibleEntity = function(ents, forceResponse)
 {
-	var target = ents.find(target => this.CanAttack(target, forceResponse));
+	let target = ents.find(target => this.CanAttack(target, forceResponse));
 	if (!target)
 		return false;
 
-	this.PushOrderFront("Attack", { "target": target, "force": false, "forceResponse": forceResponse, "allowCapture": true });
+	let prefAttackType = this.GetStance().respondStandGround && "Ranged";
+	this.PushOrderFront("Attack", { "target": target, "force": false, "forceResponse": forceResponse, "prefAttackType": prefAttackType });
 	return true;
 };
 
@@ -4589,13 +4598,13 @@
 {
 	var target = ents.find(target =>
 		this.CanAttack(target, forceResponse)
-		&& this.CheckTargetDistanceFromHeldPosition(target, IID_Attack, this.GetBestAttackAgainst(target, true))
+		&& this.CheckTargetDistanceFromHeldPosition(target, IID_Attack, this.GetBestAttackAgainst(target))
 		&& (this.GetStance().respondChaseBeyondVision || this.CheckTargetIsInVisionRange(target))
 	);
 	if (!target)
 		return false;
 
-	this.PushOrderFront("Attack", { "target": target, "force": false, "forceResponse": forceResponse, "allowCapture": true });
+	this.PushOrderFront("Attack", { "target": target, "force": false, "forceResponse": forceResponse, "prefAttackType": undefined });
 	return true;
 };
 
@@ -4986,9 +4995,9 @@
  * to a player order, and so is forced.
  * If targetClasses is given, only entities matching the targetClasses can be attacked.
  */
-UnitAI.prototype.WalkAndFight = function(x, z, targetClasses, queued)
+UnitAI.prototype.WalkAndFight = function(x, z, targetClasses, queued, prefAttackType)
 {
-	this.AddOrder("WalkAndFight", { "x": x, "z": z, "targetClasses": targetClasses, "force": true }, queued);
+	this.AddOrder("WalkAndFight", { "x": x, "z": z, "targetClasses": targetClasses, "force": true, "prefAttackType": prefAttackType }, queued);
 };
 
 /**
@@ -5009,7 +5018,7 @@
 /**
  * Adds attack order to the queue, forced by the player.
  */
-UnitAI.prototype.Attack = function(target, queued, allowCapture)
+UnitAI.prototype.Attack = function(target, queued, prefAttackType)
 {
 	if (!this.CanAttack(target))
 	{
@@ -5021,7 +5030,7 @@
 			this.WalkToTarget(target, queued);
 		return;
 	}
-	this.AddOrder("Attack", { "target": target, "force": true, "allowCapture": allowCapture}, queued);
+	this.AddOrder("Attack", { "target": target, "force": true, "prefAttackType": prefAttackType }, queued);
 };
 
 /**
@@ -5450,7 +5459,7 @@
 					if (targetClasses.vetoEntities && targetClasses.vetoEntities[targ])
 						continue;
 				}
-				this.PushOrderFront("Attack", { "target": targ, "force": true, "allowCapture": true });
+				this.PushOrderFront("Attack", { "target": targ, "force": true, "prefAttackType": undefined });
 				return true;
 			}
 		}
@@ -5476,7 +5485,7 @@
 			if (targetClasses.vetoEntities && targetClasses.vetoEntities[targ])
 				continue;
 		}
-		this.PushOrderFront("Attack", { "target": targ, "force": true, "allowCapture": true });
+		this.PushOrderFront("Attack", { "target": targ, "force": true, "prefAttackType": undefined });
 		return true;
 	}
 
@@ -5676,12 +5685,12 @@
 	if (this.IsFormationController())
 		return true;
 
-	var cmpGarrisonHolder = Engine.QueryInterface(target, IID_GarrisonHolder);
+	let cmpGarrisonHolder = Engine.QueryInterface(target, IID_GarrisonHolder);
 	if (!cmpGarrisonHolder)
 		return false;
 
 	// Verify that the target is owned by this entity's player or a mutual ally of this player
-	var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
+	let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
 	if (!cmpOwnership || !(IsOwnedByPlayer(cmpOwnership.GetOwner(), target) || IsOwnedByMutualAllyOfPlayer(cmpOwnership.GetOwner(), target)))
 		return false;
 
@@ -5691,6 +5700,15 @@
 	if (this.IsAnimal())
 		return false;
 
+	let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity)
+	if (!cmpIdentity)
+		return false;
+
+	let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack)
+	let attackTypes = cmpAttack ? cmpAttack.GetAttackTypes() : [];
+	if (!MatchesClassList(cmpIdentity.GetClassesList(), cmpGarrisonHolder.GetAllowedClasses()) || 
+	    !HasNeededAttackTypes(attackTypes, cmpGarrisonHolder.GetNeededAttackTypes()))
+		return false;
 	return true;
 };
 
Index: binaries/data/mods/public/simulation/components/tests/test_Attack.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_Attack.js	(revision 18612)
+++ binaries/data/mods/public/simulation/components/tests/test_Attack.js	(working copy)
@@ -22,7 +22,8 @@
 		});
 
 		AddMock(playerEnt1, IID_Player, {
-			"GetPlayerID": () => 1
+			"GetPlayerID": () => 1,
+			"IsEnemy": () => true
 		});
 	}
 
@@ -30,6 +31,7 @@
 
 	AddMock(attacker, IID_Position, {
 		"IsInWorld": () => true,
+		"GetTurretParent": () => INVALID_ENTITY,
 		"GetHeightOffset": () => 5
 	});
 
@@ -87,8 +89,13 @@
 		"HasClass": className => className == defenderClass
 	});
 
+	AddMock(defender, IID_Ownership, {
+		"GetOwner": () => 1
+	});
+
 	AddMock(defender, IID_Position, {
 		"IsInWorld": () => true,
+		"GetTurretParent": () => INVALID_ENTITY,
 		"GetHeightOffset": () => 0
 	});
 
@@ -148,12 +155,11 @@
 
 		TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender), true);
 
-		let allowCapturing = [true];
+		let prefAttackType = "Capture";
 		if (!isBuilding)
-			allowCapturing.push(false);
+			prefAttackType = bestAttack;
 
-		for (let allowCapturing of allowCapturing)
-			TS_ASSERT_EQUALS(cmpAttack.GetBestAttackAgainst(defender, allowCapturing), bestAttack);
+		TS_ASSERT_EQUALS(cmpAttack.GetBestAttackAgainst(defender, prefAttackType), bestAttack);
 	});
 }
 
Index: binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_UnitAI.js	(revision 18612)
+++ binaries/data/mods/public/simulation/components/tests/test_UnitAI.js	(working copy)
@@ -97,7 +97,7 @@
 	AddMock(unit, IID_Attack, {
 		GetRange: function() { return { "max": 10, "min": 0}; },
 		GetFullAttackRange: function() { return { "max": 40, "min": 0}; },
-		GetBestAttackAgainst: function(t) { return "melee"; },
+		GetBestAttackAgainst: function(t) { return "Melee"; },
 		GetPreference: function(t) { return 0; },
 		GetTimers: function() { return { "prepare": 500, "repeat": 1000 }; },
 		CanAttack: function(v) { return true; },
@@ -247,7 +247,7 @@
 		AddMock(unit + i, IID_Attack, {
 			GetRange: function() { return {"max":10, "min": 0}; },
 			GetFullAttackRange: function() { return { "max": 40, "min": 0}; },
-			GetBestAttackAgainst: function(t) { 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; },
Index: binaries/data/mods/public/simulation/helpers/Commands.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Commands.js	(revision 18612)
+++ binaries/data/mods/public/simulation/helpers/Commands.js	(working copy)
@@ -160,7 +160,7 @@
 	"attack-walk": function(player, cmd, data)
 	{
 		GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
-			cmpUnitAI.WalkAndFight(cmd.x, cmd.z, cmd.targetClasses, cmd.queued);
+			cmpUnitAI.WalkAndFight(cmd.x, cmd.z, cmd.targetClasses, cmd.queued, cmd.prefAttackType);
 		});
 	},
 
@@ -169,10 +169,8 @@
 		if (g_DebugCommands && !(IsOwnedByEnemyOfPlayer(player, cmd.target) || IsOwnedByNeutralOfPlayer(player, cmd.target)))
 			warn("Invalid command: attack target is not owned by enemy of player "+player+": "+uneval(cmd));
 
-		let allowCapture = cmd.allowCapture || cmd.allowCapture == null;
-
 		GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
-			cmpUnitAI.Attack(cmd.target, cmd.queued, allowCapture);
+			cmpUnitAI.Attack(cmd.target, cmd.queued, cmd.prefAttackType);
 		});
 	},
 
Index: binaries/data/mods/public/simulation/templates/template_structure_defense_wall_long.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_defense_wall_long.xml	(revision 18612)
+++ binaries/data/mods/public/simulation/templates/template_structure_defense_wall_long.xml	(working copy)
@@ -12,7 +12,8 @@
   </Health>
   <GarrisonHolder>
     <Max>5</Max>
-    <List datatype="tokens">Ranged+Infantry</List>
+    <List datatype="tokens">Infantry</List>
+    <NeededAttackTypes datatype="tokens">Ranged</NeededAttackTypes>
     <EjectHealth>0.1</EjectHealth>
     <EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
     <BuffHeal>0</BuffHeal>
Index: binaries/data/mods/public/simulation/templates/template_structure_defense_wall_medium.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_defense_wall_medium.xml	(revision 18612)
+++ binaries/data/mods/public/simulation/templates/template_structure_defense_wall_medium.xml	(working copy)
@@ -9,7 +9,8 @@
   </Cost>
   <GarrisonHolder>
     <Max>3</Max>
-    <List datatype="tokens">Ranged+Infantry</List>
+    <List datatype="tokens">Infantry</List>
+    <NeededAttackTypes datatype="tokens">Ranged</NeededAttackTypes>
     <EjectHealth>0.1</EjectHealth>
     <EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
     <BuffHeal>0</BuffHeal>
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged.xml	(revision 18612)
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged.xml	(working copy)
@@ -6,6 +6,14 @@
     <Crush>10</Crush>
   </Armour>
   <Attack>
+    <Melee>
+      <Hack>3</Hack>
+      <Pierce>0</Pierce>
+      <Crush>0</Crush>
+      <MaxRange>4.0</MaxRange>
+      <RepeatTime>1000</RepeatTime>
+      <PreferredClasses datatype="tokens">Human</PreferredClasses>
+    </Melee>
     <Ranged>
       <Hack>0</Hack>
       <Pierce>1.5</Pierce>
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged_archer.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged_archer.xml	(revision 18612)
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged_archer.xml	(working copy)
@@ -10,7 +10,7 @@
       <Pierce>6.0</Pierce>
       <Crush>0</Crush>
       <MaxRange>72.0</MaxRange>
-      <MinRange>0.0</MinRange>
+      <MinRange>10.0</MinRange>
       <ProjectileSpeed>120.0</ProjectileSpeed>
       <PrepareTime>1000</PrepareTime>
       <RepeatTime>1000</RepeatTime>
Index: binaries/data/mods/public/simulation/templates/units/pers_champion_infantry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/pers_champion_infantry.xml	(revision 18612)
+++ binaries/data/mods/public/simulation/templates/units/pers_champion_infantry.xml	(working copy)
@@ -1,5 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Entity parent="template_unit_champion_infantry_spearman">
+  <Attack>
+    <Ranged>
+      <Hack>0</Hack>
+      <Pierce>6.0</Pierce>
+      <Crush>0</Crush>
+      <MaxRange>65.0</MaxRange>
+      <MinRange>10.0</MinRange>
+      <ProjectileSpeed>120.0</ProjectileSpeed>
+      <PrepareTime>1000</PrepareTime>
+      <RepeatTime>1000</RepeatTime>
+      <Spread>3.0</Spread>
+    </Ranged>
+  </Attack>
   <Identity>
     <Civ>pers</Civ>
     <GenericName>Persian Immortal</GenericName>
