Index: binaries/data/mods/public/gui/gamesetup/gamesetup.js
===================================================================
--- binaries/data/mods/public/gui/gamesetup/gamesetup.js	(revision 13151)
+++ binaries/data/mods/public/gui/gamesetup/gamesetup.js	(working copy)
@@ -15,6 +15,16 @@
 const STARTING_RESOURCES_DEFAULTIDX = 1;
 // Max number of players for any map
 const MAX_PLAYERS = 8;
+// The default upper player limit for random maps by map size:
+// [Tiny, Small, Medium, Normal, Large, Very Large, Giant]
+// Ignored if explicitely defined in the RMS json
+// Still capped at MAX_PLAYERS if excessing it in getPlayersCapRMS().
+const MAX_PLAYERS_BY_MAP_SIZE_RMS = [4, 6, 8, 8, 8, 8, 8];
+// The default upper player limit for random maps
+// Ignored if explicitely defined in the RMS json
+const MIN_PLAYERS_RMS = 1;
+// The default number of player for random maps
+const DEFAULT_NUM_PLAYERS_RMS = 2;
 
 ////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -86,12 +96,12 @@
 {
 	// Load AI list and hide deprecated AIs
 	g_AIs = Engine.GetAIs();
-
+	
 	// Sort AIs by displayed name
 	g_AIs.sort(function (a, b) {
 		return a.data.name < b.data.name ? -1 : b.data.name < a.data.name ? +1 : 0;
 	});
-
+	
 	// Get default player data - remove gaia
 	g_DefaultPlayerData = initPlayerDefaults();
 	g_DefaultPlayerData.shift();
@@ -99,43 +109,43 @@
 		g_DefaultPlayerData[i].Civ = "random";
 	
 	g_MapSizes = initMapSizes();
-
+	
 	// Init civs
 	initCivNameList();
-
+	
 	// Init map types
 	var mapTypes = getGUIObjectByName("mapTypeSelection");
 	mapTypes.list = ["Scenario","Random"];
 	mapTypes.list_data = ["scenario","random"];
-
+	
 	// Setup map filters - will appear in order they are added
 	addFilter("Default", function(settings) { return settings && !keywordTestOR(settings.Keywords, ["naval", "demo", "hidden"]); });
 	addFilter("Naval Maps", function(settings) { return settings && keywordTestAND(settings.Keywords, ["naval"]); });
 	addFilter("Demo Maps", function(settings) { return settings && keywordTestAND(settings.Keywords, ["demo"]); });
 	addFilter("Old Maps", function(settings) { return !settings; });
 	addFilter("All Maps", function(settings) { return true; });
-
+	
 	// Populate map filters dropdown
 	var mapFilters = getGUIObjectByName("mapFilterSelection");
 	mapFilters.list = getFilters();
 	g_GameAttributes.mapFilter = "Default";
-
+	
 	// Setup controls for host only
 	if (g_IsController)
 	{
 		mapTypes.selected = 0;
 		mapFilters.selected = 0;
-
+		
 		initMapNameList();
-
+		
 		var numPlayersSelection = getGUIObjectByName("numPlayersSelection");
 		var players = [];
 		for (var i = 1; i <= MAX_PLAYERS; ++i)
 			players.push(i);
 		numPlayersSelection.list = players;
 		numPlayersSelection.list_data = players;
-		numPlayersSelection.selected = MAX_PLAYERS - 1;
-
+		numPlayersSelection.selected = DEFAULT_NUM_PLAYERS_RMS - players[0];
+		
 		var populationCaps = getGUIObjectByName("populationCap");
 		populationCaps.list = POPULATION_CAP;
 		populationCaps.list_data = POPULATION_CAP_DATA;
@@ -179,14 +189,14 @@
 			{
 				g_GameAttributes.settings.GameType = VICTORY_DATA[this.selected];
 			}
-
+			
 			if (!g_IsInGuiUpdate)
 			{
 				updateGameAttributes();
 			}
 		};
 		victoryConditions.selected = VICTORY_DEFAULTIDX;
-
+		
 		var mapSize = getGUIObjectByName("mapSize");
 		mapSize.list = g_MapSizes.names;
 		mapSize.list_data = g_MapSizes.tiles;
@@ -196,27 +206,28 @@
 			{
 				g_GameAttributes.settings.Size = g_MapSizes.tiles[this.selected];
 			}
-
+			changeNumPlayersCaps();
+			
 			if (!g_IsInGuiUpdate)
 			{
 				updateGameAttributes();
 			}
 		};
-
+		
 		getGUIObjectByName("revealMap").onPress = function()
 		{	// Update attributes so other players can see change
 			g_GameAttributes.settings.RevealMap = this.checked;
-
+			
 			if (!g_IsInGuiUpdate)
 			{
 				updateGameAttributes();
 			}
 		};
