Index: binaries/data/mods/public/gui/session/input.js
===================================================================
--- binaries/data/mods/public/gui/session/input.js	(revision 12160)
+++ binaries/data/mods/public/gui/session/input.js	(working copy)
@@ -1439,8 +1439,30 @@
 }
 
 // Called by GUI when user clicks training button
-function addTrainingToQueue(selection, trainEntType)
+function addTrainingToQueue(selection, trainEntType, playerState)
 {
+	// Create list of buildings which can train trainEntType
+	var appropriateBuildings = [];
+	for each (var selectedEntity in selection)
+	{
+		var state = GetEntityState(selectedEntity);
+		var canTrain = state && state.production && state.production.entities.length && state.production.entities.indexOf(trainEntType) != -1;
+		appropriateBuildings.push(selectedEntity);
+	}
+
+	// Check trainEntType entity limit and count
+	template = GetTemplateData(trainEntType);
+	var trainingCategory = null;
+	if (template.trainingRestrictions)
+		trainingCategory = template.trainingRestrictions.category;
+	var trainEntLimit = undefined;
+	var trainEntCount = undefined;
+	if (trainingCategory && playerState.entityLimits[trainingCategory])
+	{
+		trainEntLimit = playerState.entityLimits[trainingCategory];
+		trainEntCount = playerState.entityCounts[trainingCategory]
+	}
+
 	if (Engine.HotkeyIsPressed("session.batchtrain"))
 	{
 		if (inputState == INPUT_BATCHTRAINING)
@@ -1477,8 +1499,15 @@
 	}
 	else
 	{
-		// Non-batched - just create a single entity
-		Engine.PostNetworkCommand({"type": "train", "template": trainEntType, "count": 1, "entities": selection});
+		// Non-batched - just create a single entity in each building
+		// (but no more than entity limit allows)
+		var buildingsForTraining = appropriateBuildings;
+		if (trainEntLimit)
+		{
+			var canBeTrainedCount = trainEntLimit - trainEntCount;
+			buildingsForTraining = buildingsForTraining.slice(0, canBeTrainedCount);
+		}
+		Engine.PostNetworkCommand({"type": "train", "template": trainEntType, "count": 1, "entities": buildingsForTraining});
 	}
 }
 
Index: binaries/data/mods/public/gui/session/unit_commands.js
===================================================================
--- binaries/data/mods/public/gui/session/unit_commands.js	(revision 12160)
+++ binaries/data/mods/public/gui/session/unit_commands.js	(working copy)
@@ -150,7 +150,7 @@
  * @param items Panel-specific data to construct the icons with.
  * @param callback Callback function to argument to execute when an item's icon gets clicked. Takes a single 'item' argument.
  */
