Index: binaries/data/mods/public/gui/session/unit_commands.js
===================================================================
--- binaries/data/mods/public/gui/session/unit_commands.js	(revision 11386)
+++ binaries/data/mods/public/gui/session/unit_commands.js	(working copy)
@@ -531,7 +531,7 @@
 				function (item) { unload(entState.id, groups.getEntsByName(item)); } );
 		}
 
-		var formations = getEntityFormationsList(entState);
+		var formations = Engine.GuiInterfaceCall("GetAvailableFormations");
 		if (hasClass(entState, "Unit") && !hasClass(entState, "Animal") && !entState.garrisonHolder && formations.length)
 		{
 			setupUnitPanel("Formation", usedPanels, entState, formations,
Index: binaries/data/mods/public/gui/session/utility_functions.js
===================================================================
--- binaries/data/mods/public/gui/session/utility_functions.js	(revision 11386)
+++ binaries/data/mods/public/gui/session/utility_functions.js	(working copy)
@@ -121,30 +121,6 @@
 	return dmgArray.join("[font=\"serif-12\"], [/font]");
 }
 
-function getEntityFormationsList(entState)
-{
-	var civ = g_Players[entState.player].civ;
-	var formations = getCivFormations(civ);
-	return formations;
-}
-
-function getCivFormations(civ)
-{
-	// TODO: this should come from the civ JSON files instead
-
-	var civFormations = ["Scatter", "Box", "Column Closed", "Line Closed", "Column Open", "Line Open", "Flank", "Skirmish", "Wedge", "Battle Line"];
-	if (civ == "hele")
-	{
-		civFormations.push("Phalanx");
-		civFormations.push("Syntagma");
-	}
-	else if (civ == "rome")
-	{
-		civFormations.push("Testudo");
-	}
-	return civFormations;
-}
-
 function getEntityCommandsList(entState)
 {
 	var commands = [];
Index: binaries/data/mods/public/simulation/components/Formation.js
===================================================================
--- binaries/data/mods/public/simulation/components/Formation.js	(revision 11386)
+++ binaries/data/mods/public/simulation/components/Formation.js	(working copy)
@@ -214,17 +214,20 @@
 
 	// Choose a sensible size/shape for the various formations, depending on number of units
 	var cols;
-	if (columnar || this.formationName == "Column Closed")
+	
+	if (columnar)
+		this.formationName = "Column Closed";
+	switch(this.formationName)
 	{
+	case "Column Closed":
 		// Have at most 3 files
 		if (count <= 3)
 			cols = count;
 		else
 			cols = 3;
 		shape = "square";
-	}
-	else if (this.formationName == "Phalanx")
-	{
+		break;
+	case "Phalanx":
 		// Try to have at least 5 files (so batch training gives a single line),
 		// and at most 8
 		if (count <= 5)
@@ -238,9 +241,8 @@
 		else
 			cols = Math.ceil(count/6);
 		shape = "square";
-	}
-	else if (this.formationName == "Line Closed")
-	{
+		break;
+	case "Line Closed":
 		if (count <= 3)
 			cols = count;
 		else if (count < 30)
@@ -248,19 +250,16 @@
 		else
 			cols = Math.ceil(count/3);
 		shape = "square";
-	}
-	else if (this.formationName == "Testudo")
-	{
+		break;
+	case "Testudo":
 		cols = Math.ceil(Math.sqrt(count));
 		shape = "square";
-	}
-	else if (this.formationName == "Column Open")
-	{
-		cols = 2
+		break;
+	case "Column Open":
+		cols = 2;
 		shape = "opensquare";
-	}
-	else if (this.formationName == "Line Open")
-	{
+		break;
+	case "Line Open":
 		if (count <= 5)
 			cols = 3;
 		else if (count <= 11)
@@ -270,18 +269,16 @@
 		else
 			cols = 6;
 		shape = "opensquare";
-	}
-	else if (this.formationName == "Scatter")
-	{
+		break;
+	case "Scatter":
 		var width = Math.sqrt(count) * separation * 5;
 
 		for (var i = 0; i < count; ++i)
 		{
 			offsets.push({"x": Math.random()*width, "z": Math.random()*width});
 		}
-	}
-	else if (this.formationName == "Circle")
-	{
+		break;
+	case "Circle":
 		var depth;
 		var pop;
 		if (count <= 36)
@@ -291,7 +288,7 @@
 		}
 		else
 		{
-			depth = 3
+			depth = 3;
 			pop = Math.ceil(count / depth);
 		}
 
@@ -311,9 +308,8 @@
 				left--;
 			}
 		}
