Index: binaries/data/mods/public/simulation/components/AuraManager.js
===================================================================
--- binaries/data/mods/public/simulation/components/AuraManager.js	(revision 16600)
+++ binaries/data/mods/public/simulation/components/AuraManager.js	(working copy)
@@ -39,24 +39,26 @@
 	return k;
 };
 
-AuraManager.prototype.ApplyBonus = function(value, ent, data, key)
+AuraManager.prototype.ApplyBonus = function(value, ents, data, key)
 {
-	var dataList = this.ensureExists("modifications", value, ent, key, {"add":0, "multiply":1});
+	for (let ent of ents)
+	{
+		var dataList = this.ensureExists("modifications", value, ent, key, {"add":0, "multiply":1});
 
-	dataList.push(data);
+		dataList.push(data);
 
-	if (dataList.length > 1)
-		return;
+		if (dataList.length > 1)
+			continue;
 
-	// first time added this aura
-	if (data.multiply)
-		this.modificationsCache[value][ent].multiply *= data.multiply;
+		// first time added this aura
+		if (data.multiply)
+			this.modificationsCache[value][ent].multiply *= data.multiply;
 
-	if (data.add)
-		this.modificationsCache[value][ent].add += data.add;
-
-	// post message to the entity to notify it about the change
-	Engine.PostMessage(ent, MT_ValueModification, { "entities": [ent], "component": value.split("/")[0], "valueNames": [value] });
+		if (data.add)
+			this.modificationsCache[value][ent].add += data.add;
+		// post message to the entity to notify it about the change
+		Engine.PostMessage(ent, MT_ValueModification, { "entities": [ent], "component": value.split("/")[0], "valueNames": [value] });
+	}
 };
 
 AuraManager.prototype.ApplyTemplateBonus = function(value, player, classes, data, key)
@@ -84,33 +86,35 @@
 	Engine.PostMessage(SYSTEM_ENTITY, MT_TemplateModification, { "player": player, "component": value.split("/")[0], "valueNames": [value] });
 };
 
-AuraManager.prototype.RemoveBonus = function(value, ent, key)
+AuraManager.prototype.RemoveBonus = function(value, ents, key)
 {
-	var v = this.modifications[value];
-	if (!v)
-		return;
-	var e = v[ent];
-	if (!e)
-		return;
-	var dataList = e[key];
-	if (!dataList || !dataList.length)
-		return;
+	for (let ent of ents)
+	{
+		var v = this.modifications[value];
+		if (!v)
+			continue;
+		var e = v[ent];
+		if (!e)
+			continue;
+		var dataList = e[key];
+		if (!dataList || !dataList.length)
+			continue;
 
-	// get the applied data to remove again
-	var data = dataList.pop();
+		// get the applied data to remove again
+		var data = dataList.pop();
 
-	if (dataList.length > 0)
-		return;
+		if (dataList.length > 0)
+			continue;
 
-	// out of last aura of this kind, remove modifications
-	if (data.add)
-		this.modificationsCache[value][ent].add -= data.add;
+		// out of last aura of this kind, remove modifications
+		if (data.add)
+			this.modificationsCache[value][ent].add -= data.add;
 
-	if (data.multiply)
-		this.modificationsCache[value][ent].multiply /= data.multiply;
-
-	// post message to the entity to notify it about the change
-	Engine.PostMessage(ent, MT_ValueModification, { "entities": [ent], "component": value.split("/")[0], "valueNames": [value] });
+		if (data.multiply)
+			this.modificationsCache[value][ent].multiply /= data.multiply;
+		// post message to the entity to notify it about the change
+		Engine.PostMessage(ent, MT_ValueModification, { "entities": [ent], "component": value.split("/")[0], "valueNames": [value] });
+	}
 };
 
 AuraManager.prototype.RemoveTemplateBonus = function(value, player, classes, key)
Index: binaries/data/mods/public/simulation/components/Auras.js
===================================================================
--- binaries/data/mods/public/simulation/components/Auras.js	(revision 16600)
+++ binaries/data/mods/public/simulation/components/Auras.js	(working copy)
@@ -47,6 +47,11 @@
 						"<text/>" +
 					"</element>" +
 				"</optional>" +
+				"<optional>" +
+					"<element name='OverlayIcon' a:help='Icon to show on the entities affected by this aura'>" +
+						"<text/>" +
+					"</element>" +
+				"</optional>" +
 				"<element name='Affects' a:help='Affected classes'>" +
 					"<text/>" +
 				"</element>" +
@@ -103,6 +108,16 @@
 	return Object.keys(this.template);
 };
 