-
+		
 		getGUIObjectByName("lockTeams").onPress = function()
 		{	// Update attributes so other players can see change
 			g_GameAttributes.settings.LockTeams = this.checked;
-
+			
 			if (!g_IsInGuiUpdate)
 			{
 				updateGameAttributes();
@@ -226,7 +237,7 @@
 		getGUIObjectByName("enableCheats").onPress = function()
 		{	// Update attributes so other players can see change
 			g_GameAttributes.settings.CheatsEnabled = this.checked;
-
+			
 			if (!g_IsInGuiUpdate)
 			{
 				updateGameAttributes();
@@ -254,10 +265,10 @@
 			getGUIObjectByName("playerCiv["+i+"]").hidden = true;
 			getGUIObjectByName("playerTeam["+i+"]").hidden = true;
 		}
-
+		
 		getGUIObjectByName("numPlayersSelection").hidden = true;
 	}
-
+	
 	// Set up multiplayer/singleplayer bits:
 	if (!g_IsNetworked)
 	{
@@ -279,7 +290,7 @@
 			getGUIObjectByName("enableCheatsText").hidden = false;
 		}
 	}
-
+	
 	// Settings for all possible player slots
 	var boxSpacing = 32;
 	for (var i = 0; i < MAX_PLAYERS; ++i)
@@ -291,13 +302,13 @@
 		boxSize.top = i * boxSpacing;
 		boxSize.bottom = i * boxSpacing + h;
 		box.size = boxSize;
-
+		
 		// Populate team dropdowns
 		var team = getGUIObjectByName("playerTeam["+i+"]");
 		team.list = ["None", "1", "2", "3", "4"];
 		team.list_data = [-1, 0, 1, 2, 3];
 		team.selected = 0;
-
+		
 		let playerSlot = i;	// declare for inner function use
 		team.onSelectionChange = function()
 		{	// Update team
@@ -305,14 +316,14 @@
 			{
 				g_GameAttributes.settings.PlayerData[playerSlot].Team = this.selected - 1;
 			}
-
+			
 			if (!g_IsInGuiUpdate)
 			{
 				updateGameAttributes();
 			}
 		};
 		
-
+		
 		// Set events
 		var civ = getGUIObjectByName("playerCiv["+i+"]");
 		civ.onSelectionChange = function()
@@ -321,14 +332,14 @@
 			{
 				g_GameAttributes.settings.PlayerData[playerSlot].Civ = this.list_data[this.selected];
 			}
-
+			
 			if (!g_IsInGuiUpdate)
 			{
 				updateGameAttributes();
 			}
 		};
 	}