-	}
-	else if (this.formationName == "Box")
-	{
+		break;
+	case "Box":
 		var root = Math.ceil(Math.sqrt(count));
 
 		var left = count;
@@ -334,7 +330,9 @@
 					meleeleft -= stodo;
 				}
 				else	// compact
+				{
 					stodo = Math.max(0, left - (width-2)*(width-2));
+				}
 			}
 
 			for (var r = -sq; r <= sq && stodo; ++r)
@@ -352,14 +350,12 @@
 				}
 			}
 		}
-	}
-	else if (this.formationName == "Skirmish")
-	{
+		break;
+	case "Skirmish":
 		cols = Math.ceil(count/2);
 		shape = "opensquare";
-	}
-	else if (this.formationName == "Wedge")
-	{
+		break;
+	case "Wedge":
 		var depth = Math.ceil(Math.sqrt(count));
 
 		var left = count;
@@ -387,9 +383,8 @@
 				}
 			}
 		}
-	}
-	else if (this.formationName == "Flank")
-	{
+		break;
+	case "Flank":
 		cols = 3;
 		var leftside = [];
 		leftside[0] = Math.ceil(count/2);
@@ -412,14 +407,12 @@
 				left -= n;
 			}
 		}
-	}
-	else if (this.formationName == "Syntagma")
-	{
-		var cols = Math.ceil(Math.sqrt(count));
+		break;
+	case "Syntagma":
+		cols = Math.ceil(Math.sqrt(count));
 		shape = "square";
-	}
-	else if (this.formationName == "Battle Line")
-	{
+		break;
+	case "Battle Line":
 		if (count <= 5)
 			cols = count;
 		else if (count <= 10)
@@ -433,6 +426,10 @@
 		shape = "opensquare";
 		separation /= 1.5;
 		ordering = "cavalryOnTheSides";
+		break;
+	default:	// We encountered an unknown formation (probably due to a modding attempt) -> Warn the user
+		warn("Formation.js: ComputeFormationOffsets: unknown formation: " + this.formationName);
+		break;
 	}
 
 	if (shape == "square")
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js	(revision 11386)
+++ binaries/data/mods/public/simulation/components/GuiInterface.js	(working copy)
@@ -362,6 +362,13 @@
 		return "";
 };
 
+GuiInterface.prototype.GetAvailableFormations = function(player, data)
+{
+	var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
+	var cmpPlayer = Engine.QueryInterface(cmpPlayerMan.GetPlayerByID(player), IID_Player);
+	return cmpPlayer.GetFormations();
+};
+
 GuiInterface.prototype.GetFormationRequirements = function(player, data)
 {
 	return GetFormationRequirements(data.formationName);
@@ -804,6 +811,7 @@
 	"GetTemplateData": 1,
 	"GetNextNotification": 1,
 
+	"GetAvailableFormations": 1,
 	"GetFormationRequirements": 1,
 	"CanMoveEntsIntoFormation": 1,
 	"IsFormationSelected": 1,
Index: binaries/data/mods/public/simulation/components/Identity.js
===================================================================
--- binaries/data/mods/public/simulation/components/Identity.js	(revision 11386)
+++ binaries/data/mods/public/simulation/components/Identity.js	(working copy)
@@ -123,6 +123,11 @@
 	return this.GetFormationsList().indexOf(name) != -1;
 };
 
+Identity.prototype.CanUseFormations = function()
+{
+	return this.GetFormationsList() == [] ? false : true;
+};
+
 Identity.prototype.GetSelectionGroupName = function()
 {
 	return (this.template.SelectionGroupName || "");
Index: binaries/data/mods/public/simulation/components/Player.js
===================================================================
--- binaries/data/mods/public/simulation/components/Player.js	(revision 11386)
+++ binaries/data/mods/public/simulation/components/Player.js	(working copy)
@@ -25,6 +25,7 @@
 	this.diplomacy = [];	// array of diplomatic stances for this player with respect to other players (including gaia and self)
 	this.conquestCriticalEntitiesCount = 0; // number of owned units with ConquestCritical class
 	this.phase = "village";
+	this.formations = [];
 	this.startCam = undefined;
 	this.controlAllUnits = false;
 	this.isAI = false;
@@ -233,6 +234,16 @@
 	this.phase = p;
 };
 