+Auras.prototype.GetOverlayIcon = function(name)
+{
+	return this.template[name].OverlayIcon || "";
+};
+
+Auras.prototype.GetAffectedEntities = function(name)
+{
+	return this[name].targetUnits;
+};
+
 Auras.prototype.GetRange = function(name)
 {
 	if (!this.IsRangeAura(name))
@@ -136,17 +151,16 @@
 
 	this.affectedPlayers[name] = [];
 
-	var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
+	var cmpPlayer = QueryOwnerInterface(this.entity);
 
 	if (!cmpPlayer)
 		return;
 
-	var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
-	var numPlayers = cmpPlayerManager.GetNumPlayers();
+	var numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
 
 	for (var i = 0; i < numPlayers; ++i)
 	{
-		for each (var p in affectedPlayers)
+		for (let p of affectedPlayers)
 		{
 			if (p == "Player" ? cmpPlayer.GetPlayerID() == i : cmpPlayer["Is" + p](i))
 			{
@@ -159,17 +173,17 @@
 
 Auras.prototype.HasFormationAura = function()
 {
-	return this.GetAuraNames().some(this.IsFormationAura.bind(this));
+	return this.GetAuraNames().some(n => this.IsFormationAura(n));
 };
 
 Auras.prototype.HasGarrisonAura = function()
 {
-	return this.GetAuraNames().some(this.IsGarrisonAura.bind(this));
+	return this.GetAuraNames().some(n => this.IsGarrisonAura(n));
 };
 
 Auras.prototype.HasGarrisonedUnitsAura = function()
 {
-	return this.GetAuraNames().some(this.IsGarrisonedUnitsAura.bind(this));
+	return this.GetAuraNames().some(n => this.IsGarrisonedUnitsAura(n));
 };
 
 Auras.prototype.GetType = function(name)
@@ -211,7 +225,7 @@
 	var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
 	var auraNames = this.GetAuraNames();
 	// remove all bonuses
-	for each (var name in auraNames)
+	for (let name of auraNames)
 	{
 		if (!this[name])
 			continue;
@@ -219,14 +233,13 @@
 		if (this.IsGlobalAura(name))
 			this.RemoveTemplateBonus(name);
 
-		for each(var ent in this[name].targetUnits)
-			this.RemoveBonus(name, ent);
+		this.RemoveBonus(name, this[name].targetUnits);
 
 		if (this[name].rangeQuery)
 			cmpRangeManager.DestroyActiveQuery(this[name].rangeQuery);
 	}
 
-	for each (var name in auraNames)
+	for (let name of auraNames)
 	{
 		// only calculate the affected players on re-applying the bonuses
 		// this makes sure the template bonuses are removed from the correct players
@@ -277,89 +290,36 @@
 
 Auras.prototype.OnRangeUpdate = function(msg)
 {
-	var auraNames = this.GetAuraNames();
-	for each (var n in auraNames)
+	var auraNames = this.GetAuraNames().filter(n => msg.tag == this[n].rangeQuery);
+	for (let name of auraNames)
 	{
-		if (msg.tag == this[n].rangeQuery)
-		{
-			var name = n;
-			break;
-		}
+		this.ApplyBonus(name, msg.added);
+		this.RemoveBonus(name, msg.removed);
 	}
-
-	if (!name)
-		return;
-
-	var targetUnits = this[name].targetUnits;
-	var classes = this.GetClasses(name);
-
-	if (msg.added.length > 0)
-	{
-		var validList = this.GiveMembersWithValidClass(name, msg.added);
-		for each (var e in validList)
-		{
-			targetUnits.push(e);
-			this.ApplyBonus(name, e);
-		}
-	}
-
-	if (msg.removed.length > 0)
-	{
-		for each (var e in msg.removed)
-		{
-			targetUnits.splice(targetUnits.indexOf(e), 1);
-			this.RemoveBonus(name, e);
-		}
-	}
-
 };
 
 Auras.prototype.OnGarrisonedUnitsChanged = function(msg)
 {
-	var auraNames = this.GetAuraNames();
-	for each (var name in auraNames)
+	var auraNames = this.GetAuraNames().filter(n => this.IsGarrisonedUnitsAura(n));
+	for (let name of auraNames)
 	{
-		if (!this.IsGarrisonedUnitsAura(name))
-			continue;
-		for each (var ent in msg.added)
-			this.ApplyBonus(name, ent);
-		for each (var ent in msg.removed)
-			this.RemoveBonus(name, ent);
+		this.ApplyBonus(name, msg.added);
+		this.RemoveBonus(name, msg.removed);
 	}
 };
 
 Auras.prototype.ApplyFormationBonus = function(memberList)
 {
-	var auraNames = this.GetAuraNames();
-	for each (var name in auraNames)
-	{
-		if (!this.IsFormationAura(name))
-			continue;
-
-		var validList = this.GiveMembersWithValidClass(name, memberList);
-		for each (var ent in validList)
-		{
-			this[name].targetUnits.push(ent);
-			this.ApplyBonus(name,ent);
-		}
-	}
+	var auraNames = this.GetAuraNames().filter(n => this.IsFormationAura(n));
+	for (let name of auraNames)
+		this.ApplyBonus(name, memberList);
 };
 
 Auras.prototype.ApplyGarrisonBonus = function(structure)
 {
-	var auraNames = this.GetAuraNames();
-	for each (var name in auraNames)
-	{
-		if (!this.IsGarrisonAura(name))
-			continue;
-
-		var validList = this.GiveMembersWithValidClass(name, [structure]);
-		if (validList.length)
-		{
-			this[name].targetUnits.push(validList[0]);
-			this.ApplyBonus(name,validList[0]);
-		}
-	}
+	var auraNames = this.GetAuraNames().filter(n => this.IsGarrisonAura(n));
+	for (let name of auraNames)
+		this.ApplyBonus(name, [structure]);
 };
 
 Auras.prototype.ApplyTemplateBonus = function(name, players)
@@ -370,38 +330,23 @@
 	var cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager);
 	var classes = this.GetClasses(name);
 
-	for each (var mod in modifications)
-		for each (var player in players)
+	for (let mod of modifications)
+		for (let player of players)
 			cmpAuraManager.ApplyTemplateBonus(mod.value, player, classes, mod, this.templateName + "/" + name + "/" + mod.value);
 };
 
 Auras.prototype.RemoveFormationBonus = function(memberList)
 {
-	var auraNames = this.GetAuraNames();
-	for each (var name in auraNames)
-	{
-		if (!this.IsFormationAura(name))
-			continue;
-
-		for each (var ent in memberList)
-		{
-			this.RemoveBonus(name,ent);
-			this[name].targetUnits.splice(this[name].targetUnits.indexOf(ent), 1);
-		}
-	}
+	var auraNames = this.GetAuraNames().filter(n => this.IsFormationAura(n));
+	for (let name of auraNames)
+		this.RemoveBonus(name, memberList);
 };
 
 Auras.prototype.RemoveGarrisonBonus = function(structure)
 {
-	var auraNames = this.GetAuraNames();
-	for each (var name in auraNames)
-	{
-		if (!this.IsGarrisonAura(name))
-			continue;
-
-		this.RemoveBonus(name,structure);
-		this[name].targetUnits.splice(this[name].targetUnits.indexOf(structure), 1);
-	}
+	var auraNames = this.GetAuraNames().filter(n => this.IsGarrisonAura(n));
+	for (let name of auraNames)
+		this.RemoveBonus(name, [structure]);
 };
 
 Auras.prototype.RemoveTemplateBonus = function(name)
@@ -419,22 +364,46 @@
 			cmpAuraManager.RemoveTemplateBonus(mod.value, player, classes, this.templateName + "/" + name + "/" + mod.value);
 };
 
-Auras.prototype.ApplyBonus = function(name, ent)
+Auras.prototype.ApplyBonus = function(name, ents)
 {
+	var validEnts = this.GiveMembersWithValidClass(name, ents);
+	if (!validEnts.length)
+		return;
+	this[name].targetUnits = this[name].targetUnits.concat(validEnts);
 	var modifications = this.GetModifications(name);
 	var cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager);
-	for each (var mod in modifications)
-		cmpAuraManager.ApplyBonus(mod.value, ent, mod, this.templateName + "/" + name + "/" + mod.value);
-
+	for each (let mod in modifications)
+		cmpAuraManager.ApplyBonus(mod.value, validEnts, mod, this.templateName + "/" + name + "/" + mod.value);
+	// update status bars if this has an icon
+	if (!this.GetOverlayIcon(name))
+		return;
+	for (let ent of validEnts)
+	{
+		var cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars)
+		if (cmpStatusBars)
+			cmpStatusBars.AddAuraSource(this.entity, name);
+	}
 };
 
