Index: binaries/data/mods/public/simulation/components/Foundation.js
===================================================================
--- binaries/data/mods/public/simulation/components/Foundation.js	(revision 18558)
+++ binaries/data/mods/public/simulation/components/Foundation.js	(working copy)
@@ -29,6 +29,9 @@
 	// Remember the cost here, so if it changes after construction begins (from auras or technologies)
 	// we will use the correct values to refund partial construction costs
 	let cmpCost = Engine.QueryInterface(this.entity, IID_Cost);
+	if (!cmpCost)
+		error("A foundation must have a cost component to know the build time");
+
 	this.costs = cmpCost.GetResourceCosts(owner);
 
 	this.maxProgress = 0;
@@ -62,7 +65,7 @@
 	var hitpoints = cmpHealth.GetHitpoints();
 	var maxHitpoints = cmpHealth.GetMaxHitpoints();
 
-	return (hitpoints / maxHitpoints);
+	return hitpoints / maxHitpoints;
 };
 
 Foundation.prototype.GetBuildPercentage = function()
@@ -116,24 +119,31 @@
  */
 Foundation.prototype.AddBuilder = function(builderEnt)
 {
-	if (this.builders.indexOf(builderEnt) === -1)
-	{
-		this.builders.push(builderEnt);
-		Engine.QueryInterface(this.entity, IID_Visual).SetVariable("numbuilders", this.builders.length);
-		this.SetBuildMultiplier();
-		Engine.PostMessage(this.entity, MT_FoundationBuildersChanged, { "to": this.builders });
-	}
+	let index = this.builders.indexOf(builderEnt);
+	if (index != -1)
+		return;
+
+	this.builders.push(builderEnt);
+	let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
+	if (cmpVisual)
+		SetVariable("numbuilders", this.builders.length);
+
+	this.SetBuildMultiplier();
+	Engine.PostMessage(this.entity, MT_FoundationBuildersChanged, { "to": this.builders });
 };
 
 Foundation.prototype.RemoveBuilder = function(builderEnt)
 {
-	if (this.builders.indexOf(builderEnt) !== -1)
- 	{
-		this.builders.splice(this.builders.indexOf(builderEnt),1);
-		Engine.QueryInterface(this.entity, IID_Visual).SetVariable("numbuilders", this.builders.length);
- 		this.SetBuildMultiplier();
-		Engine.PostMessage(this.entity, MT_FoundationBuildersChanged, { "to": this.builders });
- 	}
+	let index = this.builders.indexOf(builderEnt);
+	if (index == -1)
+		return;
+
+	this.builders.splice(index ,1);
+	let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
+	if (cmpVisual)
+		SetVariable("numbuilders", this.builders.length);
+	this.SetBuildMultiplier();
+	Engine.PostMessage(this.entity, MT_FoundationBuildersChanged, { "to": this.builders });
  };
 
 /**
@@ -142,6 +152,7 @@
  */
 Foundation.prototype.SetBuildMultiplier = function()
 {
+	// TODO get rid of magic 0.7
 	let numBuilders = this.builders.length;
 	if (numBuilders < 2)
 		this.buildMultiplier = 1;
@@ -199,18 +210,16 @@
 	// Handle the initial 'committing' of the foundation
 	if (!this.committed)
 	{
+		// The obstruction always blocks new foundations/construction,
+		// but we've temporarily allowed units to walk all over it
+		// (via CCmpTemplateManager). Now we need to remove that temporary
+		// blocker-disabling, so that we'll perform standard unit blocking instead.
 		if (cmpObstruction && cmpObstruction.GetBlockMovementFlag())
-		{
-			// The obstruction always blocks new foundations/construction,
-			// but we've temporarily allowed units to walk all over it
-			// (via CCmpTemplateManager). Now we need to remove that temporary
-			// blocker-disabling, so that we'll perform standard unit blocking instead.
 			cmpObstruction.SetDisableBlockMovementPathfinding(false, false, -1);
 
-			// Call the related trigger event 
-			var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
-			cmpTrigger.CallEvent("ConstructionStarted", {"foundation": this.entity, "template": this.finalTemplateName});
-		}
+		// Call the related trigger event 
+		var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+		cmpTrigger.CallEvent("ConstructionStarted", {"foundation": this.entity, "template": this.finalTemplateName});
 
 		// Switch foundation to scaffold variant
 		var cmpFoundationVisual = Engine.QueryInterface(this.entity, IID_Visual);
@@ -249,6 +258,11 @@
 
 	// Add an appropriate proportion of hitpoints
 	var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
+	if (!cmpHealth)
+	{
+		error("Foundation " + this.entity + " does not have a health component.");
+		return;
+	}
 	var maxHealth = cmpHealth.GetMaxHitpoints();
 	var deltaHP = Math.max(work, Math.min(maxHealth, Math.floor(work * this.GetBuildRate() * this.buildMultiplier)));
 	if (deltaHP > 0)
@@ -274,9 +288,21 @@
 			cmpBuildingVisual.SetActorSeed(cmpVisual.GetActorSeed());
 
 		var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
+		if (!cmpPosition || !cmpPosition.IsInWorld())
+		{
+			error("Foundation " + this.entity + " does not have a position in-world.");
+			Engine.DestroyEntity(building);
+			return;
+		}
 		var cmpBuildingPosition = Engine.QueryInterface(building, IID_Position);
-		var pos = cmpPosition.GetPosition();
-		cmpBuildingPosition.JumpTo(pos.x, pos.z);
+		if (!cmpBuildingPosition)
+		{
+			error("New building " + building + " has no position component.");
+			Engine.DestroyEntity(building);
+			return;
+		}
+		var pos = cmpPosition.GetPosition2D();
+		cmpBuildingPosition.JumpTo(pos.x, pos.y);
 		var rot = cmpPosition.GetRotation();
 		cmpBuildingPosition.SetYRotation(rot.y);
 		cmpBuildingPosition.SetXZRotation(rot.x, rot.z);
@@ -307,38 +333,57 @@
 		else
 		{
 			let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
+			if (!cmpOwnership)
+			{
+				error("Foundation " + this.entity + " has no ownership.");
+				Engine.DestroyEntity(building);
+				return;
+			}
 			owner = cmpOwnership.GetOwner();
 		}
 		var cmpBuildingOwnership = Engine.QueryInterface(building, IID_Ownership);
+		if (!cmpBuildingOwnership)
+		{
+			error("New Building " + building + " has no ownership.");
+			Engine.DestroyEntity(building);
+			return;
+		}
 		cmpBuildingOwnership.SetOwner(owner);
 		
-		// ----------------------------------------------------------------------
+		/*
+		Copy over the obstruction control group IDs from the foundation
+		entities. This is needed to ensure that when a foundation is completed
+		and replaced by a new entity, it remains in the same control group(s)
+		as any other foundation entities that may surround it. This is the
+		mechanism that is used to e.g. enable wall pieces to be built closely
+		together, ignoring their mutual obstruction shapes (since they would
+		otherwise be prevented from being built so closely together). If the
+		control groups are not copied over, the new entity will default to a
+		new control group containing only itself, and will hence block
+		construction of any surrounding foundations that it was previously in
+		the same control group with.
 		
-		// Copy over the obstruction control group IDs from the foundation entities. This is needed to ensure that when a foundation
-		// is completed and replaced by a new entity, it remains in the same control group(s) as any other foundation entities that 
-		// may surround it. This is the mechanism that is used to e.g. enable wall pieces to be built closely together, ignoring their
-		// mutual obstruction shapes (since they would otherwise be prevented from being built so closely together). If the control 
-		// groups are not copied over, the new entity will default to a new control group containing only itself, and will hence block
-		// construction of any surrounding foundations that it was previously in the same control group with.
+		Note that this will result in the completed building entities having
+		control group IDs that equal entity IDs of old (and soon to be deleted)
+		foundation entities. This should not have any consequences, however,
+		since the control group IDs are only meant to be unique identifiers,
+		which is still true when reusing the old ones.
+		*/
 		
-		// Note that this will result in the completed building entities having control group IDs that equal entity IDs of old (and soon
-		// to be deleted) foundation entities. This should not have any consequences, however, since the control group IDs are only meant
-		// to be unique identifiers, which is still true when reusing the old ones.
-		
-		var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
 		var cmpBuildingObstruction = Engine.QueryInterface(building, IID_Obstruction);
-		cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup());
-		cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());
+		if (cmpObstruction && cmpBuildingObstruction)
+		{
+			cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup());
+			cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());
+		}
 		