+Player.prototype.GetFormations = function()
+{
+	return this.formations;
+};
+
+Player.prototype.SetFormations = function(formations)
+{
+	this.formations = formations;
+};
+
 Player.prototype.GetStartingCameraPos = function()
 {
 	return this.startCam.position;
Index: binaries/data/mods/public/simulation/data/cart.json
===================================================================
--- binaries/data/mods/public/simulation/data/cart.json	(revision 0)
+++ binaries/data/mods/public/simulation/data/cart.json	(working copy)
@@ -0,0 +1,15 @@
+{
+  "Formations":
+  [
+    "Scatter",
+    "Box",
+    "Column Closed",
+    "Line Closed",
+    "Column Open",
+    "Line Open",
+    "Flank",
+    "Skirmish",
+    "Wedge",
+    "Battle Line"
+  ]
+}
Index: binaries/data/mods/public/simulation/data/celt.json
===================================================================
--- binaries/data/mods/public/simulation/data/celt.json	(revision 0)
+++ binaries/data/mods/public/simulation/data/celt.json	(working copy)
@@ -0,0 +1,15 @@
+{
+  "Formations":
+  [
+    "Scatter",
+    "Box",
+    "Column Closed",
+    "Line Closed",
+    "Column Open",
+    "Line Open",
+    "Flank",
+    "Skirmish",
+    "Wedge",
+    "Battle Line"
+  ]
+}
Index: binaries/data/mods/public/simulation/data/hele.json
===================================================================
--- binaries/data/mods/public/simulation/data/hele.json	(revision 0)
+++ binaries/data/mods/public/simulation/data/hele.json	(working copy)
@@ -0,0 +1,17 @@
+{
+  "Formations":
+  [
+    "Scatter",
+    "Box",
+    "Column Closed",
+    "Line Closed",
+    "Column Open",
+    "Line Open",
+    "Flank",
+    "Skirmish",
+    "Wedge",
+    "Battle Line",
+    "Phalanx",
+    "Syntagma"
+  ]
+}
Index: binaries/data/mods/public/simulation/data/iber.json
===================================================================
--- binaries/data/mods/public/simulation/data/iber.json	(revision 0)
+++ binaries/data/mods/public/simulation/data/iber.json	(working copy)
@@ -0,0 +1,15 @@
+{
+  "Formations":
+  [
+    "Scatter",
+    "Box",
+    "Column Closed",
+    "Line Closed",
+    "Column Open",
+    "Line Open",
+    "Flank",
+    "Skirmish",
+    "Wedge",
+    "Battle Line"
+  ]
+}
Index: binaries/data/mods/public/simulation/data/pers.json
===================================================================
--- binaries/data/mods/public/simulation/data/pers.json	(revision 0)
+++ binaries/data/mods/public/simulation/data/pers.json	(working copy)
@@ -0,0 +1,15 @@
+{
+  "Formations":
+  [
+    "Scatter",
+    "Box",
+    "Column Closed",
+    "Line Closed",
+    "Column Open",
+    "Line Open",
+    "Flank",
+    "Skirmish",
+    "Wedge",
+    "Battle Line"
+  ]
+}
Index: binaries/data/mods/public/simulation/data/rome.json
===================================================================
--- binaries/data/mods/public/simulation/data/rome.json	(revision 0)
+++ binaries/data/mods/public/simulation/data/rome.json	(working copy)
@@ -0,0 +1,16 @@
+{
+  "Formations":
+  [
+    "Scatter",
+    "Box",
+    "Column Closed",
+    "Line Closed",
+    "Column Open",
+    "Line Open",
+    "Flank",
+    "Skirmish",
+    "Wedge",
+    "Battle Line",
+    "Testudo"
+  ]
+}
Index: binaries/data/mods/public/simulation/helpers/Commands.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Commands.js	(revision 11386)
+++ binaries/data/mods/public/simulation/helpers/Commands.js	(working copy)
@@ -380,7 +380,7 @@
 
 	case "formation":
 		var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
-		GetFormationUnitAIs(entities).forEach(function(cmpUnitAI) {
+		GetFormationUnitAIs(entities, cmd.name).forEach(function(cmpUnitAI) {
 			var cmpFormation = Engine.QueryInterface(cmpUnitAI.entity, IID_Formation);
 			if (!cmpFormation)
 				return;
@@ -481,7 +481,7 @@
  * Returns a list of UnitAI components, each belonging either to a
  * selected unit or to a formation entity for groups of the selected units.
  */
-function GetFormationUnitAIs(ents)
+function GetFormationUnitAIs(ents, formName)
 {
 	// If an individual was selected, remove it from any formation
 	// and command it individually
@@ -508,13 +508,13 @@
 			continue;
 
 		var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
-		// TODO: Currently we use LineClosed as effectively a boolean flag
-		// to determine whether formations are allowed at all. Instead we
-		// should check specific formation names and do something sensible
-		// (like what?) when some units don't support them.
-		// TODO: We'll also need to fix other formation code to use
-		// "LineClosed" instead of "Line Closed" etc consistently.
-		if (cmpIdentity && cmpIdentity.CanUseFormation("LineClosed"))
+		// TODO: If formName is undefined we just check whether we can 
+		// use LineClosed instead of checking the current formation.
+		// NOTE: We should keep "LineClosed" (instead of "Line Closed")
+		// to Commands.js, Identity.js and the xml files. If you need to
+		// access the formation data from the xml files use .replace(/\s+/,'').
+		if (cmpIdentity	&& cmpIdentity.CanUseFormations()
+			&& cmpIdentity.CanUseFormation(formName === undefined ? "LineClosed" : formName.replace(/\s+/,'')))
 			formedEnts.push(ent);
 		else
 			nonformedUnitAIs.push(cmpUnitAI);
@@ -651,6 +651,7 @@
 	var count = ents.length;
 
 	// TODO: should check the player's civ is allowed to use this formation
+	// See simulation/components/Player.js GetFormations() for a list of all allowed formations
 
 	var requirements = GetFormationRequirements(formationName);
 	if (!requirements)
Index: binaries/data/mods/public/simulation/helpers/Player.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Player.js	(revision 11386)
+++ binaries/data/mods/public/simulation/helpers/Player.js	(working copy)
@@ -118,6 +118,22 @@
 				}
 			}
 			
+			// If formations are explicitly defined, use that; otherwise use civ defaults
+			if (getSetting(pData, pDefs, "Formations") !== undefined)
+			{
+				cmpPlayer.SetFormations(getSetting(pData, pDefs, "Formations"));
+			}
+			else
+			{
+				// We read the formation data from simulation/data/{civ}.json
+				var rawFormations = Engine.ReadJSONFile(cmpPlayer.GetCiv()+".json");
+				if (!(rawFormations && rawFormations.Formations))
+				{
+					throw("Player.js: Error reading "+cmpPlayer.GetCiv()+".json");
+				}
+				cmpPlayer.SetFormations(rawFormations.Formations);
+			}
+			
 			var startCam = getSetting(pData, pDefs, "StartingCamera");
 			if (startCam !== undefined)
 			{
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 11386)
+++ binaries/data/mods/public/simulation/templates/template_unit_support_female_citizen.xml	(working copy)
@@ -26,7 +26,7 @@
   <Builder>
     <Rate>1.0</Rate>
     <Entities datatype="tokens">
-	  structures/{civ}_house
+      structures/{civ}_house
       structures/{civ}_mill
       structures/{civ}_farmstead
       structures/{civ}_field
@@ -51,6 +51,21 @@
     <History>Women in the ancient world took on a variety of roles - from leadership (Celts) to servant (Greeks). Women are hard workers, the economic backbone of any civilisation. In history, it was typical when all the males (capable of fighting) were killed for the females, children, and elderly to be sold as slaves.</History>
     <Tooltip>Gather resources, build civic structures, and inspire nearby males to work faster. Bonused at foraging and farming.</Tooltip>
     <Classes datatype="tokens">Worker Female</Classes>
+    <Formations datatype="tokens">
+      -Scatter
+      -Box
+      -ColumnClosed
+      -LineClosed
+      -ColumnOpen
+      -LineOpen
+      -Flank
+      -Skirmish
+      -Wedge
+      -Testudo
+      -Phalanx
+      -Syntagma
+      -BattleLine
+    </Formations>
   </Identity>
   <ResourceGatherer>
     <MaxDistance>2.0</MaxDistance>
@@ -69,8 +84,8 @@
   </ResourceGatherer>
   <Sound>
     <SoundGroups>
-	  <select>voice/hellenes/civ/civ_female_select.xml</select>
-	  <order_walk>voice/hellenes/civ/civ_female_select.xml</order_walk>
+      <select>voice/hellenes/civ/civ_female_select.xml</select>
+      <order_walk>voice/hellenes/civ/civ_female_select.xml</order_walk>
       <order_attack>voice/hellenes/civ/civ_female_select.xml</order_attack>
       <order_gather>voice/hellenes/civ/civ_female_select.xml</order_gather>
       <order_repair>voice/hellenes/civ/civ_female_select.xml</order_repair>