-Auras.prototype.RemoveBonus = function(name, ent)
+Auras.prototype.RemoveBonus = function(name, ents)
 {
+	var validEnts = this.GiveMembersWithValidClass(name, ents);
+	if (!validEnts.length)
+		return;
+	this[name].targetUnits = this[name].targetUnits.filter(v => validEnts.indexOf(v) == -1);
 	var modifications = this.GetModifications(name);
 	var cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager);
-
-	for each (var mod in modifications)
-		cmpAuraManager.RemoveBonus(mod.value, ent, this.templateName + "/" + name + "/" + mod.value);
+	for each (let mod in modifications)
+		cmpAuraManager.RemoveBonus(mod.value, validEnts, this.templateName + "/" + name + "/" + mod.value);
+	// update status bars if this has an icon
+	if (!this.GetOverlayIcon(name))
+		return;
+	for (let ent of validEnts)
+	{
+		var cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars)
+		if (cmpStatusBars)
+			cmpStatusBars.RemoveAuraSource(this.entity, name);
+	}
 };
 
 Auras.prototype.OnOwnershipChanged = function(msg)
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js	(revision 16601)
+++ binaries/data/mods/public/simulation/components/GuiInterface.js	(working copy)
@@ -32,6 +32,7 @@
 	this.timeNotificationID = 1;
 	this.timeNotifications = [];
 	this.entsRallyPointsDisplayed = [];