-
+	
 	if (g_IsNetworked)
 	{
 		// For multiplayer, focus the chat input box by default
@@ -345,7 +356,7 @@
 function handleNetMessage(message)
 {
 	log("Net message: "+uneval(message));
-
+	
 	switch (message.type)
 	{
 	case "netstatus":
@@ -356,13 +367,13 @@
 			Engine.PopGuiPage();
 			reportDisconnect(message.reason);
 			break;
-
+		
 		default:
 			error("Unrecognised netstatus type "+message.status);
 			break;
 		}
 		break;
-
+	
 	case "gamesetup":
 		if (message.data) // (the host gets undefined data on first connect, so skip that)
 		{
@@ -371,7 +382,7 @@
 
 		onGameAttributesChange();
 		break;
-
+	
 	case "players":
 		// Find and report all joinings/leavings
 		for (var host in message.hosts)
@@ -392,7 +403,7 @@
 		g_PlayerAssignments = message.hosts;
 		updatePlayerList();
 		break;
-
+	
 	case "start":
 		Engine.SwitchGuiPage("page_loading.xml", {
 			"attribs": g_GameAttributes,
@@ -400,11 +411,11 @@
 			"playerAssignments": g_PlayerAssignments
 		});
 		break;
-
+	
 	case "chat":
 		addChatMessage({ "type": "message", "guid": message.guid, "text": message.text });
 		break;
-
+	
 	default:
 		error("Unrecognised net message type "+message.type);
 	}
@@ -414,13 +425,13 @@
 function getMapDisplayName(map)
 {
 	var mapData = loadMapData(map);
-
+	
 	if (!mapData || !mapData.settings || !mapData.settings.Name)
 	{	// Give some msg that map format is unsupported
 		log("Map data missing in scenario '"+map+"' - likely unsupported format");
 		return map;
 	}
-
+	
 	return mapData.settings.Name;
 }
 
@@ -428,12 +439,12 @@
 function getMapPreview(map)
 {
 	var mapData = loadMapData(map);
-
+	
 	if (!mapData || !mapData.settings || !mapData.settings.Preview)
 	{	// Give some msg that map format is unsupported
 		return "nopreview.png";
 	}
-
+	
 	return mapData.settings.Preview;
 }
 
@@ -444,16 +455,92 @@
 	{
 		return settings[property];
 	}
-
+	
 	// Use defaults
 	if (defaults && (property in defaults))
 	{
 		return defaults[property];
 	}
-
+	
 	return undefined;
 }
 
+// Get the possible number of players for this random map ny map size
+function getPlayersCapRMS(mapName, mapSize, maxPlayersByMapSizeRMS, minPlayersRMS)
+{
+	// Get map name
+	mapName = (mapName || g_GameAttributes.map);
+	// Get map size
+	if (mapSize || g_GameAttributes.settings.Size)
+		mapSize = (mapSize || g_GameAttributes.settings.Size);
+	else
+	{
+		// Only a log to not show up at gui initialization
+		log("getPlayersCapRMS: g_GameAttributes.settings.Size is undefined! Returning MAX_PLAYERS = " + MAX_PLAYERS);
+		return {"min" : MIN_PLAYERS_RMS, "max" : MAX_PLAYERS};
+	}
+	// Get maximum numbers of players by map size array
+	if (g_MapData[mapName] && g_MapData[mapName].settings && g_MapData[mapName].settings.MaximumPlayersByMapSize)
+		maxPlayersByMapSizeRMS = (maxPlayersByMapSizeRMS || g_MapData[mapName].settings.MaximumPlayersByMapSize);
+	else
+		maxPlayersByMapSizeRMS = (maxPlayersByMapSizeRMS || MAX_PLAYERS_BY_MAP_SIZE_RMS);
+	
+	// Get minimum numbers of players by map size
+	if (g_MapData[mapName] && g_MapData[mapName].settings && g_MapData[mapName].settings.MinimumPlayers)
+		minPlayersRMS = (minPlayersRMS || g_MapData[mapName].settings.MinimumPlayers);
+	else
+		minPlayersRMS = (minPlayersRMS || MIN_PLAYERS_RMS);
+	
+	// Setting upper player cap
+	var maxPlayers = Math.min(maxPlayersByMapSizeRMS[Math.round(mapSize/64 - 2)], MAX_PLAYERS);
+	if (maxPlayers < 2)
+	{
+		warn("getPlayersCapRMS: Maximum number of players should at least be 2 but is " + maxPlayers + "! Setting it to 2.");
+		maxPlayers = 2;
+	}
+	
+	// Setting lower players cap
+	var minPlayers = minPlayersRMS;
+	if (minPlayers > maxPlayers)
+	{
+		warn("getPlayersCapRMS: minPlayers > maxPlayers! Lowering minPlayers (" + minPlayers + ") to maxPlayers (" + maxPlayers + ").");
+		minPlayers = maxPlayers;
+	}
+	
+	return {"min" : minPlayers, "max" : maxPlayers};
+}
+
+// Change player number combobox content and update number of players combobox
+function changeNumPlayersCaps(playerCaps)
+{
+	playerCaps = (playerCaps || getPlayersCapRMS());
+	var numPlayersSelection = getGUIObjectByName("numPlayersSelection");
+	var selectedNumPlayers = parseInt(numPlayersSelection.list_data[numPlayersSelection.selected]);
+	var pList = [];
+	for (var i = playerCaps.min; i <= playerCaps.max; ++i)
+		pList.push(i);
+	numPlayersSelection.list = pList;
+	numPlayersSelection.list_data = pList;
+	if (!selectedNumPlayers)
+	{
+		if (pList.indexOf(DEFAULT_NUM_PLAYERS_RMS) !== -1)
+			numPlayersSelection.selected = pList.indexOf(DEFAULT_NUM_PLAYERS_RMS);
+		else if (DEFAULT_NUM_PLAYERS_RMS > pList[pList.length - 1])
+			numPlayersSelection.selected = pList.length - 1;
+		else
+			numPlayersSelection.selected = 0;
+	}
+	else if (pList.indexOf(selectedNumPlayers) !== -1)
+		numPlayersSelection.selected = pList.indexOf(selectedNumPlayers);
+	else if (selectedNumPlayers < pList[0])
+		numPlayersSelection.selected = 0;
+	else if (selectedNumPlayers > pList[pList.length - 1])
+		numPlayersSelection.selected = pList.length - 1;
+	else
+		warn("changeNumPlayersCaps: Selected number of players could not be set properly on player cap change!");
+	selectNumPlayers(pList[numPlayersSelection.selected]);
+}
+
 // Initialize the dropdowns containing all the available civs
 function initCivNameList()
 {
@@ -470,10 +557,10 @@
 	
 	// Alphabetically sort the list, ignoring case
 	civList.sort(sortNameIgnoreCase);
-
+	
 	var civListNames = [ civ.name for each (civ in civList) ];
 	var civListCodes = [ civ.code for each (civ in civList) ];
-
+	
 	//  Add random civ to beginning of list 
 	civListNames.unshift("[color=\"orange\"]Random"); 
 	civListCodes.unshift("random"); 
@@ -495,22 +582,22 @@
 	// TODO: Should verify these are valid maps before adding to list
 	var mapSelectionBox = getGUIObjectByName("mapSelection")
 	var mapFiles;
-
+	
 	switch (g_GameAttributes.mapType)
 	{
 	case "scenario":
 		mapFiles = getXMLFileList(g_GameAttributes.mapPath);
 		break;
-
+	
 	case "random":
 		mapFiles = getJSONFileList(g_GameAttributes.mapPath);
 		break;
-
+	
 	default:
 		error("initMapNameList: Unexpected map type '"+g_GameAttributes.mapType+"'");
 		return;
 	}
-
+	
 	// Apply map filter, if any defined
 	var mapList = [];
 	for (var i = 0; i < mapFiles.length; ++i)
@@ -523,7 +610,7 @@
 			mapList.push({ "name": getMapDisplayName(file), "file": file });
 		}
 	}
-
+	
 	// Alphabetically sort the list, ignoring case
 	mapList.sort(sortNameIgnoreCase);
 	if (g_GameAttributes.mapType == "random")
@@ -531,7 +618,7 @@
 	
 	var mapListNames = [ map.name for each (map in mapList) ];
 	var mapListFiles = [ map.file for each (map in mapList) ];
-
+	
 	// Select the default map
 	var selected = mapListFiles.indexOf(g_GameAttributes.map);
 	// Default to the first element if list is not empty and we can't find the one we searched for
@@ -539,7 +626,7 @@
 	{
 		selected = 0;
 	}
-
+	
 	// Update the list control
 	mapSelectionBox.list = mapListNames;
 	mapSelectionBox.list_data = mapListFiles;
@@ -558,7 +645,7 @@
 		g_MapData[name] = {settings : {"Name" : "Random", "Description" : "Randomly selects a map from the list"}};
 		return g_MapData[name];
 	}
