Index: binaries/data/mods/public/maps/random/survivalofthefittest.js
===================================================================
--- binaries/data/mods/public/maps/random/survivalofthefittest.js	(revision 18600)
+++ binaries/data/mods/public/maps/random/survivalofthefittest.js	(working copy)
@@ -198,7 +198,7 @@
 	);
 	createArea(placer, [terrainPainter, elevationPainter, paintClass(clWater)], null);
 	
-	//creating female citizens
+	// Creating seeker woman (these need to have a lower ID than other starting woman for InitGame to identify them)
 	var femaleLocation = getTIPIADBON([ix, iz], [mapSize / 2, mapSize / 2], [-3 , 3.5], 1, 3);
 	if (femaleLocation !== undefined)
 	{
Index: binaries/data/mods/public/maps/random/survivalofthefittest_triggers.js
===================================================================
--- binaries/data/mods/public/maps/random/survivalofthefittest_triggers.js	(revision 18600)
+++ binaries/data/mods/public/maps/random/survivalofthefittest_triggers.js	(working copy)
@@ -1,5 +1,34 @@
-var treasures =
-[
+/**
+ * Time in minutes to the first enemy wave.
+ */
+var firstAttackWave = 5 + 10 * Math.random();
+
+/**
+ * Time after which resources are distributed periodically.
+ */
+var provideResourcesOffset = 14 + 2 * Math.random();
+
+/**
+ * Time interval between enemy waves.
+ */
+var attackerWaveInterval = [2, 3];
+
+/**
+ * Time interval when treasures are distributed periodically.
+ */
+var treasureInterval = [3, 4];
+
+/**
+ * Time interval after which resources are distributed periodically.
+ */
+var provideResourcesInterval = [1.25, 1.75];
+
+/**
+ * Amount of miliseconds in minute.
+ */
+const minutes = 60 * 1000;
+
+var treasures = [
 	"gaia/special_treasure_food_barrel",
 	"gaia/special_treasure_food_bin",
 	"gaia/special_treasure_food_crate",
@@ -10,14 +39,17 @@
 	"gaia/special_treasure_wood",
 	"gaia/special_treasure_wood"
 ];
-var attackerEntityTemplates =
-[
+
+/**
+ * Templates of the attacking units, sorted by civ
+ */
+var attackerTemplates = [
 	[
 		"units/athen_champion_infantry",
 		"units/athen_champion_marine",
 		"units/athen_champion_ranged",
-		"units/athen_siege_lithobolos_packed",
-		"units/athen_siege_oxybeles_packed",
+		"units/athen_mechanical_siege_lithobolos_packed",
+		"units/athen_mechanical_siege_oxybeles_packed",
 	],
 	[
 		"units/brit_champion_cavalry",
@@ -88,39 +120,35 @@
 Trigger.prototype.StartAnEnemyWave = function()
 {
 	let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
-	let attackerEntities = attackerEntityTemplates[Math.floor(Math.random() * attackerEntityTemplates.length)];
-	// A soldier for each 2-3 minutes of the game. Should be waves of 20 soldiers after an hour
-	let nextTime = Math.round(120000 + Math.random() * 60000);
-	let attackerCount = Math.round(cmpTimer.GetTime() / nextTime / attackerEntities.length);
+	let templates = attackerTemplates[Math.floor(Math.random() * attackerTemplates.length)];
+	let nextTime = Math.round(RandomReal(attackerWaveInterval) * minutes);
+	let attackerCount = Math.ceil(cmpTimer.GetTime() / nextTime / templates.length);
 
-	// spawn attackers
-	let attackers =  [];
-	for (let attackerEntity of attackerEntities)
-		attackers.push(TriggerHelper.SpawnUnitsFromTriggerPoints("A", attackerEntity, attackerCount, 0));
+	let attackers = templates.map(ent =>
+		TriggerHelper.SpawnUnitsFromTriggerPoints("A", ent, attackerCount, 0));
 
 	for (let entityType of attackers)
-	{
 		for (let origin in entityType)
 		{
 			let cmpPlayer = QueryOwnerInterface(+origin, IID_Player);
-			if (cmpPlayer.GetState() != "active")
+			if (!cmpPlayer || !cmpTrigger.playerCivicCenter[cmpPlayer.GetPlayerID()])
 				continue;
 
-			let cmpPosition =  Engine.QueryInterface(this.playerCivicCenter[cmpPlayer.GetPlayerID()], IID_Position);
-			// this shouldn't happen if the player is still active
+			let cmpPosition = Engine.QueryInterface(cmpTrigger.playerCivicCenter[cmpPlayer.GetPlayerID()], IID_Position);
+			// This shouldn't happen if the player is still active
 			if (!cmpPosition || !cmpPosition.IsInWorld)
 				continue;
 
-			// store the x and z coordinates in the command
-			let cmd = cmpPosition.GetPosition();
-			cmd.type = "attack-walk";
-			cmd.entities = entityType[origin];
-			cmd.queued = true;
-			cmd.targetClasses = undefined;
-			// send the attack-walk command
-			ProcessCommand(0, cmd);
+			let pos = cmpPosition.GetPosition();
+			ProcessCommand(0, {
+				"type": "attack-walk",
+				"entities": entityType[origin],
+				"x": pos.x,
+				"z": pos.z,
+				"targetClasses": undefined, 
+				"queued": true
+			});
 		}
-	}
 
 	let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
 	cmpGUIInterface.PushNotification({
@@ -127,21 +155,36 @@
 		"message": markForTranslation("An enemy wave is attacking!"),
 		"translateMessage": true
 	});
-	cmpTrigger.DoAfterDelay(nextTime, "StartAnEnemyWave", {}); // The next wave will come in 3 minutes
+	cmpTrigger.DoAfterDelay(nextTime, "StartAnEnemyWave", {});
 };
 
+/**
+ * Find all of the civic centers and females, disable some structures, setup activity watch.
+ */
 Trigger.prototype.InitGame = function()
 {
 	let numberOfPlayers = TriggerHelper.GetNumberOfPlayers();
-	// Find all of the civic centers, disable some structures
 	for (let i = 1; i < numberOfPlayers; ++i)
 	{
 		let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
 		let playerEntities = cmpRangeManager.GetEntitiesByPlayer(i); // Get all of each player's entities
 
+		let foundSeeker = false;
 		for (let entity of playerEntities)
+		{
 			if (TriggerHelper.EntityHasClass(entity, "CivilCentre"))
 				cmpTrigger.playerCivicCenter[i] = entity;
+
+			if (TriggerHelper.EntityHasClass(entity, "Female") && !foundSeeker)
+			{
+				foundSeeker = true;
+				let cmpDamageReceiver = Engine.QueryInterface(entity, IID_DamageReceiver);
+				cmpDamageReceiver.SetInvulnerability(true);
+
+				let cmpHealth = Engine.QueryInterface(entity, IID_Health);
+				cmpHealth.SetUndeletable(true);
+			}
+		}
 	}
 
 	// Fix alliances
@@ -158,8 +201,7 @@
 	// Make gaia black
 	QueryPlayerIDInterface(0).SetColor(0, 0, 0);
 
-	// Place the treasures
-	this.PlaceTreasures();
+	this.placeTreasures();
 
 	// Disable farms, civic centers and walls for all players
 	for (let i = 1; i < numberOfPlayers; ++i)
@@ -166,6 +208,7 @@
 	{
 		let cmpPlayer = QueryPlayerIDInterface(i);
 		let civ = cmpPlayer.GetCiv();
+
 		cmpPlayer.SetDisabledTemplates([
 			"structures/" + civ + "_field",
 			"structures/" + civ + "_corral",
@@ -177,22 +220,64 @@
 	}
 };
 
-Trigger.prototype.PlaceTreasures = function()
+Trigger.prototype.placeTreasures = function()
 {
 	let point = ["B", "C", "D"][Math.floor(Math.random() * 3)];
 	let triggerPoints = cmpTrigger.GetTriggerPoints(point);
 	for (let point of triggerPoints)
 	{
-		let template = treasures[Math.floor(Math.random() * treasures.length)]
-		TriggerHelper.SpawnUnits(point, template, 1, 0);
+		let template = treasures[Math.floor(Math.random() * treasures.length)];
+		let cmpPosition = Engine.QueryInterface(point, IID_Position);
+		let ent = Engine.AddEntity(template);
+
+		let cmpEntOwnership = Engine.QueryInterface(ent, IID_Ownership);
+		let cmpEntPosition = Engine.QueryInterface(ent, IID_Position);
+
+		if (cmpEntOwnership)
+			cmpEntOwnership.SetOwner(0);
+
+		let xOffset = RandomInt(0, 23);
+		let zOffset = RandomInt(0, 23);
+
+		if (Math.random() >= 0.8)
+		{
+			xOffset += RandomInt(5, 8);
+			zOffset += RandomInt(5, 8);
+		}
+
+		if (Math.round(Math.random()) == 1)
+			xOffset = -xOffset;
+
+		if (Math.round(Math.random()) == 1)
+			zOffset = -zOffset;
+
+		cmpEntPosition.JumpTo(cmpPosition.GetPosition().x + xOffset, cmpPosition.GetPosition().z - zOffset);
 	}
-	cmpTrigger.DoAfterDelay(4*60*1000, "PlaceTreasures", {}); //Place more treasures after 4 minutes
+
+	cmpTrigger.DoAfterDelay(RandomReal(treasureInterval) * minutes, "placeTreasures", {});
+
+	let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+	cmpGUIInterface.PushNotification({
+		"message": markForTranslation("New treasures have been placed!"),
+		"translateMessage": true
+	});
 };
 
 Trigger.prototype.InitializeEnemyWaves = function()
 {
-	let time = (5 + Math.round(Math.random() * 10)) * 60 * 1000;
 	let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+
+	cmpGUIInterface.PushNotification({
+		"message": markForTranslation("Welcome to Survival of the Fittest"),
+		"translateMessage": true
+	});
+
+	cmpGUIInterface.PushNotification({
+		"message": markForTranslation("Collect treasures with your woman to prepare for the enemies."),
+		"translateMessage": true
+	});
+
+	let time = firstAttackWave * minutes;
 	cmpGUIInterface.AddTimeNotification({
 		"message": markForTranslation("The first wave will start in %(time)s!"),
 		"translateMessage": true
@@ -200,33 +285,39 @@
 	cmpTrigger.DoAfterDelay(time, "StartAnEnemyWave", {});
 };
 
-Trigger.prototype.DefeatPlayerOnceCCIsDestroyed = function(data)
+Trigger.prototype.defeatPlayerOnceCCIsDestroyed = function(data)
 {
-	// Defeat a player that has lost his civic center
-	if (data.entity == cmpTrigger.playerCivicCenter[data.from])
-	{
+	if (data.entity == cmpTrigger.playerCivicCenter[data.from] && data.to == -1)
 		TriggerHelper.DefeatPlayer(data.from);
+};
 
-		// Check if only one player remains. He will be the winner.
-		let lastPlayerStanding = 0;
-		let numPlayersStanding = 0;
-		let numberOfPlayers = TriggerHelper.GetNumberOfPlayers();
-		for (let i = 1; i < numberOfPlayers; ++i)
-		{
-			if (QueryPlayerIDInterface(i).GetState() == "active")
-			{
-				lastPlayerStanding = i;
-				++numPlayersStanding;
-			}
-		}
-		if (numPlayersStanding == 1)
-			TriggerHelper.SetPlayerWon(lastPlayerStanding);
+Trigger.prototype.provideResources = function(data)
+{
+	if (data.first)
+	{
+		let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+		cmpGUIInterface.PushNotification({
+			"message": markForTranslation("From now on, all players will receive some resources from time to time."),
+			"translateMessage": true
+		});
 	}
+
+	let resources = {};
+	for (let type of ["food", "wood", "stone", "metal"])
+		resources[type] = 50 + Math.round(Math.random() * 100);
+
+	let numberOfPlayers = TriggerHelper.GetNumberOfPlayers();
+	for (let i = 1; i < numberOfPlayers; ++i)
+		QueryPlayerIDInterface(i).AddResources(resources);
+
+	cmpTrigger.DoAfterDelay(RandomReal(provideResourcesInterval) * minutes, "provideResources", {});
 };
 
 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
 cmpTrigger.playerCivicCenter = {};
+
 cmpTrigger.DoAfterDelay(0, "InitGame", {});
 cmpTrigger.DoAfterDelay(1000, "InitializeEnemyWaves", {});
+cmpTrigger.DoAfterDelay(provideResourcesOffset * minutes, "provideResourcesFirst", { "first": true });
 
-cmpTrigger.RegisterTrigger("OnOwnershipChanged", "DefeatPlayerOnceCCIsDestroyed", { "enabled": true });
+cmpTrigger.RegisterTrigger("OnOwnershipChanged", "defeatPlayerOnceCCIsDestroyed", { "enabled": true });
Index: binaries/data/mods/public/simulation/helpers/Random.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Random.js	(revision 18600)
+++ binaries/data/mods/public/simulation/helpers/Random.js	(working copy)
@@ -1,4 +1,14 @@
 /**
+ * Returns a random real in given interval.
+ */
+function RandomReal(interval)
+{
+	let min = Math.min(interval[0], interval[1]);
+	let max = Math.max(interval[0], interval[1]);
+	return min + (max - min) * Math.random();
+}
+
+/**
  * Returns a random integer from min (inclusive) to max (exclusive)
  */
 function RandomInt(min, max)
@@ -6,4 +16,5 @@
 	return Math.floor(min + Math.random() * (max-min));
 }
 
+Engine.RegisterGlobal("RandomReal", RandomReal);
 Engine.RegisterGlobal("RandomInt", RandomInt);