+	this.entsWithAuraAndStatusBars = new Set();
 };
 
 /*
@@ -855,13 +856,44 @@
 	}
 };
 
+GuiInterface.prototype.GetEntitiesWithStatusBars = function()
+{
+	return [...this.entsWithAuraAndStatusBars];
+};
+
 GuiInterface.prototype.SetStatusBars = function(player, cmd)
 {
-	for each (var ent in cmd.entities)
+	let affectedEnts = [];
+	for (let ent of cmd.entities)
 	{
-		var cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars);
+		let cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars);
+		if (!cmpStatusBars)
+			continue;
+		cmpStatusBars.SetEnabled(cmd.enabled);
+
+		let cmpAuras = Engine.QueryInterface(ent, IID_Auras);
+		if (!cmpAuras)
+			continue;
+
+		for (let name of cmpAuras.GetAuraNames())
+		{
+			if (!cmpAuras.GetOverlayIcon(name))
+				continue;
+			affectedEnts = affectedEnts.concat(cmpAuras.GetAffectedEntities(name));
+			if (cmd.enabled)
+				this.entsWithAuraAndStatusBars.add(ent);
+			else
+				this.entsWithAuraAndStatusBars.delete(ent);
+		}
+	}
+
+	// make the affected ents unique
+	affectedEnts = new Set(affectedEnts);
+	for (let ent of affectedEnts)
+	{
+		let cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars)
 		if (cmpStatusBars)
-			cmpStatusBars.SetEnabled(cmd.enabled);
+			cmpStatusBars.RegenerateSprites();
 	}
 };
 
Index: binaries/data/mods/public/simulation/components/StatusBars.js
===================================================================
--- binaries/data/mods/public/simulation/components/StatusBars.js	(revision 16600)
+++ binaries/data/mods/public/simulation/components/StatusBars.js	(working copy)
@@ -14,17 +14,18 @@
 StatusBars.prototype.Init = function()
 {
 	this.enabled = false;
+	this.auraSources = {};
 };
 
-// Because this is enabled directly by the GUI and is not
-// network-synchronised (it only affects local rendering),
-// we disable serialization in order to prevent OOS errors
-StatusBars.prototype.Serialize = null;
+StatusBars.prototype.Serialize = function()
+{
+	return {"auraSources": this.auraSources};
+};
 
-StatusBars.prototype.Deserialize = function()
+StatusBars.prototype.Deserialize = function(data)
 {
-	// Use default initialisation
 	this.Init();
+	this.auraSources = data.auraSources;
 };
 
 StatusBars.prototype.SetEnabled = function(enabled)
@@ -33,16 +34,28 @@
 	if (enabled == this.enabled)
 		return;
 
+	this.enabled = enabled;
+
 	// Update the displayed sprites
+	this.RegenerateSprites();
+};
 
-	this.enabled = enabled;
-
-	if (enabled)
-		this.RegenerateSprites();
+StatusBars.prototype.AddAuraSource = function(source, auraName)
+{
+	if (this.auraSources[source])
+		this.auraSources[source].push(auraName);
 	else
-		this.ResetSprites();
+		this.auraSources[source] = [auraName];
+	this.RegenerateSprites();
 };
 
+StatusBars.prototype.RemoveAuraSource = function(source, auraName)
+{
+	let names = this.auraSources[source];
+	names.splice(names.indexOf(auraName), 1);
+	this.RegenerateSprites();
+};
+
 StatusBars.prototype.OnHealthChanged = function(msg)
 {
 	if (this.enabled)
@@ -61,17 +74,58 @@
 		this.RegenerateSprites();
 };
 
-StatusBars.prototype.ResetSprites = function()
+StatusBars.prototype.RegenerateSprites = function()
 {
-	var cmpOverlayRenderer = Engine.QueryInterface(this.entity, IID_OverlayRenderer);
+	let cmpOverlayRenderer = Engine.QueryInterface(this.entity, IID_OverlayRenderer);
 	cmpOverlayRenderer.Reset();
+
+	let yoffset = 0;
+	if (this.enabled)
+		yoffset = this.AddBars(cmpOverlayRenderer, yoffset);
+	yoffset = this.AddAuraIcons(cmpOverlayRenderer, yoffset);
+	return yoffset;
 };
 
-StatusBars.prototype.RegenerateSprites = function()
+// Internal helper functions
+StatusBars.prototype.AddAuraIcons = function(cmpOverlayRenderer, yoffset)
 {
-	var cmpOverlayRenderer = Engine.QueryInterface(this.entity, IID_OverlayRenderer);
-	cmpOverlayRenderer.Reset();
+	let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+	let sources = cmpGuiInterface.GetEntitiesWithStatusBars().filter(e => this.auraSources[e] && this.auraSources[e].length);
 
+	if (!sources.length)
+		return yoffset;
+
+	let iconSet = new Set();
+	for (let ent of sources)
+	{
+		let cmpAuras = Engine.QueryInterface(ent, IID_Auras); 
+		if (!cmpAuras) // probably the ent just died 
+			continue; 
+		for (let name of this.auraSources[ent]) 
+			iconSet.add(cmpAuras.GetOverlayIcon(name)); 
+	}
+
+	// World-space offset from the unit's position
+	var offset = { "x": 0, "y": +this.template.HeightOffset, "z": 0 };
+
+	let iconSize = +this.template.BarWidth / 2; 
+	let xoffset = -iconSize * (iconSet.size - 1) * 0.6
+	for (let icon of iconSet) 
+	{ 
+		cmpOverlayRenderer.AddSprite( 
+			icon, 
+			{ "x": xoffset - iconSize/2, "y": yoffset }, 
+			{ "x": xoffset + iconSize/2, "y": yoffset + iconSize }, 
+			offset
+		); 
+		xoffset += iconSize * 1.2;
+	} 
+
+	return yoffset + iconSize + this.template.BarHeight / 2;
+};
+
+StatusBars.prototype.AddBars = function(cmpOverlayRenderer, yoffset)
+{
 	// Size of health bar (in world-space units)
 	var width = +this.template.BarWidth;
 	var height = +this.template.BarHeight;
@@ -79,26 +133,23 @@
 	// World-space offset from the unit's position
 	var offset = { "x": 0, "y": +this.template.HeightOffset, "z": 0 };
 
-	// Billboard offset of next bar
-	var yoffset = 0;
-
 	var AddBar = function(type, amount)
 	{
 		cmpOverlayRenderer.AddSprite(
 			"art/textures/ui/session/icons/"+type+"_bg.png",
-			{ "x": -width/2, "y": -height/2 + yoffset },
-			{ "x": width/2, "y": height/2 + yoffset },
+			{ "x": -width/2, "y":yoffset },
+			{ "x": width/2, "y": height + yoffset },
 			offset
 		);
 
 		cmpOverlayRenderer.AddSprite(
 			"art/textures/ui/session/icons/"+type+"_fg.png",
-			{ "x": -width/2, "y": -height/2 + yoffset },
-			{ "x": width*(amount - 0.5), "y": height/2 + yoffset },
+			{ "x": -width/2, "y": yoffset },
+			{ "x": width*(amount - 0.5), "y": height + yoffset },
 			offset
 		);
 
-		yoffset -= height * 1.2;
+		yoffset += height * 1.2;
 	};
 
 	var cmpMirage = Engine.QueryInterface(this.entity, IID_Mirage);
@@ -119,6 +170,7 @@
 	else if (cmpMirage && cmpMirage.ResourceSupply())
 		AddBar("supply", cmpMirage.IsInfinite() ? 1 : cmpMirage.GetAmount() / cmpMirage.GetMaxAmount());
 
+	return yoffset;
 	/*
 	// Rank icon disabled for now - see discussion around
 	// http://www.wildfiregames.com/forum/index.php?s=&showtopic=13608&view=findpost&p=212154
Index: binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml	(revision 16600)
+++ binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml	(working copy)
@@ -10,6 +10,7 @@
       </Modifications>
       <AuraName>Healing Aura</AuraName>
       <AuraDescription>Heals nearby units at 1 HP per second.</AuraDescription>
+      <OverlayIcon>art/textures/ui/session/auras/heal.png</OverlayIcon>
     </heal>
   </Auras>
   <BuildRestrictions>