-
+	
 	if (!g_MapData[name])
 	{
 		switch (g_GameAttributes.mapType)
@@ -566,17 +653,17 @@
 		case "scenario":
 			g_MapData[name] = Engine.LoadMapSettings(g_GameAttributes.mapPath+name);
 			break;
-
+		
 		case "random":
 			g_MapData[name] = parseJSONData(g_GameAttributes.mapPath+name+".json");
 			break;
-
+		
 		default:
 			error("loadMapData: Unexpected map type '"+g_GameAttributes.mapType+"'");
 			return undefined;
 		}
 	}
-
+	
 	return g_MapData[name];
 }
 
@@ -624,19 +711,19 @@
 	{
 		return;
 	}
-
+	
 	// Network clients can't change number of players
 	if (g_IsNetworked && !g_IsController)
 	{
 		return;
 	}
-
+	
 	// Only meaningful for random maps
 	if (g_GameAttributes.mapType != "random")
 	{
 		return;
 	}
-
+	
 	// Update player data
 	var pData = g_GameAttributes.settings.PlayerData;
 	if (pData && num < pData.length)
@@ -652,7 +739,7 @@
 			g_GameAttributes.settings.PlayerData.push(g_DefaultPlayerData[i]);
 		}
 	}
-
+	
 	// Some players may have lost their assigned slot
 	for (var guid in g_PlayerAssignments)
 	{
@@ -665,7 +752,7 @@
 				g_PlayerAssignments = { "local": { "name": "You", "player": 1, "civ": "", "team": -1} };
 		}
 	}
-
+	
 	updateGameAttributes();
 }
 
@@ -677,20 +764,20 @@
 	{
 		return;
 	}
-
+	
 	// Network clients can't change map type
 	if (g_IsNetworked && !g_IsController)
 	{
 		return;
 	}
-
+	
 	// Reset game attributes
 	g_GameAttributes.map = "";
 	g_GameAttributes.mapType = type;
-
+	
 	// Clear old map data
 	g_MapData = {};
-
+	
 	// Select correct path
 	switch (g_GameAttributes.mapType)
 	{
@@ -700,24 +787,27 @@
 		g_GameAttributes.map = (g_IsNetworked ? DEFAULT_NETWORKED_MAP : DEFAULT_OFFLINE_MAP);
 		g_GameAttributes.mapPath = "maps/scenarios/";
 		break;
-
+	
 	case "random":
+		var playerCaps = getPlayersCapRMS();
 		g_GameAttributes.mapPath = "maps/random/";
 		g_GameAttributes.settings = {
-			PlayerData: g_DefaultPlayerData.slice(0, 4),
+			PlayerData: g_DefaultPlayerData.slice(0, Math.max(Math.min(DEFAULT_NUM_PLAYERS_RMS, playerCaps.max), playerCaps.min)),
 			Seed: Math.floor(Math.random() * 65536),
 			CheatsEnabled: g_GameAttributes.settings.CheatsEnabled
 		};
 		break;
-
+	
 	default:
 		error("selectMapType: Unexpected map type '"+g_GameAttributes.mapType+"'");
 		return;
 	}
-
+	
 	initMapNameList();
-
+	
 	updateGameAttributes();
+	
+	changeNumPlayersCaps();
 }
 
 function selectMapFilter(filterName)
@@ -727,17 +817,17 @@
 	{
 		return;
 	}
-
+	
 	// Network clients can't change map filter
 	if (g_IsNetworked && !g_IsController)
 	{
 		return;
 	}
-
+	
 	g_GameAttributes.mapFilter = filterName;
-
+	
 	initMapNameList();
-
+	
 	updateGameAttributes();
 }
 
@@ -749,22 +839,22 @@
 	{
 		return;
 	}
-
+	
 	// Network clients can't change map
 	if (g_IsNetworked && !g_IsController)
 	{
 		return;
 	}
-
+	
 	// Return if we have no map
 	if (!name)
 	{
 		return;
 	}
-
+	
 	var mapData = loadMapData(name);
 	var mapSettings = (mapData && mapData.settings ? deepcopy(mapData.settings) : {});
-
+	
 	// Copy any new settings
 	g_GameAttributes.map = name;
 	g_GameAttributes.script = mapSettings.Script;
@@ -800,8 +890,10 @@
 			}
 		}
 	}
-
+	
 	updateGameAttributes();
+	
+	changeNumPlayersCaps();
 }
 
 function launchGame()
@@ -811,7 +903,7 @@
 		error("Only host can start game");
 		return;
 	}
-
+	
 	// Check that we have a map
 	if (!g_GameAttributes.map)
 	{
@@ -821,7 +913,7 @@
 	if (g_GameAttributes.map == "random")
 		selectMap(getGUIObjectByName("mapSelection").list_data[Math.floor(Math.random() *
 			(getGUIObjectByName("mapSelection").list.length - 1)) + 1]);
-
+	
 	g_GameAttributes.settings.mapType = g_GameAttributes.mapType;
 	var numPlayers = g_GameAttributes.settings.PlayerData.length; 
 	// Assign random civilizations to players with that choice 
@@ -894,7 +986,7 @@
 		}
 		// Remove extra player data
 		g_GameAttributes.settings.PlayerData = g_GameAttributes.settings.PlayerData.slice(0, numPlayers);