-		// ----------------------------------------------------------------------
-
 		var cmpPlayerStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker);
 		if (cmpPlayerStatisticsTracker)
 			cmpPlayerStatisticsTracker.IncreaseConstructedBuildingsCounter(building);
 
-		var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
 		var cmpBuildingHealth = Engine.QueryInterface(building, IID_Health);
-		cmpBuildingHealth.SetHitpoints(cmpHealth.GetHitpoints());
+		if (cmpBuildingHealth)
+			cmpBuildingHealth.SetHitpoints(cmpHealth.GetHitpoints());
 
 		PlaySound("constructed", building);
 
Index: binaries/data/mods/public/simulation/components/tests/test_Foundation.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_Foundation.js	(nonexistent)
+++ binaries/data/mods/public/simulation/components/tests/test_Foundation.js	(working copy)
@@ -0,0 +1,129 @@
+/*Engine.LoadHelperScript("ValueModification.js");
+Engine.LoadComponentScript("interfaces/Auras.js");
+Engine.LoadComponentScript("interfaces/AuraManager.js");
+Engine.LoadComponentScript("interfaces/TechnologyManager.js");
+Engine.LoadComponentScript("Auras.js");
+Engine.LoadComponentScript("AuraManager.js");*/
+
+Engine.LoadHelperScript("Player.js");
+Engine.LoadComponentScript("interfaces/Cost.js");
+Engine.LoadComponentScript("interfaces/Foundation.js");
+Engine.LoadComponentScript("interfaces/Health.js");
+Engine.LoadComponentScript("interfaces/RallyPoint.js");
+Engine.LoadComponentScript("interfaces/StatisticsTracker.js");
+Engine.LoadComponentScript("interfaces/TerritoryDecay.js");
+Engine.LoadComponentScript("interfaces/Trigger.js");
+Engine.LoadComponentScript("Foundation.js");
+
+AddMock(SYSTEM_ENTITY, IID_Trigger, {
+	"CallEvent": () => {},
+});
+
+let player = 1;
+let playerEnt = 3;
+
+AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
+	"GetPlayerByID": () => playerEnt,
+});
+
+AddMock(playerEnt, IID_StatisticsTracker, {
+	"IncreaseConstructedBuildingsCounter": b => { TS_ASSERT_EQUALS(b, newEnt); },
+});
+
+let foundationEnt = 20;
+let newEnt = 21;
+let finalTemplate = "structures/athen_civil_centre.xml";
+let foundationHP = 1;
+let maxHP = 100;
+
+AddMock(foundationEnt, IID_Cost, {
+	"GetBuildTime": () => 50,
+	"GetResourceCosts": () => ({ "wood": 100 }),
+});
+
+AddMock(foundationEnt, IID_Health, {
+	"GetHitpoints": () => foundationHP,
+	"GetMaxHitpoints": () => maxHP,
+	"Increase": (a) => { foundationHP = Math.min(foundationHP + a, maxHP); cmpFoundation.OnHealthChanged(); },
+});
+
+AddMock(foundationEnt, IID_Obstruction, {
+	"GetBlockMovementFlag": () => true,
+	"GetUnitCollisions": () => [],
+	"SetDisableBlockMovementPathfinding": () => {},
+});
+
+AddMock(foundationEnt, IID_Ownership, {
+	"GetOwner": () => player,
+});
+
+AddMock(foundationEnt, IID_Position, {
+	"GetPosition2D": () => new Vector2D(1, 2),
+	"GetRotation": () => new Vector3D(1, 2, 3),
+	"IsInWorld": () => true,
+});
+
+Engine.AddEntity = function(template)
+{
+	AddMock(newEnt, IID_Position, {
+		"JumpTo": (x, y) => { TS_ASSERT_EQUALS(x, 1); TS_ASSERT_EQUALS(y, 2); },
+		"SetYRotation": r => { TS_ASSERT_EQUALS(r, 2); },
+		"SetXZRotation": (rx, rz) => { TS_ASSERT_EQUALS(rx, 1); TS_ASSERT_EQUALS(rz, 3); },
+	});
+	AddMock(newEnt, IID_Ownership, {
+		"SetOwner": owner => { TS_ASSERT_EQUALS(owner, player); },
+	});
+	return newEnt;
+};
+
+let PlaySound = function(name, source)
+{
+	TS_ASSERT_EQUALS(name, "constructed");
+	TS_ASSERT_EQUALS(source, newEnt);
+};
+Engine.RegisterGlobal("PlaySound", PlaySound);
+Engine.RegisterGlobal("MT_EntityRenamed", "entityRenamed");
+
+let cmpFoundation = ConstructComponent(foundationEnt, "Foundation", {});
+
+// INITIALISE
+cmpFoundation.InitialiseConstruction(player, finalTemplate);
+
+TS_ASSERT_EQUALS(cmpFoundation.owner, player);
+TS_ASSERT_EQUALS(cmpFoundation.finalTemplateName, finalTemplate);
+TS_ASSERT_EQUALS(cmpFoundation.maxProgress, 0);
+TS_ASSERT_EQUALS(cmpFoundation.initialised, true);
+
+// BUILDER COUNT
+TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 0);
+cmpFoundation.AddBuilder(10);
+TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 1);
+TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, 1);
+cmpFoundation.AddBuilder(11);
+TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 2);
+TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, Math.pow(2, 0.7) / 2);
+cmpFoundation.AddBuilder(11);
+TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 2);
+TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, Math.pow(2, 0.7) / 2);
+cmpFoundation.RemoveBuilder(11);
+TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 1);
+TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, 1);
+cmpFoundation.RemoveBuilder(11);
+TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 1);
+TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, 1);
+
+// COMMIT FOUNDATION
+TS_ASSERT_EQUALS(cmpFoundation.committed, false);
+let work = 5;
+cmpFoundation.Build(10, work);
+TS_ASSERT_EQUALS(cmpFoundation.committed, true);
+TS_ASSERT_EQUALS(foundationHP, 1 + work * cmpFoundation.GetBuildRate() * cmpFoundation.buildMultiplier);
+TS_ASSERT_EQUALS(cmpFoundation.maxProgress, foundationHP / maxHP);
+
+// FINISH CONSTRUCTION
+cmpFoundation.Build(10, 1000);
+TS_ASSERT_EQUALS(cmpFoundation.maxProgress, 1);
+TS_ASSERT_EQUALS(foundationHP, maxHP);
+
+
+
 