-function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
+function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, callback)
 {
 	usedPanels[guiName] = 1;
 
@@ -359,7 +359,6 @@
 					tooltip += "\n" + getEntitySpeed(template);
 
 				tooltip += "\n\n[font=\"serif-bold-13\"]Shift-click[/font][font=\"serif-13\"] to train " + trainNum + ".[/font]";
-
 				break;
 				
 			case RESEARCH:
@@ -507,6 +506,19 @@
 		{
 			icon.sprite = "stretched:session/icons/" + item.icon;
 		}
+		else if (guiName == TRAINING)
+		{
+			if (template.icon)
+			{
+				var trainingCategory = null;
+				if (template.trainingRestrictions)
+					trainingCategory = template.trainingRestrictions.category;
+				var grayscale = "";
+				if (trainingCategory && playerState.entityLimits[trainingCategory] && playerState.entityCounts[trainingCategory] >= playerState.entityLimits[trainingCategory])
+					grayscale = "grayscale:";
+				icon.sprite = "stretched:" + grayscale + "session/portraits/" + template.icon;
+			}
+		}
 		else if (guiName == GATE)
 		{
 			var gateIcon;
@@ -671,7 +683,7 @@
 }
 
 // Sets up "unit barter panel" - special case for setupUnitPanel
-function setupUnitBarterPanel(unitEntState)
+function setupUnitBarterPanel(unitEntState, playerState)
 {
 	// Amount of player's resource to exchange
 	var amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
@@ -750,27 +762,32 @@
 	var player = Engine.GetPlayerID();
 	if (entState.player == player || g_DevSettings.controlAll)
 	{
+		// Get player state to check some constraints
+		// e.g. presence of a hero or build limits
+		var simState = Engine.GuiInterfaceCall("GetSimulationState");
+		var playerState = simState.players[player];
+
 		if (selection.length > 1)
-			setupUnitPanel(SELECTION, usedPanels, entState, g_Selection.groups.getTemplateNames(),
+			setupUnitPanel(SELECTION, usedPanels, entState, playerState, g_Selection.groups.getTemplateNames(),
 				function (entType) { changePrimarySelectionGroup(entType); } );
 
 		var commands = getEntityCommandsList(entState);
 		if (commands.length)
-			setupUnitPanel(COMMAND, usedPanels, entState, commands,
+			setupUnitPanel(COMMAND, usedPanels, entState, playerState, commands,
 				function (item) { performCommand(entState.id, item.name); } );
 
 		if (entState.garrisonHolder)
 		{
 			var groups = new EntityGroups();
 			groups.add(entState.garrisonHolder.entities);
-			setupUnitPanel(GARRISON, usedPanels, entState, groups.getTemplateNames(),
+			setupUnitPanel(GARRISON, usedPanels, entState, playerState, groups.getTemplateNames(),
 				function (item) { unload(entState.id, groups.getEntsByName(item)); } );
 		}
 
 		var formations = Engine.GuiInterfaceCall("GetAvailableFormations");
 		if (hasClass(entState, "Unit") && !hasClass(entState, "Animal") && !entState.garrisonHolder && formations.length)
 		{
-			setupUnitPanel(FORMATION, usedPanels, entState, formations,
+			setupUnitPanel(FORMATION, usedPanels, entState, playerState, formations,
 				function (item) { performFormation(entState.id, item); } );
 		}
 
@@ -779,7 +796,7 @@
 		var stances = ["violent", "aggressive", "passive", "defensive", "standground"];
 		if (hasClass(entState, "Unit") && !hasClass(entState, "Animal") && !entState.garrisonHolder && stances.length)
 		{
-			setupUnitPanel(STANCE, usedPanels, entState, stances,
+			setupUnitPanel(STANCE, usedPanels, entState, playerState, stances,
 				function (item) { performStance(entState.id, item); } );
 		}
 
@@ -787,7 +804,7 @@
 		if (entState.barterMarket)
 		{
 			usedPanels["Barter"] = 1;
-			setupUnitBarterPanel(entState);
+			setupUnitBarterPanel(entState, playerState);
 		}
 
 		var buildableEnts = [];
@@ -808,10 +825,10 @@
 
 		// The first selected entity's type has priority.
 		if (entState.buildEntities)
-			setupUnitPanel(CONSTRUCTION, usedPanels, entState, buildableEnts, startBuildingPlacement);
+			setupUnitPanel(CONSTRUCTION, usedPanels, entState, playerState, buildableEnts, startBuildingPlacement);
 		else if (entState.production && entState.production.entities)
-			setupUnitPanel(TRAINING, usedPanels, entState, trainableEnts,
-				function (trainEntType) { addTrainingToQueue(selection, trainEntType); } );
+			setupUnitPanel(TRAINING, usedPanels, entState, playerState, trainableEnts,
+				function (trainEntType) { addTrainingToQueue(selection, trainEntType, playerState); } );
 		else if (entState.trader)
 			setupUnitTradingPanel(usedPanels, entState, selection);
 		else
@@ -822,7 +839,7 @@
 				setupUnitPanel(CONSTRUCTION, usedPanels, entState, buildableEnts, startBuildingPlacement);
 			else if (trainableEnts.length)
 				setupUnitPanel(TRAINING, usedPanels, entState, trainableEnts,
-					function (trainEntType) { addTrainingToQueue(selection, trainEntType); } );
+					function (trainEntType) { addTrainingToQueue(selection, trainEntType, playerState); } );
 		}
 
 		// Show technologies if the active panel has at most one row of icons.
@@ -835,7 +852,7 @@
 		}
 
 		if (entState.production && entState.production.queue.length)
-			setupUnitPanel(QUEUE, usedPanels, entState, entState.production.queue,
+			setupUnitPanel(QUEUE, usedPanels, entState, playerState, entState.production.queue,
 				function (item) { removeFromProductionQueue(entState.id, item.id); } );
 		
 		if(!entState.foundation && (entState.gate || hasClass(entState, "LongWall")))
Index: binaries/data/mods/public/simulation/helpers/Templates.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Templates.js	(revision 0)
+++ binaries/data/mods/public/simulation/helpers/Templates.js	(revision 0)
@@ -0,0 +1,24 @@
+/**
+ * Return template.Identity.Classes._string if exists
+ */
+function GetTemplateIdentityClassesString(template)
+{
+	var identityClassesString = undefined;
+	if (template.Identity && template.Identity.Classes && "_string" in template.Identity.Classes)
+		identityClassesString = template.Identity.Classes._string;
+	return identityClassesString;
+}
+
+/**
+ * Check whether template.Identity.Classes contains specified class
+ */
+function TemplateHasIdentityClass(template, className)
+{
+	var identityClassesString = GetTemplateIdentityClassesString(template);
+	var hasClass = identityClassesString && identityClassesString.indexOf(className) != -1;
+	return hasClass;
+}
+
+Engine.RegisterGlobal("GetTemplateIdentityClassesString", GetTemplateIdentityClassesString);
+Engine.RegisterGlobal("TemplateHasIdentityClass", TemplateHasIdentityClass);
+
Index: binaries/data/mods/public/simulation/helpers/Commands.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Commands.js	(revision 12160)
+++ binaries/data/mods/public/simulation/helpers/Commands.js	(working copy)
@@ -142,11 +142,32 @@
 
 	case "train":
 		var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
+		
+		// Check entity limits
+		var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
+		var template = cmpTempMan.GetTemplate(cmd.template);
+		var unitCategory = null;
+		if (template.TrainingRestrictions)
+			unitCategory = template.TrainingRestrictions.Category;
+
 		// Verify that the building(s) can be controlled by the player
 		if (entities.length > 0)
 		{
 			for each (var ent in entities)
 			{
+				if (unitCategory)
+				{
+					var cmpPlayerEntityLimits = QueryOwnerInterface(ent, IID_EntityLimits);
+					if (!cmpPlayerEntityLimits.AllowedToTrain(unitCategory, cmd.count))
+					{
+						if (g_DebugCommands)
+						{
+							warn(unitCategory + " train limit is reached: "+uneval(cmd));
+						}
+						continue;
+					}
+				}
+
 				var cmpTechMan = QueryOwnerInterface(ent, IID_TechnologyManager);
 				// TODO: Enable this check once the AI gets technology support
 				if (cmpTechMan.CanProduce(cmd.template) || true)
@@ -524,9 +545,9 @@
 		return false;
 	}
 	
-	// Check build limits
-	var cmpBuildLimits = QueryPlayerIDInterface(player, IID_BuildLimits);
-	if (!cmpBuildLimits || !cmpBuildLimits.AllowedToBuild(cmpBuildRestrictions.GetCategory()))
+	// Check entity limits
+	var cmpEntityLimits = QueryPlayerIDInterface(player, IID_EntityLimits);
+	if (!cmpEntityLimits || !cmpEntityLimits.AllowedToBuild(cmpBuildRestrictions.GetCategory()))
 	{
 		if (g_DebugCommands)
 		{
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js	(revision 12160)
+++ binaries/data/mods/public/simulation/components/GuiInterface.js	(working copy)
@@ -49,7 +49,7 @@
 	for (var i = 0; i < n; ++i)
 	{
 		var playerEnt = cmpPlayerMan.GetPlayerByID(i);
-		var cmpPlayerBuildLimits = Engine.QueryInterface(playerEnt, IID_BuildLimits);
+		var cmpPlayerEntityLimits = Engine.QueryInterface(playerEnt, IID_EntityLimits);
 		var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
 		
 		// Work out what phase we are in
@@ -84,8 +84,8 @@
 			"phase": phase,
 			"isAlly": allies,
 			"isEnemy": enemies,
-			"buildLimits": cmpPlayerBuildLimits.GetLimits(),
-			"buildCounts": cmpPlayerBuildLimits.GetCounts(),
+			"entityLimits": cmpPlayerEntityLimits.GetLimits(),
+			"entityCounts": cmpPlayerEntityLimits.GetCounts(),
 			"techModifications": cmpTechnologyManager.GetTechModifications()
 		};
 		ret.players.push(playerData);
@@ -373,7 +373,14 @@
 			if (template.BuildRestrictions.Distance.MaxDistance) ret.buildRestrictions.distance.max = +template.BuildRestrictions.Distance.MaxDistance;
 		}
 	}
-	
+
+	if (template.TrainingRestrictions)
+	{
+		ret.trainingRestrictions = {
+			"category": template.TrainingRestrictions.Category,
+		};
+	}
+
 	if (template.Cost)
 	{
 		ret.cost = {};
@@ -442,6 +449,7 @@
 		ret.icon = template.Identity.Icon;
 		ret.tooltip =  template.Identity.Tooltip;
 		ret.requiredTechnology = template.Identity.RequiredTechnology;
+		ret.identityClassesString = GetTemplateIdentityClassesString(template);
 	}
 
 	if (template.UnitMotion)
Index: binaries/data/mods/public/simulation/components/BuildLimits.js
===================================================================
--- binaries/data/mods/public/simulation/components/BuildLimits.js	(revision 12160)
+++ binaries/data/mods/public/simulation/components/BuildLimits.js	(working copy)
@@ -1,128 +0,0 @@
-function BuildLimits() {}
-
-BuildLimits.prototype.Schema =
-	"<a:help>Specifies per category limits on number of buildings that can be constructed for each player.</a:help>" +
-	"<a:example>" +
-		"<Limits>" +
-		  "<CivilCentre/>" +
-		  "<DefenseTower>25</DefenseTower>" +
-		  "<Fortress>10</Fortress>" +
-		  "<Special>" +
-			"<LimitPerCivCentre>1</LimitPerCivCentre>" +
-		  "</Special>" +
-		"</Limits>" +
-	"</a:example>" +
-	"<element name='LimitMultiplier'>" +
-		"<ref name='positiveDecimal'/>" +
-	"</element>" +
-	"<element name='Limits'>" +
-		"<zeroOrMore>" +
-			"<element a:help='Specifies a category of building on which to apply this limit. See BuildRestrictions for list of categories.'>" +
-				"<anyName />" +
-				"<choice>" +
-					"<text />" +
-					"<element name='LimitPerCivCentre' a:help='Specifies that this limit is per number of civil centres.'>" +
-						"<data type='nonNegativeInteger'/>" +
-					"</element>" +
-				"</choice>" +
-			"</element>" +
-		"</zeroOrMore>" +
-	"</element>";
-
-/*
- *	TODO: Use an inheriting player_{civ}.xml template for civ-specific limits
- */
-
-BuildLimits.prototype.Init = function()
-{
-	this.limit = {};
-	this.count = {};
-	for (var category in this.template.Limits)
-	{
-		this.limit[category] = this.template.Limits[category];
-		this.count[category] = 0;
-	}
-};
-
-BuildLimits.prototype.IncrementCount = function(category)
-{
-	if (this.count[category] !== undefined)
-	{
-		this.count[category]++;
-	}
-};
-
-BuildLimits.prototype.DecrementCount = function(category)
-{
-	if (this.count[category] !== undefined)
-	{
-		this.count[category]--;
-	}
-};
-
-BuildLimits.prototype.GetLimits = function()
-{
-	return this.limit;
-};
-
-BuildLimits.prototype.GetCounts = function()
-{
-	return this.count;
-};
-
-BuildLimits.prototype.AllowedToBuild = function(category)
-{
-	// TODO: The UI should reflect this before the user tries to place the building,
-	//			since the limits are independent of placement location
-
-	// Allow unspecified categories and those with no limit
-	if (this.count[category] === undefined || this.limit[category] === undefined)
-	{
-		return true;
-	}
-	
-	// Rather than complicating the schema unecessarily, just handle special cases here
-	if (this.limit[category].LimitPerCivCentre !== undefined)
-	{
-		if (this.count[category] >= this.count["CivilCentre"] * this.limit[category].LimitPerCivCentre)
-		{
-			var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 
-			var notification = {"player": cmpPlayer.GetPlayerID(), "message": category+" build limit of "+this.limit[category].LimitPerCivCentre+" per civil centre reached"};
-			var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
-			cmpGUIInterface.PushNotification(notification);
-			
-			return false;
-		}
-	}
-	else if (this.count[category] >= this.limit[category])
-	{
-		var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
-		var notification = {"player": cmpPlayer.GetPlayerID(), "message": category+" build limit of "+this.limit[category]+ " reached"};
-		var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
-		cmpGUIInterface.PushNotification(notification);
-		
-		return false;
-	}
-	
-	return true;
-};
-
-BuildLimits.prototype.OnGlobalOwnershipChanged = function(msg)
-{
-	// This automatically updates build counts
-	var cmpBuildRestrictions = Engine.QueryInterface(msg.entity, IID_BuildRestrictions);
-	if (cmpBuildRestrictions)
-	{
-		var playerID = (Engine.QueryInterface(this.entity, IID_Player)).GetPlayerID();
-		if (msg.from == playerID)
-		{
-			this.DecrementCount(cmpBuildRestrictions.GetCategory());
-		}
-		if (msg.to == playerID)
-		{
-			this.IncrementCount(cmpBuildRestrictions.GetCategory());
-		}
-	}
-};
-
-Engine.RegisterComponentType(IID_BuildLimits, "BuildLimits", BuildLimits);
Index: binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js	(revision 12160)
+++ binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js	(working copy)
@@ -1,8 +1,8 @@
 Engine.LoadComponentScript("interfaces/Attack.js");
 Engine.LoadComponentScript("interfaces/Barter.js");
 Engine.LoadComponentScript("interfaces/Builder.js");
-Engine.LoadComponentScript("interfaces/BuildLimits.js");
 Engine.LoadComponentScript("interfaces/DamageReceiver.js");
+Engine.LoadComponentScript("interfaces/EntityLimits.js");
 Engine.LoadComponentScript("interfaces/Foundation.js");
 Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
 Engine.LoadComponentScript("interfaces/Gate.js");
@@ -67,7 +67,7 @@
 	IsEnemy: function() { return true; },
 });
 
-AddMock(100, IID_BuildLimits, {
+AddMock(100, IID_EntityLimits, {
 	GetLimits: function() { return {"Foo": 10}; },
 	GetCounts: function() { return {"Foo": 5}; },
 });
@@ -118,7 +118,7 @@
 	IsEnemy: function() { return false; },
 });
 
-AddMock(101, IID_BuildLimits, {
+AddMock(101, IID_EntityLimits, {
 	GetLimits: function() { return {"Bar": 20}; },
 	GetCounts: function() { return {"Bar": 0}; },
 });
@@ -171,8 +171,8 @@
 			phase: "",
 			isAlly: [false, false, false],
 			isEnemy: [true, true, true],
-			buildLimits: {"Foo": 10},
-			buildCounts: {"Foo": 5},
+			entityLimits: {"Foo": 10},
+			entityCounts: {"Foo": 5},
 			techModifications: {},
 		},
 		{
@@ -189,8 +189,8 @@
 			phase: "village",
 			isAlly: [true, true, true],
 			isEnemy: [false, false, false],
-			buildLimits: {"Bar": 20},
-			buildCounts: {"Bar": 0},
+			entityLimits: {"Bar": 20},
+			entityCounts: {"Bar": 0},
 			techModifications: {},
 		}
 	],
@@ -214,8 +214,8 @@
 			phase: "",
 			isAlly: [false, false, false],
 			isEnemy: [true, true, true],
-			buildLimits: {"Foo": 10},
-			buildCounts: {"Foo": 5},
+			entityLimits: {"Foo": 10},
+			entityCounts: {"Foo": 5},
 			techModifications: {},
 			statistics: {
 				unitsTrained: 10,
@@ -248,8 +248,8 @@
 			phase: "village",
 			isAlly: [true, true, true],
 			isEnemy: [false, false, false],
-			buildLimits: {"Bar": 20},
-			buildCounts: {"Bar": 0},
+			entityLimits: {"Bar": 20},
+			entityCounts: {"Bar": 0},
 			techModifications: {},
 			statistics: {
 				unitsTrained: 10,
Index: binaries/data/mods/public/simulation/components/ProductionQueue.js
===================================================================
--- binaries/data/mods/public/simulation/components/ProductionQueue.js	(revision 12160)
+++ binaries/data/mods/public/simulation/components/ProductionQueue.js	(working copy)
@@ -199,6 +199,14 @@
 			if (!cmpPlayer.TrySubtractResources(totalCosts))
 				return;
 
+			// Update entity count in the EntityLimits component
+			if (template.TrainingRestrictions)
+			{
+				var unitCategory = template.TrainingRestrictions.Category;
+				var cmpPlayerEntityLimits = QueryOwnerInterface(this.entity, IID_EntityLimits);
+				cmpPlayerEntityLimits.IncreaseCount(unitCategory, count);
+			}
+
 			this.queue.push({
 				"id": this.nextID++,
 				"player": cmpPlayer.GetPlayerID(),
@@ -292,6 +300,19 @@
 		
 		var cmpPlayer = QueryPlayerIDInterface(item.player, IID_Player);
 
+		// Update entity count in the EntityLimits component
+		if (item.unitTemplate)
+		{
+			var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
+			var template = cmpTempMan.GetTemplate(item.unitTemplate);
+			if (template.TrainingRestrictions)
+			{
+				var unitCategory = template.TrainingRestrictions.Category;
+				var cmpPlayerEntityLimits = QueryOwnerInterface(this.entity, IID_EntityLimits);
+				cmpPlayerEntityLimits.DecreaseCount(unitCategory, item.count);
+			}
+		}
+
 		// Refund the resource cost for this batch
 		var totalCosts = {};
 		for each (var r in ["food", "wood", "stone", "metal"])
@@ -401,7 +422,19 @@
 		//	so only create them once and use as needed
 		for (var i = 0; i < count; ++i)
 		{
-			this.entityCache.push(Engine.AddEntity(templateName));
+			var ent = Engine.AddEntity(templateName);
+			this.entityCache.push(ent);
+
+			// Decrement entity count in the EntityLimits component
+			// since it will be increased by EntityLimits.OnGlobalOwnershipChanged function,
+			// i.e. we replace a 'trained' entity to an 'alive' one
+			var cmpTrainingRestrictions = Engine.QueryInterface(ent, IID_TrainingRestrictions);
+			if (cmpTrainingRestrictions)
+			{
+				var unitCategory = cmpTrainingRestrictions.GetCategory();
+				var cmpPlayerEntityLimits = QueryOwnerInterface(this.entity, IID_EntityLimits);
+				cmpPlayerEntityLimits.DecrementCount(unitCategory);
+			}
 		}
 	}
 
Index: binaries/data/mods/public/simulation/components/Player.js
===================================================================
--- binaries/data/mods/public/simulation/components/Player.js	(revision 12160)
+++ binaries/data/mods/public/simulation/components/Player.js	(working copy)
@@ -343,23 +343,19 @@
 Player.prototype.OnGlobalOwnershipChanged = function(msg)
 {
 	var isConquestCritical = false;
-
 	// Load class list only if we're going to need it
 	if (msg.from == this.playerID || msg.to == this.playerID)
 	{
 		var cmpIdentity = Engine.QueryInterface(msg.entity, IID_Identity);
 		if (cmpIdentity)
 		{
-			var classes = cmpIdentity.GetClassesList();
-			isConquestCritical = classes.indexOf("ConquestCritical") != -1;
+			isConquestCritical = cmpIdentity.HasClass("ConquestCritical");
 		}
 	}
-	
 	if (msg.from == this.playerID)
 	{
 		if (isConquestCritical)
-			this.conquestCriticalEntitiesCount--;	
-
+			this.conquestCriticalEntitiesCount--;
 		var cost = Engine.QueryInterface(msg.entity, IID_Cost);
 		if (cost)
 		{
@@ -367,12 +363,10 @@
 			this.popBonuses -= cost.GetPopBonus();
 		}
 	}
-	
 	if (msg.to == this.playerID)
 	{
 		if (isConquestCritical)
 			this.conquestCriticalEntitiesCount++;
-			
 		var cost = Engine.QueryInterface(msg.entity, IID_Cost);
 		if (cost)
 		{
Index: binaries/data/mods/public/simulation/components/EntityLimits.js
===================================================================
--- binaries/data/mods/public/simulation/components/EntityLimits.js	(revision 0)
+++ binaries/data/mods/public/simulation/components/EntityLimits.js	(revision 0)
@@ -0,0 +1,164 @@
+function EntityLimits() {}
+
+EntityLimits.prototype.Schema =
+	"<a:help>Specifies per category limits on number of entities (buildings or units) that can be created for each player.</a:help>" +
+	"<a:example>" +
+		"<Limits>" +
+		  "<CivilCentre/>" +
+		  "<DefenseTower>25</DefenseTower>" +
+		  "<Fortress>10</Fortress>" +
+		  "<Hero>1</Hero>" +
+		  "<Special>" +
+			"<LimitPerCivCentre>1</LimitPerCivCentre>" +
+		  "</Special>" +
+		"</Limits>" +
+	"</a:example>" +
+	"<element name='LimitMultiplier'>" +
+		"<ref name='positiveDecimal'/>" +
+	"</element>" +
+	"<element name='Limits'>" +
+		"<zeroOrMore>" +
+			"<element a:help='Specifies a category of building on which to apply this limit. See BuildRestrictions for list of categories.'>" +
+				"<anyName />" +
+				"<choice>" +
+					"<text />" +
+					"<element name='LimitPerCivCentre' a:help='Specifies that this limit is per number of civil centres.'>" +
+						"<data type='nonNegativeInteger'/>" +
+					"</element>" +
+				"</choice>" +
+			"</element>" +
+		"</zeroOrMore>" +
+	"</element>";
+
+/*
+ *	TODO: Use an inheriting player_{civ}.xml template for civ-specific limits
+ */
+
+EntityLimits.prototype.Init = function()
+{
+	this.limit = {};
+	this.count = {};
+	for (var category in this.template.Limits)
+	{
+		this.limit[category] = this.template.Limits[category];
+		this.count[category] = 0;
+	}
+};
+
+EntityLimits.prototype.IncreaseCount = function(category, value)
+{
+	if (this.count[category] !== undefined)
+	{
+		this.count[category] += value;
+	}
+};
+
+EntityLimits.prototype.DecreaseCount = function(category, value)
+{
+	if (this.count[category] !== undefined)
+	{
+		this.count[category] -= value;
+	}
+};
+
+EntityLimits.prototype.IncrementCount = function(category)
+{
+	this.IncreaseCount(category, 1);
+};
+
+EntityLimits.prototype.DecrementCount = function(category)
+{
+	this.DecreaseCount(category, 1);
+};
+
+EntityLimits.prototype.GetLimits = function()
+{
+	return this.limit;
+};
+
+EntityLimits.prototype.GetCounts = function()
+{
+	return this.count;
+};
+
+EntityLimits.prototype.AllowedToBuild = function(category)
+{
+	// TODO: The UI should reflect this before the user tries to place the building,
+	//			since the limits are independent of placement location
+
+	// Allow unspecified categories and those with no limit
+	if (this.count[category] === undefined || this.limit[category] === undefined)
+	{
+		return true;
+	}
+	
+	// Rather than complicating the schema unecessarily, just handle special cases here
+	if (this.limit[category].LimitPerCivCentre !== undefined)
+	{
+		if (this.count[category] >= this.count["CivilCentre"] * this.limit[category].LimitPerCivCentre)
+		{
+			var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 
+			var notification = {"player": cmpPlayer.GetPlayerID(), "message": category+" build limit of "+this.limit[category].LimitPerCivCentre+" per civil centre reached"};
+			var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+			cmpGUIInterface.PushNotification(notification);
+			
+			return false;
+		}
+	}
+	else if (this.count[category] >= this.limit[category])
+	{
+		var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
+		var notification = {"player": cmpPlayer.GetPlayerID(), "message": category+" train limit of "+this.limit[category]+ " reached"};
+		var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+		cmpGUIInterface.PushNotification(notification);
+		
+		return false;
+	}
+	
+	return true;
+};
+
+EntityLimits.prototype.AllowedToTrain = function(category, count)
+{
+	// Allow unspecified categories and those with no limit
+	if (this.count[category] === undefined || this.limit[category] === undefined)
+	{
+		return true;
+	}
+	if (this.count[category] + count > this.limit[category])
+	{
+		var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
+		var notification = {"player": cmpPlayer.GetPlayerID(), "message": category+" build limit of "+this.limit[category]+ " reached"};
+		var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+		cmpGUIInterface.PushNotification(notification);
+		
+		return false;
+	}
+	return true;
+};
+
+EntityLimits.prototype.OnGlobalOwnershipChanged = function(msg)
+{
+	// This automatically updates entity counts
+	var category = null;
+	var cmpBuildRestrictions = Engine.QueryInterface(msg.entity, IID_BuildRestrictions);
+	if (cmpBuildRestrictions)
+		category = cmpBuildRestrictions.GetCategory();
+	var cmpTrainingRestrictions = Engine.QueryInterface(msg.entity, IID_TrainingRestrictions);
+	if (cmpTrainingRestrictions)
+		category = cmpTrainingRestrictions.GetCategory();
+	if (category)
+	{
+		var playerID = (Engine.QueryInterface(this.entity, IID_Player)).GetPlayerID();
+		if (msg.from == playerID)
+		{
+			this.DecrementCount(category);
+		}
+		if (msg.to == playerID)
+		{
+			this.IncrementCount(category);
+		}
+	}
+};
+
+Engine.RegisterComponentType(IID_EntityLimits, "EntityLimits", EntityLimits);
Index: binaries/data/mods/public/simulation/components/interfaces/TrainingRestrictions.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/TrainingRestrictions.js	(revision 0)
+++ binaries/data/mods/public/simulation/components/interfaces/TrainingRestrictions.js	(revision 0)
@@ -0,0 +1,2 @@
+Engine.RegisterInterface("TrainingRestrictions");
+
Index: binaries/data/mods/public/simulation/components/interfaces/BuildLimits.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/BuildLimits.js	(revision 12160)
+++ binaries/data/mods/public/simulation/components/interfaces/BuildLimits.js	(working copy)
@@ -1 +0,0 @@
-Engine.RegisterInterface("BuildLimits");
\ No newline at end of file
Index: binaries/data/mods/public/simulation/components/interfaces/EntityLimits.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/EntityLimits.js	(revision 0)
+++ binaries/data/mods/public/simulation/components/interfaces/EntityLimits.js	(revision 0)
@@ -0,0 +1 @@
+Engine.RegisterInterface("EntityLimits");
Index: binaries/data/mods/public/simulation/components/TrainingRestrictions.js
===================================================================
--- binaries/data/mods/public/simulation/components/TrainingRestrictions.js	(revision 0)
+++ binaries/data/mods/public/simulation/components/TrainingRestrictions.js	(revision 0)
@@ -0,0 +1,22 @@
+function TrainingRestrictions() {}
+
+TrainingRestrictions.prototype.Schema =
+	"<a:help>Specifies unit training restrictions, currently only unit category.</a:help>" +
+	"<a:example>" +
+		"<TrainingRestrictions>" +
+			"<Category>Hero</Category>" +
+		"</TrainingRestrictions>" +
+	"</a:example>" +
+	"<element name='Category' a:help='Specifies the category of this unit, for satisfying special constraints.'>" +
+		"<choice>" +
+			"<value>Hero</value>" +
+			"<value>FemaleCitizen</value>" +
+		"</choice>" +
+	"</element>";
+
+TrainingRestrictions.prototype.GetCategory = function()
+{
+	return this.template.Category;
+};
+
+Engine.RegisterComponentType(IID_TrainingRestrictions, "TrainingRestrictions", TrainingRestrictions);
Index: binaries/data/mods/public/simulation/ai/qbot/military.js
===================================================================
--- binaries/data/mods/public/simulation/ai/qbot/military.js	(revision 12160)
+++ binaries/data/mods/public/simulation/ai/qbot/military.js	(working copy)
@@ -324,7 +324,7 @@
 // Adds towers to the defenceBuilding queue
 MilitaryAttackManager.prototype.buildDefences = function(gameState, queues){ 
 	if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv('structures/{civ}_defense_tower'))
-			+ queues.defenceBuilding.totalLength() < gameState.getBuildLimits()["DefenseTower"]) {
+			+ queues.defenceBuilding.totalLength() < gameState.getEntityLimits()["DefenseTower"]) {
 		
 		
 		gameState.getOwnEntities().forEach(function(dropsiteEnt) {
@@ -343,7 +343,7 @@
 		numFortresses += gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bFort[i]));
 	}
 	
-	if (numFortresses + queues.defenceBuilding.totalLength() < gameState.getBuildLimits()["Fortress"]) {
+	if (numFortresses + queues.defenceBuilding.totalLength() < gameState.getEntityLimits()["Fortress"]) {
 		if (gameState.countEntitiesByType(gameState.applyCiv("units/{civ}_support_female_citizen")) > gameState.ai.modules["economy"].targetNumWorkers * 0.5){
 			if (gameState.getTimeElapsed() > 350 * 1000 * numFortresses){
 				if (gameState.ai.pathsToMe && gameState.ai.pathsToMe.length > 0){
Index: binaries/data/mods/public/simulation/ai/qbot/gamestate.js
===================================================================
--- binaries/data/mods/public/simulation/ai/qbot/gamestate.js	(revision 12160)
+++ binaries/data/mods/public/simulation/ai/qbot/gamestate.js	(working copy)
@@ -300,20 +300,20 @@
 	return this.updatingCollection("resource-" + resource, Filters.byResource(resource), this.getEntities());
 };
 
-GameState.prototype.getBuildLimits = function() {
-	return this.playerData.buildLimits;
+GameState.prototype.getEntityLimits = function() {
+	return this.playerData.entityLimits;
 };
 
-GameState.prototype.getBuildCounts = function() {
-	return this.playerData.buildCounts;
+GameState.prototype.getEntityCounts = function() {
+	return this.playerData.entityCounts;
 };
 
 // Checks whether the maximum number of buildings have been cnstructed for a certain catergory
-GameState.prototype.isBuildLimitReached = function(category) {
-	if(this.playerData.buildLimits[category] === undefined || this.playerData.buildCounts[category] === undefined)
+GameState.prototype.isEntityLimitReached = function(category) {
+	if(this.playerData.entityLimits[category] === undefined || this.playerData.entityCounts[category] === undefined)
 		return false;
-	if(this.playerData.buildLimits[category].LimitsPerCivCentre != undefined)
-		return (this.playerData.buildCounts[category] >= this.playerData.buildCounts["CivilCentre"]*this.playerData.buildLimits[category].LimitPerCivCentre);
+	if(this.playerData.entityLimits[category].LimitsPerCivCentre != undefined)
+		return (this.playerData.entityCounts[category] >= this.playerData.entityCounts["CivilCentre"]*this.playerData.entityLimits[category].LimitPerCivCentre);
 	else
-		return (this.playerData.buildCounts[category] >= this.playerData.buildLimits[category]);
+		return (this.playerData.entityCounts[category] >= this.playerData.entityLimits[category]);
 };
Index: binaries/data/mods/public/simulation/ai/jubot/gamestate.js
===================================================================
--- binaries/data/mods/public/simulation/ai/jubot/gamestate.js	(revision 12160)
+++ binaries/data/mods/public/simulation/ai/jubot/gamestate.js	(working copy)
@@ -415,33 +415,33 @@
 	 * Returns player build limits
 	 * an object where each key is a category corresponding to a build limit for the player.
 	 */
-	getBuildLimits: function()
+	getEntityLimits: function()
 	{
-		return this.playerData.buildLimits;
+		return this.playerData.entityLimits;
 	},
 	
 	/**
 	 * Returns player build counts
 	 * an object where each key is a category corresponding to the current building count for the player.
 	 */
-	getBuildCounts: function()
+	getEntityCounts: function()
 	{
-		return this.playerData.buildCounts;
+		return this.playerData.entityCounts;
 	},
 	
 	/**
 	 * Checks if the player's build limit has been reached for the given category.
 	 * The category comes from the entity tenplate, specifically the BuildRestrictions component.
 	 */
-	isBuildLimitReached: function(category)
+	isEntityLimitReached: function(category)
 	{
-		if (this.playerData.buildLimits[category] === undefined || this.playerData.buildCounts[category] === undefined)
+		if (this.playerData.entityLimits[category] === undefined || this.playerData.entityCounts[category] === undefined)
 			return false;
 		
 		// There's a special case of build limits per civ centre, so check that first
-		if (this.playerData.buildLimits[category].LimitPerCivCentre !== undefined)
-			return (this.playerData.buildCounts[category] >= this.playerData.buildCounts["CivilCentre"]*this.playerData.buildLimits[category].LimitPerCivCentre);
+		if (this.playerData.entityLimits[category].LimitPerCivCentre !== undefined)
+			return (this.playerData.entityCounts[category] >= this.playerData.entityCounts["CivilCentre"]*this.playerData.entityLimits[category].LimitPerCivCentre);
 		else
-			return (this.playerData.buildCounts[category] >= this.playerData.buildLimits[category]);
+			return (this.playerData.entityCounts[category] >= this.playerData.entityLimits[category]);
 	},
 });
Index: binaries/data/mods/public/simulation/ai/testbot/gamestate.js
===================================================================
--- binaries/data/mods/public/simulation/ai/testbot/gamestate.js	(revision 12160)
+++ binaries/data/mods/public/simulation/ai/testbot/gamestate.js	(working copy)
@@ -268,33 +268,33 @@
 	 * Returns player build limits
 	 * an object where each key is a category corresponding to a build limit for the player.
 	 */
-	getBuildLimits: function()
+	getEntityLimits: function()
 	{
-		return this.playerData.buildLimits;
+		return this.playerData.entityLimits;
 	},
 	
 	/**
-	 * Returns player build counts
-	 * an object where each key is a category corresponding to the current building count for the player.
+	 * Returns player entity counts
+	 * an object where each key is a category corresponding to the current entity count for the player.
 	 */
-	getBuildCounts: function()
+	getEntityCounts: function()
 	{
-		return this.playerData.buildCounts;
+		return this.playerData.entityCounts;
 	},
 	
 	/**
-	 * Checks if the player's build limit has been reached for the given category.
+	 * Checks if the player's entity limit has been reached for the given category.
 	 * The category comes from the entity tenplate, specifically the BuildRestrictions component.
 	 */
-	isBuildLimitReached: function(category)
+	isEntityLimitReached: function(category)
 	{
-		if (this.playerData.buildLimits[category] === undefined || this.playerData.buildCounts[category] === undefined)
+		if (this.playerData.entityLimits[category] === undefined || this.playerData.entityCounts[category] === undefined)
 			return false;
 		
 		// There's a special case of build limits per civ centre, so check that first
-		if (this.playerData.buildLimits[category].LimitPerCivCentre !== undefined)
-			return (this.playerData.buildCounts[category] >= this.playerData.buildCounts["CivilCentre"]*this.playerData.buildLimits[category].LimitPerCivCentre);
+		if (this.playerData.entityLimits[category].LimitPerCivCentre !== undefined)
+			return (this.playerData.entityCounts[category] >= this.playerData.entityCounts["CivilCentre"]*this.playerData.entityLimits[category].LimitPerCivCentre);
 		else
-			return (this.playerData.buildCounts[category] >= this.playerData.buildLimits[category]);
+			return (this.playerData.entityCounts[category] >= this.playerData.entityLimits[category]);
 	},
 });
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_ranged.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_ranged.xml	(revision 12160)
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_ranged.xml	(working copy)
@@ -51,6 +51,9 @@
       </Texture>
     </Overlay>
   </Selectable>
+  <TrainingRestrictions>
+    <Category>Hero</Category>
+  </TrainingRestrictions>
   <UnitMotion>
     <WalkSpeed>8.5</WalkSpeed>
     <Run>
Index: binaries/data/mods/public/simulation/templates/special/player.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/special/player.xml	(revision 12160)
+++ binaries/data/mods/public/simulation/templates/special/player.xml	(working copy)
@@ -1,13 +1,15 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Entity>
-  <BuildLimits>
+  <EntityLimits>
     <LimitMultiplier>1.0</LimitMultiplier>
     <Limits>
       <CivilCentre/>
       <DefenseTower>25</DefenseTower>
       <Fortress>10</Fortress>
+      <FemaleCitizen>6</FemaleCitizen>
+      <Hero>1</Hero>
     </Limits>
-  </BuildLimits>
+  </EntityLimits>
   <Player/>
   <StatisticsTracker/>
   <TechnologyManager/>
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml	(revision 12160)
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml	(working copy)
@@ -69,6 +69,9 @@
   <Stamina>
     <Max>2500</Max>
   </Stamina>
+  <TrainingRestrictions>
+    <Category>Hero</Category>
+  </TrainingRestrictions>
   <UnitMotion>
     <WalkSpeed>11.0</WalkSpeed>
     <Run>
Index: binaries/data/mods/public/simulation/templates/template_unit_hero.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero.xml	(revision 12160)
+++ binaries/data/mods/public/simulation/templates/template_unit_hero.xml	(working copy)
@@ -62,6 +62,9 @@
       <death>actor/human/death/death.xml</death>
     </SoundGroups>
   </Sound>
+  <TrainingRestrictions>
+    <Category>Hero</Category>
+  </TrainingRestrictions>
   <UnitMotion>
     <WalkSpeed>9.0</WalkSpeed>
     <Run>
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_infantry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_infantry.xml	(revision 12160)
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_infantry.xml	(working copy)
@@ -67,6 +67,9 @@
   <Stamina>
     <Max>1500</Max>
   </Stamina>
+  <TrainingRestrictions>
+    <Category>Hero</Category>
+  </TrainingRestrictions>
   <UnitMotion>
     <WalkSpeed>8.5</WalkSpeed>
     <Run>
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_archer.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_archer.xml	(revision 12160)
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_archer.xml	(working copy)
@@ -61,4 +61,7 @@
       </Texture>
     </Overlay>
   </Selectable>
+  <TrainingRestrictions>
+    <Category>Hero</Category>
+  </TrainingRestrictions>
 </Entity>
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_javelinist.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_javelinist.xml	(revision 12160)
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_javelinist.xml	(working copy)
@@ -65,4 +65,7 @@
       </Texture>
     </Overlay>
   </Selectable>
+  <TrainingRestrictions>
+    <Category>Hero</Category>
+  </TrainingRestrictions>
 </Entity>
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_archer.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_archer.xml	(revision 12160)
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_archer.xml	(working copy)
@@ -69,6 +69,9 @@
       </Texture>
     </Overlay>
   </Selectable>
+  <TrainingRestrictions>
+    <Category>Hero</Category>
+  </TrainingRestrictions>
   <UnitMotion>
     <WalkSpeed>11.0</WalkSpeed>
     <Run>
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_javelinist.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_javelinist.xml	(revision 12160)
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_javelinist.xml	(working copy)
@@ -59,6 +59,9 @@
       </Texture>
     </Overlay>
   </Selectable>
+  <TrainingRestrictions>
+    <Category>Hero</Category>
+  </TrainingRestrictions>
   <UnitMotion>
     <WalkSpeed>11.5</WalkSpeed>
     <Run>
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 12160)
+++ binaries/data/mods/public/simulation/templates/template_unit_support_female_citizen.xml	(working copy)
@@ -91,6 +91,9 @@
   <Stamina>
     <Max>500</Max>
   </Stamina>
+  <TrainingRestrictions>
+    <Category>FemaleCitizen</Category>
+  </TrainingRestrictions>
   <UnitMotion>
     <WalkSpeed>8.0</WalkSpeed>
     <Run>