-
+		
 		Engine.StartGame(g_GameAttributes, playerID);
 		Engine.SwitchGuiPage("page_loading.xml", {
 			"attribs": g_GameAttributes,
@@ -909,13 +1001,13 @@
 function onGameAttributesChange()
 {
 	g_IsInGuiUpdate = true;
-
+	
 	// Don't set any attributes here, just show the changes in GUI
-
+	
 	var mapName = g_GameAttributes.map || "";
 	var mapSettings = g_GameAttributes.settings;
 	var numPlayers = (mapSettings.PlayerData ? mapSettings.PlayerData.length : MAX_PLAYERS);
-
+	
 	// Update some controls for clients
 	if (!g_IsController)
 	{
@@ -932,9 +1024,10 @@
 		startingResourcesBox.selected = startingResourcesBox.list_data.indexOf(mapSettings.StartingResources);
 		initMapNameList();
 	}
-
+	
 	// Controls common to all map types
 	var numPlayersSelection = getGUIObjectByName("numPlayersSelection");
+	var minNumPlayers = numPlayersSelection.list_data[0];
 	var revealMap = getGUIObjectByName("revealMap");
 	var victoryCondition = getGUIObjectByName("victoryCondition");
 	var lockTeams = getGUIObjectByName("lockTeams");
@@ -966,7 +1059,7 @@
 	case "random":
 		if (g_IsController)
 		{	//Host
-			numPlayersSelection.selected = numPlayers - 1;
+			numPlayersSelection.selected = numPlayers - minNumPlayers;
 			numPlayersSelection.hidden = false;
 			mapSize.hidden = false;
 			revealMap.hidden = false;
@@ -1017,9 +1110,9 @@
 			victoryConditionText.caption = VICTORY_TEXT[victoryIdx];
 			lockTeamsText.caption = (mapSettings.LockTeams ? "Yes" : "No");
 		}
-
+		
 		break;
-
+	
 	case "scenario":
 		// For scenario just reflect settings for the current map
 		numPlayersSelection.hidden = true;
@@ -1045,31 +1138,44 @@
 		victoryConditionText.caption = VICTORY_TEXT[victoryIdx];
 		lockTeamsText.caption = (mapSettings.LockTeams ? "Yes" : "No");
 		getGUIObjectByName("populationCap").selected = POPULATION_CAP_DEFAULTIDX;
-
+		
 		break;
-
+	
 	default:
 		error("onGameAttributesChange: Unexpected map type '"+g_GameAttributes.mapType+"'");
 		return;
 	}
-
+	
 	// Display map name
 	getGUIObjectByName("mapInfoName").caption = getMapDisplayName(mapName);
 	
 	// Load the description from the map file, if there is one
 	var description = mapSettings.Description || "Sorry, no description available.";
-
+	// Adding player caps to the map description
+	if (g_GameAttributes.mapType == "random")
+	{
+		if (g_MapData[mapName] && g_MapData[mapName].settings && g_MapData[mapName].settings.MinimumPlayers)
+			description += "\n\nMinimum number of players: " + g_MapData[mapName].settings.MinimumPlayers;
+		else
+			description += "\n";
+		description += "\nMaximum numbers of players (by map size):";
+		if (g_MapData[mapName] && g_MapData[mapName].settings && g_MapData[mapName].settings.MaximumPlayersByMapSize)
+			description += "\nTiny: " + g_MapData[mapName].settings.MaximumPlayersByMapSize.toString().replace(/,/g, "-").replace("-", ", Small: ").replace("-", ", Medium: ").replace("-", ", Normal: ").replace("-", ", Large: ").replace("-", ", Very Large: ").replace("-", ", Giant: ");
+		else
+			description += "\nTiny: " + MAX_PLAYERS_BY_MAP_SIZE_RMS.toString().replace(/,/g, "-").replace("-", ", Small: ").replace("-", ", Medium: ").replace("-", ", Normal: ").replace("-", ", Large: ").replace("-", ", Very Large: ").replace("-", ", Giant: ");
+	}
+	// Add naval warning to the map description
 	if (g_GameAttributes.mapFilter == "Naval Maps")
 		description += g_NavalWarning;
-
+	
 	// Describe the number of players
 	var playerString = numPlayers + " " + (numPlayers == 1 ? "player" : "players") + ". ";
-
+	
 	for (var i = 0; i < MAX_PLAYERS; ++i)
 	{
 		// Show only needed player slots
 		getGUIObjectByName("playerBox["+i+"]").hidden = (i >= numPlayers);
-
+		
 		// Show player data or defaults as necessary
 		if (i < numPlayers)
 		{
@@ -1083,12 +1189,12 @@
 			// Player data / defaults
 			var pData = mapSettings.PlayerData ? mapSettings.PlayerData[i] : {};
 			var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[i] : {};
-
+			
 			// Common to all game types
 			var color = iColorToString(getSetting(pData, pDefs, "Colour"));
 			pColor.sprite = "colour:"+color+" 100";
 			pName.caption = getSetting(pData, pDefs, "Name");
-
+			
 			var team = getSetting(pData, pDefs, "Team");
 			var civ = getSetting(pData, pDefs, "Civ");
 			
@@ -1124,9 +1230,9 @@
 	}
 
 	getGUIObjectByName("mapInfoDescription").caption = playerString + description;
-
+	
 	g_IsInGuiUpdate = false;
-
+	
 	// Game attributes include AI settings, so update the player list
 	updatePlayerList();
 }
@@ -1146,32 +1252,32 @@
 function updatePlayerList()
 {
 	g_IsInGuiUpdate = true;
-
+	
 	var hostNameList = [];
 	var hostGuidList = [];
 	var assignments = [];
 	var aiAssignments = {};
 	var noAssignment;
 	var assignedCount = 0;
-
+	
 	for (var guid in g_PlayerAssignments)
 	{
 		var name = g_PlayerAssignments[guid].name;
 		var hostID = hostNameList.length;
 		var player = g_PlayerAssignments[guid].player;
-
+		
 		hostNameList.push(name);
 		hostGuidList.push(guid);
 		assignments[player] = hostID;
-
+		
 		if (player != 255)
 			assignedCount++;
 	}
-
+	
 	// Only enable start button if we have enough assigned players
 	if (g_IsController)
 		getGUIObjectByName("startGame").enabled = (assignedCount > 0);
-
+	
 	for each (var ai in g_AIs)
 	{
 		if (ai.data.hidden) 
@@ -1195,21 +1301,21 @@
 		hostNameList.push("[color=\"70 150 70 255\"]AI: " + ai.data.name);
 		hostGuidList.push("ai:" + ai.id);
 	}
-
+	
 	noAssignment = hostNameList.length;
 	hostNameList.push("[color=\"140 140 140 255\"]Unassigned");
 	hostGuidList.push("");
-
+	
 	for (var i = 0; i < MAX_PLAYERS; ++i)
 	{
 		let playerSlot = i;
 		let playerID = i+1; // we don't show Gaia, so first slot is ID 1
-
+		
 		var selection = assignments[playerID];
-
+		
 		var configButton = getGUIObjectByName("playerConfig["+i+"]");
 		configButton.hidden = true;
-
+		
 		// Look for valid player slots
 		if (playerSlot < g_GameAttributes.settings.PlayerData.length)
 		{
@@ -1225,10 +1331,10 @@
 					else
 						warn("AI \""+aiId+"\" not present. Defaulting to unassigned.");
 				}
-
+				
 				if (!selection)
 					selection = noAssignment;
-
+				
 				// Since no human is assigned, show the AI config button
 				if (g_IsController)
 				{
@@ -1240,7 +1346,7 @@
 							id: g_GameAttributes.settings.PlayerData[playerSlot].AI,
 							callback: function(ai) {
 								g_GameAttributes.settings.PlayerData[playerSlot].AI = ai.id;
-
+								
 								if (g_IsNetworked)
 								{
 									Engine.SetNetworkGameAttributes(g_GameAttributes);
@@ -1267,7 +1373,7 @@
 					}
 				}
 			}
-
+			
 			var assignBox = getGUIObjectByName("playerAssignment["+i+"]");
 			assignBox.list = hostNameList;
 			assignBox.list_data = hostGuidList;
@@ -1275,7 +1381,7 @@
 			{
 				assignBox.selected = selection;
 			}
-
+			
 			if (g_IsNetworked && g_IsController)
 			{
 				assignBox.onselectionchange = function ()
@@ -1299,7 +1405,7 @@
 						}
 						else
 							swapPlayers(guid, playerSlot);
-
+						
 						Engine.SetNetworkGameAttributes(g_GameAttributes);
 					}
 				};
@@ -1323,14 +1429,14 @@
 						}
 						else
 							swapPlayers(guid, playerSlot);
-
+						
 						updatePlayerList();
 					}
 				};
 			}
 		}
 	}
-
+	
 	g_IsInGuiUpdate = false;
 }
 
@@ -1339,7 +1445,7 @@
 	// Player slots are indexed from 0 as Gaia is omitted.
 	var newPlayerID = newSlot + 1;
 	var playerID = g_PlayerAssignments[guid].player;
-
+	
 	// Attempt to swap the player or AI occupying the target slot,
 	// if any, into the slot this player is currently in.
 	if (playerID != 255)
@@ -1356,16 +1462,16 @@
 				break;
 			}
 		}
-
+		
 		// Transfer the AI from the target slot to the current slot.
 		g_GameAttributes.settings.PlayerData[playerID - 1].AI = g_GameAttributes.settings.PlayerData[newSlot].AI;
 	}
-
+	
 	if (g_IsNetworked)
 		Engine.AssignNetworkPlayer(newPlayerID, guid);
 	else
 		g_PlayerAssignments[guid].player = newPlayerID;
-
+	
 	// Remove AI from this player slot
 	g_GameAttributes.settings.PlayerData[newSlot].AI = "";
 }
@@ -1385,10 +1491,10 @@
 {
 	var username = escapeText(msg.username || g_PlayerAssignments[msg.guid].name);
 	var message = escapeText(msg.text);
-
+	
 	// TODO: Maybe host should have distinct font/color?
 	var color = "white";
-
+	
 	if (g_PlayerAssignments[msg.guid] && g_PlayerAssignments[msg.guid].player != 255)
 	{	// Valid player who has been assigned - get player colour
 		var player = g_PlayerAssignments[msg.guid].player - 1;
@@ -1397,32 +1503,32 @@
 		var mapSettings = (mapData && mapData.settings ? mapData.settings : {});
 		var pData = mapSettings.PlayerData ? mapSettings.PlayerData[player] : {};
 		var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[player] : {};
-
+		
 		color = iColorToString(getSetting(pData, pDefs, "Colour"));
 	}
-
+	
 	var formatted;
 	switch (msg.type)
 	{
 	case "connect":
 		formatted = '[font="serif-bold-13"][color="'+ color +'"]' + username + '[/color][/font] [color="gold"]has joined[/color]';
 		break;
-
+	
 	case "disconnect":
 		formatted = '[font="serif-bold-13"][color="'+ color +'"]' + username + '[/color][/font] [color="gold"]has left[/color]';
 		break;
-
+	
 	case "message":
 		formatted = '[font="serif-bold-13"]<[color="'+ color +'"]' + username + '[/color]>[/font] ' + message;
 		break;
-
+	
 	default:
 		error("Invalid chat message '" + uneval(msg) + "'");
 		return;
 	}
-
+	
 	g_ChatMessages.push(formatted);
-
+	
 	getGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
 }
 
@@ -1442,7 +1548,7 @@
 		var newFilter = {};
 		newFilter.name = name;
 		newFilter.filter = filterFunc;
-
+		
 		g_MapFilters.push(newFilter);
 	}
 	else
@@ -1459,7 +1565,7 @@
 	{
 		filters.push(g_MapFilters[i].name);
 	}
-
+	
 	return filters;
 }
 
@@ -1473,7 +1579,7 @@
 			return g_MapFilters[i].filter(mapSettings);
 		}
 	}
-
+	
 	error("Invalid map filter: "+name);
 	return false;
 }
@@ -1485,7 +1591,7 @@
 	{
 		return false;
 	}
-
+	
 	for (var m = 0; m < matches.length; ++m)
 	{	// Fail on not match
 		if (keywords.indexOf(matches[m]) == -1)
@@ -1503,7 +1609,7 @@
 	{
 		return false;
 	}
-
+	
 	for (var m = 0; m < matches.length; ++m)
 	{	// Success on match
 		if (keywords.indexOf(matches[m]) != -1)
Index: binaries/data/mods/public/maps/random/archipelago.json
===================================================================
--- binaries/data/mods/public/maps/random/archipelago.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/archipelago.json	(working copy)
@@ -8,6 +8,7 @@
 		"Keywords": ["naval"],
 		"Preview" : "archipelago.png",
 		"CircularMap" : true,
+		"MaximumPlayersByMapSize" : [3, 6, 8, 8, 8, 8, 8],
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
Index: binaries/data/mods/public/maps/random/belgian_uplands.json
===================================================================
--- binaries/data/mods/public/maps/random/belgian_uplands.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/belgian_uplands.json	(working copy)
@@ -2,11 +2,12 @@
 	"settings" : {
 		"Name" : "Belgian Uplands",
 		"Script" : "belgian_uplands.js",
+		"Preview" : "belgian_uplands.png",
 		"Description" : "An experimental map with its hightmap generated by erosion to look more natural. Not all seeds will be fair though! Tiny maps with 8 players may take a while to generate.",
 		"CircularMap" : false,
+		"MaximumPlayersByMapSize" : [2, 4, 6, 8, 8, 8, 8],
 		"BaseTerrain" : ["temp_grass", "temp_grass_b", "temp_grass_c", "temp_grass_d", "temp_grass_long_b", "temp_grass_clovers_2", "temp_grass_mossy", "temp_grass_plants"],
 		"BaseHeight" : 0,
-		"Preview" : "belgian_uplands.png",
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
Index: binaries/data/mods/public/maps/random/canyon.json
===================================================================
--- binaries/data/mods/public/maps/random/canyon.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/canyon.json	(working copy)
@@ -7,6 +7,8 @@
 		"BaseHeight" : 30,
 		"Preview" : "canyon.png",
 		"CircularMap" : true,
+		"MinimumPlayers" : 2,
+		"MaximumPlayersByMapSize" : [3, 6, 8, 8, 8, 8, 8],
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
\ No newline at end of file
Index: binaries/data/mods/public/maps/random/corsica.json
===================================================================
--- binaries/data/mods/public/maps/random/corsica.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/corsica.json	(working copy)
@@ -8,6 +8,7 @@
 		"Keywords": ["naval"],
 		"Preview" : "corsica.png",
 		"CircularMap" : false,
+		"MaximumPlayersByMapSize" : [2, 4, 6, 8, 8, 8, 8],
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
\ No newline at end of file
Index: binaries/data/mods/public/maps/random/deep_forest.json
===================================================================
--- binaries/data/mods/public/maps/random/deep_forest.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/deep_forest.json	(working copy)
@@ -7,6 +7,7 @@
 		"BaseHeight" : 0,
 		"Preview" : "deep_forest.png",
 		"CircularMap" : true,
+		"MaximumPlayersByMapSize" : [3, 6, 8, 8, 8, 8, 8],
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
Index: binaries/data/mods/public/maps/random/guadalquivir_river.js
===================================================================
--- binaries/data/mods/public/maps/random/guadalquivir_river.js	(revision 13151)
+++ binaries/data/mods/public/maps/random/guadalquivir_river.js	(working copy)
@@ -142,7 +142,7 @@
 	createArea(placer, painter, null);
 	
 	// create starting units
-	placeCivDefaultEntities(fx, fz, id, BUILDING_ANGlE);
+	placeCivDefaultEntities(fx, fz, id, BUILDING_ANGlE, (mapSize/64 - 3 < 1) ? {"iberWall" : false} : undefined);
 	
 	// create animals
 	for (var j = 0; j < 2; ++j)
Index: binaries/data/mods/public/maps/random/guadalquivir_river.json
===================================================================
--- binaries/data/mods/public/maps/random/guadalquivir_river.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/guadalquivir_river.json	(working copy)
@@ -7,6 +7,7 @@
 		"BaseHeight" : -5,
 		"Preview" : "guadalquivir_river.png",
 		"CircularMap" : true,
+		"MaximumPlayersByMapSize" : [2, 4, 6, 6, 8, 8, 8],
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
Index: binaries/data/mods/public/maps/random/gulf_of_bothnia.json
===================================================================
--- binaries/data/mods/public/maps/random/gulf_of_bothnia.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/gulf_of_bothnia.json	(working copy)
@@ -7,6 +7,7 @@
 		"BaseHeight" : 3,
 		"Preview" : "gulf_of_bothnia.png",
 		"CircularMap" : true,
+		"MinimumPlayers" : 2,
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
\ No newline at end of file
Index: binaries/data/mods/public/maps/random/islands.json
===================================================================
--- binaries/data/mods/public/maps/random/islands.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/islands.json	(working copy)
@@ -8,6 +8,7 @@
 		"Keywords": ["naval"],
 		"Preview" : "islands.png",
 		"CircularMap" : true,
+		"MaximumPlayersByMapSize" : [2, 4, 6, 8, 8, 8, 8],
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
Index: binaries/data/mods/public/maps/random/migration.json
===================================================================
--- binaries/data/mods/public/maps/random/migration.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/migration.json	(working copy)
@@ -8,6 +8,7 @@
 		"Keywords": ["naval"],
 		"Preview" : "migration.png",
 		"CircularMap" : true,
+		"MaximumPlayersByMapSize" : [2, 3, 5, 5, 6, 7, 8],
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
Index: binaries/data/mods/public/maps/random/pheonician_levant.json
===================================================================
--- binaries/data/mods/public/maps/random/pheonician_levant.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/pheonician_levant.json	(working copy)
@@ -7,6 +7,7 @@
 		"BaseHeight" : 1,
 		"Preview" : "phoenician_levant.png",
 		"CircularMap" : false,
+		"MaximumPlayersByMapSize" : [4, 4, 8, 8, 8, 8, 8],
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
\ No newline at end of file
Index: binaries/data/mods/public/maps/random/pyrenean_sierra.js
===================================================================
--- binaries/data/mods/public/maps/random/pyrenean_sierra.js	(revision 13151)
+++ binaries/data/mods/public/maps/random/pyrenean_sierra.js	(working copy)
@@ -351,7 +351,7 @@
 	createArea(placer, painter, null);
 	
 	// create starting units
-	placeCivDefaultEntities(fx, fz, id, BUILDING_ANGlE);
+	placeCivDefaultEntities(fx, fz, id, BUILDING_ANGlE, (round(mapSize/64) - 2 == 1) ? {"iberWall" : "towers"} : undefined);
 	
 	// create animals
 	for (var j = 0; j < 2; ++j)
Index: binaries/data/mods/public/maps/random/pyrenean_sierra.json
===================================================================
--- binaries/data/mods/public/maps/random/pyrenean_sierra.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/pyrenean_sierra.json	(working copy)
@@ -7,6 +7,7 @@
 		"BaseHeight" : -100,
 		"Preview" : "pyrenean_sierra.png",
 		"CircularMap" : true,
+		"MinimumPlayers" : 2,
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
\ No newline at end of file
Index: binaries/data/mods/public/maps/random/snowflake_searocks.json
===================================================================
--- binaries/data/mods/public/maps/random/snowflake_searocks.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/snowflake_searocks.json	(working copy)
@@ -7,6 +7,8 @@
 		"BaseHeight" : -5,
 		"Preview" : "snowflake_searocks.png",
 		"CircularMap" : true,
+		"MinimumPlayers" : 2,
+		"MaximumPlayersByMapSize" : [3, 4, 8, 8, 8, 8, 8],
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
Index: binaries/data/mods/public/maps/random/unknown.json
===================================================================
--- binaries/data/mods/public/maps/random/unknown.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/unknown.json	(working copy)
@@ -7,6 +7,8 @@
 		"BaseHeight" : -5,
 		"Preview" : "unknown.png",
 		"CircularMap" : true,
+		"MinimumPlayers" : 2,
+		"MaximumPlayersByMapSize" : [2, 4, 6, 8, 8, 8, 8],
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
Index: binaries/data/mods/public/maps/random/unknown_land.json
===================================================================
--- binaries/data/mods/public/maps/random/unknown_land.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/unknown_land.json	(working copy)
@@ -7,6 +7,8 @@
 		"BaseHeight" : -5,
 		"Preview" : "unknown.png",
 		"CircularMap" : true,
+		"MinimumPlayers" : 2,
+		"MaximumPlayersByMapSize" : [2, 4, 6, 8, 8, 8, 8],
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
Index: binaries/data/mods/public/maps/random/unknown_nomad.json
===================================================================
--- binaries/data/mods/public/maps/random/unknown_nomad.json	(revision 13151)
+++ binaries/data/mods/public/maps/random/unknown_nomad.json	(working copy)
@@ -8,6 +8,8 @@
 		"Preview" : "unknown.png",
 		"Keywords": ["demo"],
 		"CircularMap" : true,
+		"MinimumPlayers" : 2,
+		"MaximumPlayersByMapSize" : [2, 4, 6, 8, 8, 8, 8],
 		"XXXXXX" : "Optionally define other things here, like we would for a scenario"
 	}
 }
