Implements random map system, fixes #6.

Includes default library "rmgen" w/ API based on rmgen tool.
Modifies rmgen scripts Cantabrian Highlands, Neareastern Badlands, and
Latium.
Old map support dropped from MapReader.
Fixes a few bugs in existing game setup and initialization scripts.

This was SVN commit r9096.
This commit is contained in:
historic_bruno 2011-03-22 01:34:45 +00:00
parent 23ebe32b4c
commit 0e0ed94926
46 changed files with 4399 additions and 2163 deletions

View File

@ -1,3 +1,14 @@
////////////////////////////////////////////////////////////////////////////////////////////////
// Constants
// TODO: Move some of these into common location (other scripts may need)
const MAP_SIZES_TEXT = ["Small", "Medium", "Large", "Huge"];
const MAP_SIZES_DATA = [16, 20, 24, 32];
// Max number of players for any map
const MAX_PLAYERS = 8;
////////////////////////////////////////////////////////////////////////////////////////////////
// Is this is a networked game, or offline
var g_IsNetworked;
@ -11,6 +22,7 @@ var g_IsInGuiUpdate;
var g_PlayerAssignments = {};
// Default game setup attributes
var g_DefaultPlayerData = [];
var g_GameAttributes = {
mapType: "",
map: "",
@ -27,12 +39,6 @@ var g_GameAttributes = {
}
}
// Max number of players for any map
var g_MaxPlayers = 8;
// Number of players for currently selected map
var g_NumPlayers = 0;
var g_AIs = [];
var g_ChatMessages = [];
@ -43,12 +49,13 @@ var g_CivData = {};
var g_MapFilters = [];
// To prevent the display locking up while we load the map metadata,
// we'll start with a 'loading' message and switch to the main screen in the
// tick handler
var g_LoadingState = 0; // 0 = not started, 1 = loading, 2 = loaded
////////////////////////////////////////////////////////////////////////////////////////////////
function init(attribs)
{
switch (attribs.type)
@ -75,26 +82,26 @@ function initMain()
{
// Load AI list
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
var pDefs = initPlayerDefaults();
pDefs.shift();
g_DefaultPlayerData = initPlayerDefaults();
g_DefaultPlayerData.shift();
// Build player settings using defaults
g_GameAttributes.settings.PlayerData = pDefs;
g_GameAttributes.settings.PlayerData = g_DefaultPlayerData;
// Init civs
initCivNameList();
// Init map types
var mapTypes = getGUIObjectByName("mapTypeSelection");
mapTypes.list = ["Scenario"]; // TODO: May offer saved game type for multiplayer games?
mapTypes.list_data = ["scenario"];
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, ["demo", "hidden"]); });
@ -106,10 +113,14 @@ function initMain()
var mapFilters = getGUIObjectByName("mapFilterSelection");
mapFilters.list = getFilters();
g_GameAttributes.mapFilter = "Default";
// Setup controls for host only
if (g_IsController)
{
// Generate seed for random maps
// TODO: There's probably a better way to do this
g_GameAttributes.settings.Seed = Math.floor(Math.random() * 65536);
// Set a default map
// TODO: This should be remembered from the last session
if (!g_IsNetworked)
@ -122,46 +133,46 @@ function initMain()
initMapNameList();
var numPlayers = getGUIObjectByName("numPlayersSelection");
var numPlayersSelect = getGUIObjectByName("numPlayersSelection");
var players = [];
for (var i = 1; i <= g_MaxPlayers; ++i)
for (var i = 1; i <= MAX_PLAYERS; ++i)
players.push(i);
numPlayers.list = players;
numPlayers.list_data = players;
numPlayers.selected = g_MaxPlayers - 1;
numPlayersSelect.list = players;
numPlayersSelect.list_data = players;
numPlayersSelect.selected = MAX_PLAYERS - 1;
var victoryConditions = getGUIObjectByName("victoryCondition");
victoryConditions.list = ["Conquest", "None"];
victoryConditions.list_data = ["conquest", "endless"];
victoryConditions.onSelectionChange = function()
{
{ // Update attributes so other players can see change
if (this.selected != -1)
g_GameAttributes.settings.GameType = this.list_data[this.selected];
if (!g_IsInGuiUpdate)
onGameAttributesChange();
updateGameAttributes();
};
victoryConditions.selected = -1;
var mapSize = getGUIObjectByName("mapSize");
mapSize.list = ["Small", "Medium", "Large", "Huge"];
mapSize.list_data = [16, 32, 48, 64];
mapSize.list = MAP_SIZES_TEXT;
mapSize.list_data = MAP_SIZES_DATA;
mapSize.onSelectionChange = function()
{
{ // Update attributes so other players can see change
if (this.selected != -1)
g_GameAttributes.settings.Size = parseInt(this.list_data[this.selected]);
g_GameAttributes.settings.Size = this.list_data[this.selected];
if (!g_IsInGuiUpdate)
onGameAttributesChange();
updateGameAttributes();
};
mapSize.selected = -1;
mapSize.selected = 0;
getGUIObjectByName("revealMap").onPress = function()
{ // Update attributes so other players can see change
g_GameAttributes.settings.RevealMap = this.checked;
if (!g_IsInGuiUpdate)
onGameAttributesChange();
updateGameAttributes();
};
getGUIObjectByName("lockTeams").onPress = function()
@ -169,7 +180,7 @@ function initMain()
g_GameAttributes.settings.LockTeams = this.checked;
if (!g_IsInGuiUpdate)
onGameAttributesChange();
updateGameAttributes();
};
}
else
@ -182,8 +193,12 @@ function initMain()
// Disable player and game options controls
// TODO: Shouldn't players be able to choose their own assignment?
for (var i = 0; i < g_MaxPlayers; ++i)
for (var i = 0; i < MAX_PLAYERS; ++i)
{
getGUIObjectByName("playerAssignment["+i+"]").enabled = false;
getGUIObjectByName("playerCiv["+i+"]").hidden = true;
getGUIObjectByName("playerTeam["+i+"]").hidden = true;
}
getGUIObjectByName("numPlayersBox").hidden = true;
@ -200,7 +215,7 @@ function initMain()
// Settings for all possible player slots
var boxSpacing = 32;
for (var i = 0; i < g_MaxPlayers; ++i)
for (var i = 0; i < MAX_PLAYERS; ++i)
{
// Space player boxes
var box = getGUIObjectByName("playerBox["+i+"]");
@ -223,7 +238,7 @@ function initMain()
g_GameAttributes.settings.PlayerData[playerSlot].Team = this.selected - 1;
if (!g_IsInGuiUpdate)
onGameAttributesChange();
updateGameAttributes();
};
// Set events
@ -234,10 +249,10 @@ function initMain()
g_GameAttributes.settings.PlayerData[playerSlot].Civ = this.list_data[this.selected];
if (!g_IsInGuiUpdate)
onGameAttributesChange();
updateGameAttributes();
};
}
if (g_IsNetworked)
{
// For multiplayer, focus the chat input box by default
@ -251,37 +266,6 @@ function initMain()
}
}
function cancelSetup()
{
Engine.DisconnectNetworkGame();
}
function onTick()
{
// First tick happens before first render, so don't load yet
if (g_LoadingState == 0)
{
g_LoadingState++;
}
else if (g_LoadingState == 1)
{
getGUIObjectByName("loadingWindow").hidden = true;
getGUIObjectByName("setupWindow").hidden = false;
initMain();
g_LoadingState++;
}
else if (g_LoadingState == 2)
{
while (true)
{
var message = Engine.PollNetworkClient();
if (!message)
break;
handleNetMessage(message);
}
}
}
function handleNetMessage(message)
{
log("Net message: "+uneval(message));
@ -296,20 +280,20 @@ function handleNetMessage(message)
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)
g_GameAttributes = message.data;
onGameAttributesChange();
break;
case "players":
// Find and report all joinings/leavings
for (var host in message.hosts)
@ -322,7 +306,7 @@ function handleNetMessage(message)
g_PlayerAssignments = message.hosts;
updatePlayerList();
break;
case "start":
Engine.SwitchGuiPage("page_loading.xml", {
"attribs": g_GameAttributes,
@ -330,7 +314,7 @@ function handleNetMessage(message)
"playerAssignments": g_PlayerAssignments
});
break;
case "chat":
addChatMessage({ "type": "message", "guid": message.guid, "text": message.text });
break;
@ -345,12 +329,12 @@ function getMapDisplayName(map)
{
var mapData = loadMapData(map);
if(!mapData || !mapData.settings || !mapData.settings.Name)
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;
}
@ -374,15 +358,15 @@ function initCivNameList()
g_CivData = loadCivData();
var civList = [ { "name": civ.Name, "code": civ.Code } for each (civ in g_CivData) ];
// 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) ];
// Update the dropdowns
for (var i = 0; i < g_MaxPlayers; ++i)
for (var i = 0; i < MAX_PLAYERS; ++i)
{
var civ = getGUIObjectByName("playerCiv["+i+"]");
civ.list = civListNames;
@ -391,7 +375,6 @@ function initCivNameList()
}
}
// Initialise the list control containing all the available maps
function initMapNameList()
{
@ -405,7 +388,7 @@ function initMapNameList()
case "scenario":
mapFiles = getXMLFileList(g_GameAttributes.mapPath);
break;
case "random":
mapFiles = getJSONFileList(g_GameAttributes.mapPath);
break;
@ -414,7 +397,7 @@ function initMapNameList()
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)
@ -428,16 +411,16 @@ function initMapNameList()
// Alphabetically sort the list, ignoring case
mapList.sort(sortNameIgnoreCase);
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
if (selected == -1 && mapList.length)
selected = 0;
// Update the list control
mapSelectionBox.list = mapListNames;
mapSelectionBox.list_data = mapListFiles;
@ -470,13 +453,47 @@ function loadMapData(name)
return g_MapData[name];
}
////////////////////////////////////////////////////////////////////////////////////////////////
// GUI event handlers
function cancelSetup()
{
Engine.DisconnectNetworkGame();
}
function onTick()
{
// First tick happens before first render, so don't load yet
if (g_LoadingState == 0)
{
g_LoadingState++;
}
else if (g_LoadingState == 1)
{
getGUIObjectByName("loadingWindow").hidden = true;
getGUIObjectByName("setupWindow").hidden = false;
initMain();
g_LoadingState++;
}
else if (g_LoadingState == 2)
{
while (true)
{
var message = Engine.PollNetworkClient();
if (!message)
break;
handleNetMessage(message);
}
}
}
// Called when user selects number of players
function selectNumPlayers(num)
{
// Avoid recursion
if (g_IsInGuiUpdate)
return;
// Network clients can't change number of players
if (g_IsNetworked && !g_IsController)
return;
@ -485,12 +502,19 @@ function selectNumPlayers(num)
if (g_GameAttributes.mapType != "random")
return;
g_NumPlayers = num;
// Update player data
var pData = g_GameAttributes.settings.PlayerData;
if (pData && num < pData.length)
{ // Remove extra player data
g_GameAttributes.settings.PlayerData = pData.slice(0, num);
}
else
{ // Add player data from defaults
for (var i = pData.length; i < num; ++i)
g_GameAttributes.settings.PlayerData.push(g_DefaultPlayerData[i]);
}
if (g_IsNetworked)
Engine.SetNetworkGameAttributes(g_GameAttributes);
else
onGameAttributesChange();
updateGameAttributes();
}
// Called when the user selects a map type from the list
@ -527,10 +551,7 @@ function selectMapType(type)
initMapNameList();
if (g_IsNetworked)
Engine.SetNetworkGameAttributes(g_GameAttributes);
else
onGameAttributesChange();
updateGameAttributes();
}
function selectMapFilter(filterName)
@ -547,10 +568,7 @@ function selectMapFilter(filterName)
initMapNameList();
if (g_IsNetworked)
Engine.SetNetworkGameAttributes(g_GameAttributes);
else
onGameAttributesChange();
updateGameAttributes();
}
// Called when the user selects a map from the list
@ -573,61 +591,98 @@ function selectMap(name)
var mapData = loadMapData(name);
var mapSettings = (mapData && mapData.settings ? mapData.settings : {});
// Load map type specific settings (so we can overwrite them in the GUI)
// Copy any new settings
switch (g_GameAttributes.mapType)
{
case "scenario":
g_NumPlayers = (mapSettings.PlayerData ? mapSettings.PlayerData.length : g_MaxPlayers);
g_GameAttributes.settings.PlayerData = (mapSettings.PlayerData ? mapSettings.PlayerData : g_DefaultPlayerData);
break;
case "random":
// Copy any new settings
if (mapSettings.PlayerData)
g_NumPlayers = mapSettings.PlayerData.length;
g_GameAttributes.script = mapSettings.Script;
g_GameAttributes.settings.Size = getSetting(mapSettings, g_GameAttributes.settings, "Size");
g_GameAttributes.settings.BaseTerrain = getSetting(mapSettings, g_GameAttributes.settings, "BaseTerrain");
g_GameAttributes.settings.BaseHeight = getSetting(mapSettings, g_GameAttributes.settings, "BaseHeight");
g_GameAttributes.settings.RevealMap = getSetting(mapSettings, g_GameAttributes.settings, "RevealMap");
g_GameAttributes.settings.GameType = getSetting(mapSettings, g_GameAttributes.settings, "GameType");
break;
default:
error("selectMap: Unexpected map type '"+g_GameAttributes.mapType+"'");
return;
}
g_GameAttributes.settings.Description = getSetting(mapSettings, g_GameAttributes.settings, "Description");
g_GameAttributes.settings.RevealMap = getSetting(mapSettings, g_GameAttributes.settings, "RevealMap");
g_GameAttributes.settings.LockTeams = getSetting(mapSettings, g_GameAttributes.settings, "LockTeams");
g_GameAttributes.settings.GameType = getSetting(mapSettings, g_GameAttributes.settings, "GameType");
// Reset player assignments on map change
if (!g_IsNetworked)
{ // Slot 1
g_PlayerAssignments = { "local": { "name": "You", "player": 1, "civ": "", "team": -1} };
updatePlayerList();
}
else
{
var numPlayers = (mapSettings.PlayerData ? mapSettings.PlayerData.length : g_GameAttributes.settings.PlayerData.length);
for (var guid in g_PlayerAssignments)
{ // Unassign extra players
var player = g_PlayerAssignments[guid].player;
if (player <= g_MaxPlayers && player > g_NumPlayers)
if (player <= MAX_PLAYERS && player > numPlayers)
Engine.AssignNetworkPlayer(player, "");
}
}
if (g_IsNetworked)
Engine.SetNetworkGameAttributes(g_GameAttributes);
else
onGameAttributesChange();
updateGameAttributes();
}
function launchGame()
{
if (g_IsNetworked && !g_IsController)
{
error("Only host can start game");
return;
}
if (g_IsNetworked)
{
Engine.SetNetworkGameAttributes(g_GameAttributes);
Engine.StartNetworkGame();
}
else
{
// Find the player ID which the user has been assigned to
var numPlayers = g_GameAttributes.settings.PlayerData.length;
var playerID = -1;
for (var i = 0; i < numPlayers; ++i)
{
var assignBox = getGUIObjectByName("playerAssignment["+i+"]");
if (assignBox.list_data[assignBox.selected] == "local")
playerID = i+1;
}
// 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,
"isNetworked" : g_IsNetworked,
"playerAssignments": g_PlayerAssignments
});
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
function onGameAttributesChange()
{
g_IsInGuiUpdate = true;
// Don't set any attributes here, just show the changes in GUI
var mapName = g_GameAttributes.map || "";
var mapData = loadMapData(mapName);
var mapSettings = (mapData && mapData.settings ? mapData.settings : {});
var mapSettings = g_GameAttributes.settings;
var numPlayers = (mapSettings.PlayerData ? mapSettings.PlayerData.length : MAX_PLAYERS);
// Update some controls for clients
if (!g_IsController)
{
@ -641,8 +696,6 @@ function onGameAttributesChange()
mapSelectionBox.selected = mapSelectionBox.list_data.indexOf(mapName);
initMapNameList();
g_NumPlayers = (mapSettings.PlayerData ? mapSettings.PlayerData.length : g_MaxPlayers);
}
// Controls common to all map types
@ -655,19 +708,16 @@ function onGameAttributesChange()
var lockTeamsText = getGUIObjectByName("lockTeamsText");
var mapSizeText = getGUIObjectByName("mapSizeText");
var numPlayersBox = getGUIObjectByName("numPlayersBox");
// Handle map type specific logic
switch (g_GameAttributes.mapType)
{
case "random":
g_GameAttributes.script = mapSettings.Script;
var sizeIdx = mapSize.list_data.indexOf(g_GameAttributes.settings.Size.toString());
// Show options for host/controller
var sizeIdx = MAP_SIZES_DATA.indexOf(mapSettings.Size);
if (g_IsController)
{
getGUIObjectByName("numPlayersSelection").selected = g_NumPlayers - 1;
{ //Host
getGUIObjectByName("numPlayersSelection").selected = numPlayers - 1;
numPlayersBox.hidden = false;
mapSize.hidden = false;
revealMap.hidden = false;
@ -675,25 +725,23 @@ function onGameAttributesChange()
lockTeams.hidden = false;
mapSizeText.caption = "Map size:";
mapSize.selected = sizeIdx;
revealMapText.caption = "Reveal map:";
revealMap.checked = (g_GameAttributes.settings.RevealMap ? true : false);
revealMap.checked = (mapSettings.RevealMap ? true : false);
victoryConditionText.caption = "Victory condition:";
victoryCondition.selected = victoryCondition.list_data.indexOf(g_GameAttributes.settings.GameType);
victoryCondition.selected = victoryCondition.list_data.indexOf(mapSettings.GameType);
lockTeamsText.caption = "Teams locked:";
lockTeams.checked = (g_GameAttributes.settings.LockTeams === undefined || g_GameAttributes.settings.LockTeams ? true : false);
lockTeams.checked = (mapSettings.LockTeams === undefined || mapSettings.LockTeams ? true : false);
}
else
{
mapSizeText.caption = "Map size: " + (mapSize.list[sizeIdx] !== undefined ? mapSize.list[sizeIdx] : "Default");
revealMapText.caption = "Reveal map: " + (g_GameAttributes.settings.RevealMap ? "Yes" : "No");
victoryConditionText.caption = "Victory condition: " + (g_GameAttributes.settings.GameType && g_GameAttributes.settings.GameType == "endless" ? "None" : "Conquest");
lockTeamsText.caption = "Teams locked: " + (g_GameAttributes.settings.LockTeams === undefined || g_GameAttributes.settings.LockTeams ? "Yes" : "No");
{ // Client
mapSizeText.caption = "Map size: " + (sizeIdx != -1 ? MAP_SIZES_TEXT[sizeIdx] : "Other (" + mapSettings.Size + ")");
revealMapText.caption = "Reveal map: " + (mapSettings.RevealMap ? "Yes" : "No");
victoryConditionText.caption = "Victory condition: " + (mapSettings.GameType && mapSettings.GameType == "endless" ? "None" : "Conquest");
lockTeamsText.caption = "Teams locked: " + (mapSettings.LockTeams === undefined || mapSettings.LockTeams ? "Yes" : "No");
}
break;
case "saved":
case "scenario":
// For scenario just reflect settings for the current map
numPlayersBox.hidden = true;
@ -716,21 +764,20 @@ function onGameAttributesChange()
// 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.";
// Describe the number of players
var playerString = g_NumPlayers + " " + (g_NumPlayers == 1 ? "player" : "players") + ". ";
var playerString = numPlayers + " " + (numPlayers == 1 ? "player" : "players") + ". ";
for (var i = 0; i < g_MaxPlayers; ++i)
for (var i = 0; i < MAX_PLAYERS; ++i)
{
// Show only needed player slots
getGUIObjectByName("playerBox["+i+"]").hidden = (i >= g_NumPlayers);
getGUIObjectByName("playerBox["+i+"]").hidden = (i >= numPlayers);
// Show player data or defaults as necessary
if (i < g_NumPlayers)
if (i < numPlayers)
{
var pName = getGUIObjectByName("playerName["+i+"]");
var pCiv = getGUIObjectByName("playerCiv["+i+"]");
@ -741,7 +788,7 @@ function onGameAttributesChange()
// Player data / defaults
var pData = mapSettings.PlayerData ? mapSettings.PlayerData[i] : {};
var pDefs = g_GameAttributes.settings.PlayerData ? g_GameAttributes.settings.PlayerData[i] : {};
var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[i] : {};
// Common to all game types
var color = iColorToString(getSetting(pData, pDefs, "Colour"));
@ -750,16 +797,18 @@ function onGameAttributesChange()
var team = getSetting(pData, pDefs, "Team");
var civ = getSetting(pData, pDefs, "Civ");
if (g_GameAttributes.mapType == "scenario")
{
// TODO: If scenario settings can be changed, handle that (using dropdowns rather than textboxes)
pCivText.caption = g_CivData[civ].Name;
pTeamText.caption = (team !== undefined && team >= 0) ? team+1 : "-";
// For clients or scenarios, hide some player dropdowns
if (!g_IsController || g_GameAttributes.mapType == "scenario")
{
pCivText.hidden = false;
pCiv.hidden = true;
pTeamText.hidden = false;
pTeam.hidden = true;
// Set text values
pCivText.caption = g_CivData[civ].Name;
pTeamText.caption = (team !== undefined && team >= 0) ? team+1 : "-";
}
else if (g_GameAttributes.mapType == "random")
{
@ -783,41 +832,12 @@ function onGameAttributesChange()
updatePlayerList();
}
function launchGame()
function updateGameAttributes()
{
if (g_IsNetworked && !g_IsController)
{
error("Only host can start game");
return;
}
// TODO: Generate seed here for random maps
if (g_IsNetworked)
{
Engine.SetNetworkGameAttributes(g_GameAttributes);
Engine.StartNetworkGame();
}
else
{
// Find the player ID which the user has been assigned to
var playerID = -1;
for (var i = 0; i < g_NumPlayers; ++i)
{
var assignBox = getGUIObjectByName("playerAssignment["+i+"]");
if (assignBox.list_data[assignBox.selected] == "local")
playerID = i+1;
}
// Remove extra player data
g_GameAttributes.settings.PlayerData = g_GameAttributes.settings.PlayerData.slice(0, g_NumPlayers);
Engine.StartGame(g_GameAttributes, playerID);
Engine.SwitchGuiPage("page_loading.xml", {
"attribs": g_GameAttributes,
"isNetworked" : g_IsNetworked,
"playerAssignments": g_PlayerAssignments
});
}
else
onGameAttributesChange();
}
function updatePlayerList()
@ -842,135 +862,139 @@ function updatePlayerList()
}
for each (var ai in g_AIs)
{
{ // Give AI a different color so it stands out
aiAssignments[ai.id] = hostNameList.length;
hostNameList.push("[color=\"90 90 90 255\"]AI: " + ai.data.name);
hostNameList.push("[color=\"30 90 30 255\"]AI: " + ai.data.name);
hostGuidList.push("ai:" + ai.id);
}
noAssignment = hostNameList.length;
hostNameList.push("[color=\"90 90 90 255\"]Unassigned");
hostGuidList.push("");
for (var i = 0; i < g_MaxPlayers; ++i)
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;
// If no human is assigned, look for an AI instead
if (selection === undefined)
// Look for valid player slots
if (playerSlot < g_GameAttributes.settings.PlayerData.length)
{
var aiId = g_GameAttributes.settings.PlayerData[playerSlot].AI;
if (aiId)
selection = aiAssignments[aiId];
else
selection = noAssignment;
// Since no human is assigned, show the AI config button
if (g_IsController)
// If no human is assigned, look for an AI instead
if (selection === undefined)
{
configButton.hidden = false;
configButton.onpress = function()
var aiId = g_GameAttributes.settings.PlayerData[playerSlot].AI;
if (aiId)
selection = aiAssignments[aiId];
else
selection = noAssignment;
// Since no human is assigned, show the AI config button
if (g_IsController)
{
Engine.PushGuiPage("page_aiconfig.xml", {
ais: g_AIs,
id: g_GameAttributes.settings.PlayerData[playerSlot].AI,
callback: function(ai) {
g_GameAttributes.settings.PlayerData[playerSlot].AI = ai.id;
configButton.hidden = false;
configButton.onpress = function()
{
Engine.PushGuiPage("page_aiconfig.xml", {
ais: g_AIs,
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);
else
updatePlayerList();
if (g_IsNetworked)
Engine.SetNetworkGameAttributes(g_GameAttributes);
else
updatePlayerList();
}
});
};
}
}
else
{
// There was a human, so make sure we don't have any AI left
// over in their slot, if we're in charge of the attributes
if (g_IsController && g_GameAttributes.settings.PlayerData[playerSlot].AI && g_GameAttributes.settings.PlayerData[playerSlot].AI != "")
{
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
if (g_IsNetworked)
Engine.SetNetworkGameAttributes(g_GameAttributes);
}
}
var assignBox = getGUIObjectByName("playerAssignment["+i+"]");
assignBox.list = hostNameList;
assignBox.list_data = hostGuidList;
if (assignBox.selected != selection)
assignBox.selected = selection;
if (g_IsNetworked && g_IsController)
{
assignBox.onselectionchange = function ()
{
if (!g_IsInGuiUpdate)
{
var guid = hostGuidList[this.selected];
if (guid == "")
{
// Unassign any host from this player slot
Engine.AssignNetworkPlayer(playerID, "");
// Remove AI from this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
}
});
else if (guid.substr(0, 3) == "ai:")
{
// Unassign any host from this player slot
Engine.AssignNetworkPlayer(playerID, "");
// Set the AI for this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = guid.substr(3);
}
else
{
// Reassign the host to this player slot
Engine.AssignNetworkPlayer(playerID, guid);
// Remove AI from this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
}
Engine.SetNetworkGameAttributes(g_GameAttributes);
}
};
}
}
else
{
// There was a human, so make sure we don't have any AI left
// over in their slot, if we're in charge of the attributes
if (g_IsController && g_GameAttributes.settings.PlayerData[playerSlot].AI != "")
else if (!g_IsNetworked)
{
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
if (g_IsNetworked)
Engine.SetNetworkGameAttributes(g_GameAttributes);
assignBox.onselectionchange = function ()
{
if (!g_IsInGuiUpdate)
{
var guid = hostGuidList[this.selected];
if (guid == "")
{
// Remove AI from this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
}
else if (guid.substr(0, 3) == "ai:")
{
// Set the AI for this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = guid.substr(3);
}
else
{
// Update the selected host's player ID
g_PlayerAssignments[guid].player = playerID;
// Remove AI from this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
}
updatePlayerList();
}
};
}
}
var assignBox = getGUIObjectByName("playerAssignment["+i+"]");
assignBox.list = hostNameList;
assignBox.list_data = hostGuidList;
if (assignBox.selected != selection)
assignBox.selected = selection;
if (g_IsNetworked && g_IsController)
{
assignBox.onselectionchange = function ()
{
if (!g_IsInGuiUpdate)
{
var guid = hostGuidList[this.selected];
if (guid == "")
{
// Unassign any host from this player slot
Engine.AssignNetworkPlayer(playerID, "");
// Remove AI from this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
}
else if (guid.substr(0, 3) == "ai:")
{
// Unassign any host from this player slot
Engine.AssignNetworkPlayer(playerID, "");
// Set the AI for this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = guid.substr(3);
}
else
{
// Reassign the host to this player slot
Engine.AssignNetworkPlayer(playerID, guid);
// Remove AI from this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
}
Engine.SetNetworkGameAttributes(g_GameAttributes);
}
};
}
else if (!g_IsNetworked)
{
assignBox.onselectionchange = function ()
{
if (!g_IsInGuiUpdate)
{
var guid = hostGuidList[this.selected];
if (guid == "")
{
// Remove AI from this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
}
else if (guid.substr(0, 3) == "ai:")
{
// Set the AI for this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = guid.substr(3);
}
else
{
// Update the selected host's player ID
g_PlayerAssignments[guid].player = playerID;
// Remove AI from this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
}
updatePlayerList();
}
};
}
}
g_IsInGuiUpdate = false;
@ -1002,7 +1026,7 @@ function addChatMessage(msg)
var mapData = loadMapData(mapName);
var mapSettings = (mapData && mapData.settings ? mapData.settings : {});
var pData = mapSettings.PlayerData ? mapSettings.PlayerData[player] : {};
var pDefs = g_GameAttributes.settings.PlayerData ? g_GameAttributes.settings.PlayerData[player] : {};
var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[player] : {};
color = iColorToString(getSetting(pData, pDefs, "Colour"));
}
@ -1032,7 +1056,7 @@ function addChatMessage(msg)
getGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Basic map filters API
// Add a new map list filter

View File

@ -134,15 +134,15 @@
<object name="gameOptionsBox" size="260 100%-260 100% 100%-190">
<object size="2 2 100% 50%-2"> <!-- Top row -->
<object name="mapSizeText" size="0 0 175 100%" type="text">Map size:</object>
<object name="mapSize" size="75 -2 175 100%-6" type="dropdown" style="wheatDropDown" hidden="true"/>
<object name="mapSize" size="75 -2 175 100%-6" type="dropdown" style="wheatDropDown" hidden="true" tooltip_style="onscreenToolTip" tooltip="Select map size (larger may reduce performance)"/>
<object name="victoryConditionText" size="195 0 450 100%" type="text">Victory condition:</object>
<object name="victoryCondition" size="325 -2 450 100%-6" type="dropdown" style="wheatDropDown" hidden="true"/>
<object name="victoryCondition" size="325 -2 450 100%-6" type="dropdown" style="wheatDropDown" hidden="true" tooltip_style="onscreenToolTip" tooltip="Select victory condition"/>
</object>
<object size="2 50%+2 100% 100%"> <!-- Bottom row -->
<object name="revealMapText" size="0 0 120 100%" type="text">Reveal map:</object>
<object name="revealMap" size="100 2 120 100%" type="checkbox" style="wheatCrossBox" hidden="true"/>
<object name="lockTeamsText" size="195 0 450 100%" type="text">Teams locked:</object>
<object name="lockTeams" size="260 2 280 100%" type="checkbox" style="wheatCrossBox" hidden="true"/>
<object name="revealMap" size="120 2 140 100%" type="checkbox" style="wheatCrossBox" hidden="true" tooltip_style="onscreenToolTip" tooltip="Toggle reveal map"/>
<object name="lockTeamsText" size="195 0 350 100%" type="text">Teams locked:</object>
<object name="lockTeams" size="350 2 370 100%" type="checkbox" style="wheatCrossBox" hidden="true" tooltip_style="onscreenToolTip" tooltip="Toggle locked teams"/>
</object>
</object>

View File

@ -1,4 +0,0 @@
const SIZE = 64;
init(SIZE, "grass dirt 50", 0);

View File

@ -0,0 +1,8 @@
// Blank map, possibly useful for Atlas or as fallback
RMS.LoadLibrary("rmgen");
InitMap();
// Export map data
ExportMap();

View File

@ -1 +0,0 @@
init(256, "grass_mediterranean_green_50", 0);

View File

@ -1 +0,0 @@
init(400, "grass_mediterranean_green_50", 0);

View File

@ -1 +0,0 @@
init(512, "grass_mediterranean_green_50", 0);

View File

@ -1,336 +1,376 @@
// constants
const SIZE = 208;
const NUM_PLAYERS = 4;
const tGrass = ["grass_temperate_pasture_a", "grass_temperate_pasture_b"];
const tGrassForest = "grass_temperate_a";
const tCliff = ["cliff_temperate_granite", "cliff_temperate_brown"];
const tForest = "forestfloor_temperate_a";
const tGrassDirt75 = "grass_temperate_dirt_2";
const tGrassDirt50 = "grass_temperate_dirt_b";
const tGrassDirt25 = "grass_temperate_dry_dirt";
const tDirt = "dirt_temperate_b_dry";
const tGrassPatch = "grass_temperate_field_wild";
const tShore = "shoreline_temperate_rocks";
const tShoreBlend = "shoreline_temperate_rocks_dirt";
const tWater = "sand_temperate_vegetation";
const oTree = "flora_tree_oak";
const oTreeLarge = "flora_tree_oak";
const oBerryBush = "flora_bush_berry";
const oSheep = "fauna_sheep";
const oDeer = "fauna_deer";
const oMine = "geology_stone_temperate";
const oGrass = "props/flora/grass_temp_field.xml";
const oGrassShort = "props/flora/grass_field_lush_short.xml";
const oReeds = "props/flora/grass_temp_field_dry.xml";
const oRockLarge = "geology/stone_granite_large.xml";
const oRockMedium = "flora_bush_temperate";
const oBushMedium = "props/flora/bush_tempe_me.xml";
const oBushSmall = "props/flora/bush_tempe_sm.xml";
// initialize map
println("Initializing map...");
init(SIZE, tGrass, 3);
// create tile classes
clPlayer = createTileClass();
clHill = createTileClass();
clForest = createTileClass();
clWater = createTileClass();
clSettlement = createTileClass();
clDirt = createTileClass();
clRock = createTileClass();
clFood = createTileClass();
clBaseResource = createTileClass();
// place players
playerX = new Array(NUM_PLAYERS+1);
playerY = new Array(NUM_PLAYERS+1);
playerAngle = new Array(NUM_PLAYERS+1);
startAngle = randFloat() * 2 * PI;
for(i=1; i<=NUM_PLAYERS; i++) {
playerAngle[i] = startAngle + i*2*PI/NUM_PLAYERS;
playerX[i] = 0.5 + 0.35*cos(playerAngle[i]);
playerY[i] = 0.5 + 0.35*sin(playerAngle[i]);
}
for(i=1; i<=NUM_PLAYERS; i++) {
println("Creating base for player " + i + "...");
// some constants
radius = 17;
cliffRadius = 2;
elevation = 20;
// get the x and y in tiles
fx = fractionToTiles(playerX[i]);
fy = fractionToTiles(playerY[i]);
ix = round(fx);
iy = round(fy);
// calculate size based on the radius
size = PI * radius * radius;
// create the hill
placer = new ClumpPlacer(size, 0.95, 0.6, 0, ix, iy);
terrainPainter = new LayeredPainter(
[cliffRadius], // widths
[tCliff, tGrass] // terrains
);
elevationPainter = new SmoothElevationPainter(
ELEVATION_SET, // type
elevation, // elevation
cliffRadius // blend radius
);
createArea(placer, [terrainPainter, elevationPainter, paintClass(clPlayer)], null);
// create the ramp
rampAngle = playerAngle[i] + PI + (2*randFloat()-1)*PI/8;
rampDist = radius - 1;
rampX = round(fx + rampDist * cos(rampAngle));
rampY = round(fy + rampDist * sin(rampAngle));
placer = new ClumpPlacer(100, 0.9, 0.5, 0, rampX, rampY);
painter = new SmoothElevationPainter(ELEVATION_SET, elevation-6, 5);
createArea(placer, painter, null);
placer = new ClumpPlacer(75, 0.9, 0.5, 0, rampX, rampY);
painter = new TerrainPainter(tGrass);
createArea(placer, painter, null);
// create the central dirt patch
placer = new ClumpPlacer(PI*3.5*3.5, 0.3, 0.1, 0, ix, iy);
painter = new LayeredPainter(
[1,1,1], // widths
[tGrassDirt75, tGrassDirt50, tGrassDirt25, tDirt] // terrains
);
createArea(placer, painter, null);
// create the TC and the "villies"
group = new SimpleGroup(
[ // elements (type, count, distance)
new SimpleObject("hele_civil_centre", 1,1, 0,0),
new SimpleObject("hele_infantry_javelinist_b", 3,3, 5,5)
],
true, null, ix, iy
);
createObjectGroup(group, i);
// Create the Settlement under the TC
group = new SimpleGroup(
[new SimpleObject("special_settlement", 1,1, 0,0)],
true, null, ix, iy
);
createObjectGroup(group, 0);
// create berry bushes
bbAngle = randFloat()*2*PI;
bbDist = 9;
bbX = round(fx + bbDist * cos(bbAngle));
bbY = round(fy + bbDist * sin(bbAngle));
group = new SimpleGroup(
[new SimpleObject(oBerryBush, 5,5, 0,2)],
true, clBaseResource, bbX, bbY
);
createObjectGroup(group, 0);
// create mines
mAngle = bbAngle;
while(abs(mAngle - bbAngle) < PI/3) {
mAngle = randFloat()*2*PI;
}
mDist = 9;
mX = round(fx + mDist * cos(mAngle));
mY = round(fy + mDist * sin(mAngle));
group = new SimpleGroup(
[new SimpleObject(oMine, 4,4, 0,2)],
true, clBaseResource, mX, mY
);
createObjectGroup(group, 0);
// create starting straggler trees
group = new SimpleGroup(
[new SimpleObject(oTree, 3,3, 8,12)],
true, clBaseResource, ix, iy
);
createObjectGroup(group, 0, avoidClasses(clBaseResource,2));
// create grass tufts
for(j=0; j<10; j++) {
gAngle = randFloat()*2*PI;
gDist = 6 + randInt(9);
gX = round(fx + gDist * cos(gAngle));
gY = round(fy + gDist * sin(gAngle));
group = new SimpleGroup([new SimpleObject(oGrassShort, 3,6, 0,1, -PI/8,PI/8)],
false, clBaseResource, gX, gY);
createObjectGroup(group, 0);
}
}
// create lakes
println("Creating lakes...");
placer = new ClumpPlacer(140, 0.8, 0.1, 0);
terrainPainter = new LayeredPainter(
[1,1], // widths
[tShoreBlend, tShore, tWater] // terrains
);
elevationPainter = new SmoothElevationPainter(ELEVATION_SET, -7, 3);
createAreas(placer, [terrainPainter, elevationPainter, paintClass(clWater)],
avoidClasses(clPlayer, 2, clWater, 20),
round(1.3 * NUM_PLAYERS)
);
// create bumps
println("Creating bumps...");
placer = new ClumpPlacer(10, 0.3, 0.06, 0);
painter = new SmoothElevationPainter(ELEVATION_MODIFY, 2, 2);
createAreas(placer, painter,
avoidClasses(clWater, 2, clPlayer, 0),
SIZE*SIZE/100
);
// create hills
println("Creating hills...");
placer = new ClumpPlacer(30, 0.2, 0.1, 0);
terrainPainter = new LayeredPainter(
[3], // widths
[tCliff, [tGrass,tGrass,tGrassDirt75]] // terrains
);
elevationPainter = new SmoothElevationPainter(ELEVATION_SET, 12, 2);
createAreas(placer, [terrainPainter, elevationPainter, paintClass(clHill)],
avoidClasses(clPlayer, 2, clWater, 5, clHill, 15),
2 * NUM_PLAYERS
);
// create forests
println("Creating forests...");
placer = new ClumpPlacer(32, 0.1, 0.1, 0);
painter = new LayeredPainter([2], [[tGrassForest, tGrass, tForest],
[tGrassForest, tForest]]);
createAreas(placer, [painter, paintClass(clForest)],
avoidClasses(clPlayer, 1, clWater, 3, clForest, 10, clHill, 0),
6 * NUM_PLAYERS
);
// create dirt patches
println("Creating dirt patches...");
var sizes = [8,14,20];
for(i=0; i<sizes.length; i++) {
placer = new ClumpPlacer(sizes[i], 0.3, 0.06, 0);
painter = new LayeredPainter([1,1], [
[tGrass,tGrassDirt75],[tGrassDirt75,tGrassDirt50],
[tGrassDirt50,tGrassDirt25]]);
createAreas(placer, [painter, paintClass(clDirt)],
avoidClasses(clWater, 1, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 0),
SIZE*SIZE/4000
);
}
// create grass patches
println("Creating grass patches...");
var sizes = [5,9,13];
for(i=0; i<sizes.length; i++) {
placer = new ClumpPlacer(sizes[i], 0.3, 0.06, 0);
painter = new TerrainPainter(tGrassPatch);
createAreas(placer, painter,
avoidClasses(clWater, 1, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 0),
SIZE*SIZE/4000
);
}
// create mines
println("Creating mines...");
group = new SimpleGroup([new SimpleObject(oMine, 4,6, 0,2)], true, clRock);
createObjectGroups(group, 0,
[avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clRock, 13),
new BorderTileClassConstraint(clHill, 0, 4)],
3 * NUM_PLAYERS, 100
);
// create settlements
println("Creating settlements...");
group = new SimpleGroup([new SimpleObject("special_settlement", 1,1, 0,0)], true, clSettlement);
createObjectGroups(group, 0,
avoidClasses(clWater, 0, clForest, 0, clPlayer, 15, clHill, 0, clRock, 5, clSettlement, 35),
2 * NUM_PLAYERS, 50
);
// create small decorative rocks
println("Creating large decorative rocks...");
group = new SimpleGroup([new SimpleObject(oRockMedium, 1,3, 0,1)], true);
createObjectGroups(group, 0,
avoidClasses(clForest, 0, clPlayer, 0, clHill, 0, clSettlement, 3),
SIZE*SIZE/1000, 50
);
// create large decorative rocks
println("Creating large decorative rocks...");
group = new SimpleGroup([new SimpleObject(oRockLarge, 1,2, 0,1),
new SimpleObject(oRockMedium, 1,3, 0,2)], true);
createObjectGroups(group, 0,
avoidClasses(clForest, 0, clPlayer, 0, clHill, 0, clSettlement, 5),
SIZE*SIZE/2000, 50
);
// create deer
println("Creating deer...");
group = new SimpleGroup([new SimpleObject(oDeer, 5,7, 0,4)], true, clFood);
createObjectGroups(group, 0,
avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0, clFood, 20, clSettlement, 5),
3 * NUM_PLAYERS, 50
);
// create sheep
println("Creating sheep...");
group = new SimpleGroup([new SimpleObject(oSheep, 2,3, 0,2)], true, clFood);
createObjectGroups(group, 0,
avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0, clFood, 20, clSettlement, 5),
3 * NUM_PLAYERS, 50
);
// create straggler trees
println("Creating straggler trees...");
group = new SimpleGroup([new SimpleObject(oTree, 1,1, 0,0)], true);
createObjectGroups(group, 0,
avoidClasses(clWater, 1, clForest, 1, clHill, 1, clPlayer, 1, clSettlement, 2),
SIZE*SIZE/1100
);
// create small grass tufts
println("Creating small grass tufts...");
group = new SimpleGroup([new SimpleObject(oGrassShort, 3,6, 0,1, -PI/8,PI/8)]);
createObjectGroups(group, 0,
avoidClasses(clWater, 2, clHill, 2, clPlayer, 2, clDirt, 0),
SIZE*SIZE/90
);
// create large grass tufts
println("Creating large grass tufts...");
group = new SimpleGroup([new SimpleObject(oGrass, 20,30, 0,1.8, -PI/8,PI/8),
new SimpleObject(oGrassShort, 20,30, 1.2,2.5, -PI/8,PI/8)]);
createObjectGroups(group, 0,
avoidClasses(clWater, 3, clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0),
SIZE*SIZE/900
);
// create bushes
println("Creating bushes...");
group = new SimpleGroup([new SimpleObject(oBushSmall, 2,4, 0,2)]);
createObjectGroups(group, 0,
avoidClasses(clWater, 1, clHill, 1, clPlayer, 1, clDirt, 1),
SIZE*SIZE/2000, 50
);
// create reeds
println("Creating reeds...");
group = new SimpleGroup([new SimpleObject(oReeds, 5,10, 0,1.5, -PI/8,PI/8)]);
createObjectGroups(group, 0,
[new BorderTileClassConstraint(clWater, 3, 0), new StayInTileClassConstraint(clWater, 1)],
10 * NUM_PLAYERS, 100
);
RMS.LoadLibrary("rmgen");
// terrain textures
const tGrass = ["medit_grass_field_a", "medit_grass_field_b"];
const tGrassForest = "medit_grass_wild";
const tCliff = ["medit_cliff_italia", "medit_cliff_italia_grass"];
const tGrassDirt75 = "medit_dirt";
const tGrassDirt50 = "medit_dirt_b";
const tGrassDirt25 = "medit_dirt_c";
const tDirt = "medit_dirt_b";
const tGrassPatch = "medit_grass_wild";
const tShore = "medit_rocks";
const tShoreBlend = "medit_rocks_grass";
const tWater = "medit_rocks_wet";
// gaia entities
const oTree = "gaia/flora_tree_oak";
const oTreeLarge = "gaia/flora_tree_oak";
const oBerryBush = "gaia/flora_bush_berry";
const oSheep = "gaia/fauna_sheep";
const oDeer = "gaia/fauna_deer";
const oMine = "gaia/geology_stone_temperate";
// decorative props
const aGrass = "actor|props/flora/grass_temp_field.xml";
const aGrassShort = "actor|props/flora/grass_field_lush_short.xml";
const aReeds = "actor|props/flora/grass_temp_field_dry.xml";
const aRockLarge = "actor|geology/stone_granite_large.xml";
const aRockMedium = "actor|geology/stone_granite_med.xml";
const aBushMedium = "actor|props/flora/bush_tempe_me.xml";
const aBushSmall = "actor|props/flora/bush_tempe_sm.xml";
// terrain + entity (for painting)
const pForest = tGrassForest + TERRAIN_SEPARATOR + oTree;
// initialize map
log("Initializing map...");
InitMap();
var numPlayers = getNumPlayers();
var mapSize = getMapSize();
// create tile classes
var clPlayer = createTileClass();
var clHill = createTileClass();
var clForest = createTileClass();
var clWater = createTileClass();
var clDirt = createTileClass();
var clRock = createTileClass();
var clFood = createTileClass();
var clBaseResource = createTileClass();
// place players
var playerX = new Array(numPlayers+1);
var playerY = new Array(numPlayers+1);
var playerAngle = new Array(numPlayers+1);
var startAngle = randFloat() * 2 * PI;
for (var i=1; i<=numPlayers; i++)
{
playerAngle[i] = startAngle + i*2*PI/numPlayers;
playerX[i] = 0.5 + 0.35*cos(playerAngle[i]);
playerY[i] = 0.5 + 0.35*sin(playerAngle[i]);
}
for (var i=1; i<=numPlayers; i++)
{
log("Creating base for player " + i + "...");
// some constants
var radius = 17;
var cliffRadius = 2;
var elevation = 20;
// get the x and y in tiles
var fx = fractionToTiles(playerX[i]);
var fy = fractionToTiles(playerY[i]);
var ix = round(fx);
var iy = round(fy);
// calculate size based on the radius
var size = PI * radius * radius;
// create the hill
var placer = new ClumpPlacer(size, 0.95, 0.6, 0, ix, iy);
var terrainPainter = new LayeredPainter(
[tCliff, tGrass], // terrains
[cliffRadius] // widths
);
var elevationPainter = new SmoothElevationPainter(
ELEVATION_SET, // type
elevation, // elevation
cliffRadius // blend radius
);
createArea(placer, [terrainPainter, elevationPainter, paintClass(clPlayer)], null);
// create the ramp
var rampAngle = playerAngle[i] + PI + (2*randFloat()-1)*PI/8;
var rampDist = radius - 1;
var rampX = round(fx + rampDist * cos(rampAngle));
var rampY = round(fy + rampDist * sin(rampAngle));
placer = new ClumpPlacer(100, 0.9, 0.5, 0, rampX, rampY);
var painter = new SmoothElevationPainter(ELEVATION_SET, elevation-6, 5);
createArea(placer, painter, null);
placer = new ClumpPlacer(75, 0.9, 0.5, 0, rampX, rampY);
painter = new TerrainPainter(tGrass);
createArea(placer, painter, null);
// create the central dirt patch
placer = new ClumpPlacer(PI*3.5*3.5, 0.3, 0.1, 0, ix, iy);
painter = new LayeredPainter(
[tGrassDirt75, tGrassDirt50, tGrassDirt25, tDirt], // terrains
[1,1,1] // widths
);
createArea(placer, painter, null);
// create the TC and citizens
var civ = getCivCode(i - 1);
var group = new SimpleGroup( // elements (type, min/max count, min/max distance)
[new SimpleObject("structures/"+civ+"_civil_centre", 1,1, 0,0), new SimpleObject("units/"+civ+"_support_female_citizen", 3,3, 5,5)],
true, null, ix, iy
);
createObjectGroup(group, i);
// create berry bushes
var bbAngle = randFloat()*2*PI;
var bbDist = 9;
var bbX = round(fx + bbDist * cos(bbAngle));
var bbY = round(fy + bbDist * sin(bbAngle));
group = new SimpleGroup(
[new SimpleObject(oBerryBush, 5,5, 0,2)],
true, clBaseResource, bbX, bbY
);
createObjectGroup(group, 0);
// create mines
var mAngle = bbAngle;
while(abs(mAngle - bbAngle) < PI/3)
{
mAngle = randFloat()*2*PI;
}
var mDist = 9;
var mX = round(fx + mDist * cos(mAngle));
var mY = round(fy + mDist * sin(mAngle));
group = new SimpleGroup(
[new SimpleObject(oMine, 4,4, 0,2)],
true, clBaseResource, mX, mY
);
createObjectGroup(group, 0);
// create starting straggler trees
group = new SimpleGroup(
[new SimpleObject(oTree, 3,3, 8,12)],
true, clBaseResource, ix, iy
);
createObjectGroup(group, 0, avoidClasses(clBaseResource,2));
// create grass tufts
for (var j=0; j < 10; j++)
{
var gAngle = randFloat()*2*PI;
var gDist = 6 + randInt(9);
var gX = round(fx + gDist * cos(gAngle));
var gY = round(fy + gDist * sin(gAngle));
group = new SimpleGroup(
[new SimpleObject(aGrassShort, 3,6, 0,1, -PI/8,PI/8)],
false, clBaseResource, gX, gY
);
createObjectGroup(group, undefined);
}
}
// create lakes
log("Creating lakes...");
placer = new ClumpPlacer(140, 0.8, 0.1, 0);
terrainPainter = new LayeredPainter(
[tShoreBlend, tShore, tWater], // terrains
[1,1] // widths
);
elevationPainter = new SmoothElevationPainter(ELEVATION_SET, -7, 3);
createAreas(
placer,
[terrainPainter, elevationPainter, paintClass(clWater)],
avoidClasses(clPlayer, 2, clWater, 20),
round(1.3 * numPlayers)
);
// create bumps
log("Creating bumps...");
placer = new ClumpPlacer(10, 0.3, 0.06, 0);
painter = new SmoothElevationPainter(ELEVATION_MODIFY, 2, 2);
createAreas(
placer,
painter,
avoidClasses(clWater, 2, clPlayer, 0),
mapSize*mapSize/1000
);
// create hills
log("Creating hills...");
placer = new ClumpPlacer(30, 0.2, 0.1, 0);
terrainPainter = new LayeredPainter(
[tCliff, [tGrass,tGrass,tGrassDirt75]], // terrains
[3] // widths
);
elevationPainter = new SmoothElevationPainter(ELEVATION_SET, 12, 2);
createAreas(
placer,
[terrainPainter, elevationPainter, paintClass(clHill)],
avoidClasses(clPlayer, 2, clWater, 5, clHill, 15),
2 * numPlayers
);
// create forests
log("Creating forests...");
placer = new ClumpPlacer(32, 0.1, 0.1, 0);
painter = new LayeredPainter(
[[tGrassForest, tGrass, pForest], [tGrassForest, pForest]], // terrains
[2] // widths
);
createAreas(
placer,
[painter, paintClass(clForest)],
avoidClasses(clPlayer, 1, clWater, 3, clForest, 10, clHill, 0),
6 * numPlayers
);
// create dirt patches
log("Creating dirt patches...");
var sizes = [8,14,20];
for (i=0; i<sizes.length; i++)
{
placer = new ClumpPlacer(sizes[i], 0.3, 0.06, 0);
painter = new LayeredPainter(
[[tGrass,tGrassDirt75],[tGrassDirt75,tGrassDirt50], [tGrassDirt50,tGrassDirt25]], // terrains
[1,1] // widths
);
createAreas(
placer,
[painter, paintClass(clDirt)],
avoidClasses(clWater, 1, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 0),
mapSize*mapSize/4000
);
}
// create grass patches
log("Creating grass patches...");
var sizes = [5,9,13];
for (i=0; i<sizes.length; i++)
{
placer = new ClumpPlacer(sizes[i], 0.3, 0.06, 0);
painter = new TerrainPainter(tGrassPatch);
createAreas(
placer,
painter,
avoidClasses(clWater, 1, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 0),
mapSize*mapSize/4000
);
}
// create mines
log("Creating mines...");
group = new SimpleGroup(
[new SimpleObject(oMine, 4,6, 0,2)],
true, clRock
);
createObjectGroups(
group, 0,
[avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clRock, 13),
new BorderTileClassConstraint(clHill, 0, 4)],
3 * numPlayers, 100
);
// create small decorative rocks
log("Creating large decorative rocks...");
group = new SimpleGroup(
[new SimpleObject(aRockMedium, 1,3, 0,1)],
true
);
createObjectGroups(
group, undefined,
avoidClasses(clForest, 0, clPlayer, 0, clHill, 0),
mapSize*mapSize/1000, 50
);
// create large decorative rocks
log("Creating large decorative rocks...");
group = new SimpleGroup(
[new SimpleObject(aRockLarge, 1,2, 0,1), new SimpleObject(aRockMedium, 1,3, 0,2)],
true
);
createObjectGroups(
group, undefined,
avoidClasses(clForest, 0, clPlayer, 0, clHill, 0),
mapSize*mapSize/2000, 50
);
// create deer
log("Creating deer...");
group = new SimpleGroup(
[new SimpleObject(oDeer, 5,7, 0,4)],
true, clFood
);
createObjectGroups(group, 0,
avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0, clFood, 20),
3 * numPlayers, 50
);
// create sheep
log("Creating sheep...");
group = new SimpleGroup(
[new SimpleObject(oSheep, 2,3, 0,2)],
true, clFood
);
createObjectGroups(group, 0,
avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0, clFood, 20),
3 * numPlayers, 50
);
// create straggler trees
log("Creating straggler trees...");
group = new SimpleGroup(
[new SimpleObject(oTree, 1,1, 0,0)],
true
);
createObjectGroups(group, 0,
avoidClasses(clWater, 1, clForest, 1, clHill, 1, clPlayer, 1),
mapSize*mapSize/1100
);
// create small grass tufts
log("Creating small grass tufts...");
group = new SimpleGroup(
[new SimpleObject(aGrassShort, 3,6, 0,1, -PI/8,PI/8)]
);
createObjectGroups(group, undefined,
avoidClasses(clWater, 2, clHill, 2, clPlayer, 2, clDirt, 0),
mapSize*mapSize/90
);
// create large grass tufts
log("Creating large grass tufts...");
group = new SimpleGroup(
[new SimpleObject(aGrass, 20,30, 0,1.8, -PI/8,PI/8), new SimpleObject(aGrassShort, 20,30, 1.2,2.5, -PI/8,PI/8)]
);
createObjectGroups(group, undefined,
avoidClasses(clWater, 3, clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0),
mapSize*mapSize/900
);
// create bushes
log("Creating bushes...");
group = new SimpleGroup(
[new SimpleObject(aBushSmall, 2,4, 0,2)]
);
createObjectGroups(group, undefined,
avoidClasses(clWater, 1, clHill, 1, clPlayer, 1, clDirt, 1),
mapSize*mapSize/2000, 50
);
// create reeds
log("Creating reeds...");
group = new SimpleGroup(
[new SimpleObject(aReeds, 5,10, 0,1.5, -PI/8,PI/8)]
);
createObjectGroups(group, undefined,
[new BorderTileClassConstraint(clWater, 3, 0), new StayInTileClassConstraint(clWater, 1)],
10 * numPlayers, 100
);
// Export map data
ExportMap();

View File

@ -0,0 +1,10 @@
{
"settings" : {
"Name" : "Cantabrian Highlands",
"Script" : "cantabrian_highlands.js",
"Description" : "Each player starts on a hill surrounded by cliffs",
"BaseTerrain" : ["medit_grass_field_a", "medit_grass_field_b"],
"BaseHeight" : 3,
"XXXXXX" : "Optionally define other things here, like we would for a scenario"
}
}

View File

@ -1,449 +1,514 @@
const SIZE = 176;
const NUM_PLAYERS = 4;
const WATER_WIDTH = .2;
// Terrain and object constants
tOceanDepths = "ocean_medit_depths";
tOceanRockDeep = "ocean_medit_rock_deep";
tOceanRockShallow = "ocean_medit_rock_shallow";
tOceanCoral = "ocean_medit_coral";
tBeachWet = "beach_medit_wet";
tBeachDry = "beach_medit_dry";
tBeachGrass = "beach_medit_grass_50";
tBeachCliff = "cliff_medit_beach";
tGrassDry = ["grass_mediterranean_dry_a", "grass_mediterranean_dry_b", "grass_mediterranean_dry_c"];
tGrass = ["grass_mediterranean_green_50", "grass_mediterranean_green_flowers"];
tGrassLush = ["grass_temperate_dry_tufts", "grass_mediterranean_green_flowers"];
tGrassShrubs = ["grass_mediterranean_green_shrubs", "grass_mediterranean_green_flowers"];
tGrassRock = ["grass_mediterranean_green_rock"];
tDirt = "dirt_medit_a";
tDirtGrass = "dirt_medit_grass_50";
tDirtCliff = "cliff_medit_dirt";
tGrassCliff = "cliff_medit_grass_a";
tCliff = ["cliff_medit_face_b", "cliff_medit_face_b", "cliff_medit_foliage_a"];
tForestFloor = "forestfloor_medit_dirt";
oPalm = "flora_tree_medit_fan_palm";
oLombardyPoplar = "flora_tree_poplar_lombardy";
oOak = "flora_tree_oak";
oPoplar = "flora_tree_poplar";
oCarob = "flora_tree_carob";
oBeech = "flora_tree_euro_beech";
oPine = "flora_tree_aleppo_pine";
oBerryBush = "flora_bush_berry";
oSheep = "fauna_sheep";
oStone = "geology_stone_greek";
oMetal = "geology_metal_greek";
oBushLargeDry = "props/flora/bush_medit_la_dry.xml";
oBushLarge = "props/flora/bush_medit_la.xml";
oBushMedDry = "props/flora/bush_medit_me_dry.xml";
oBushMed = "props/flora/bush_medit_me.xml";
oBushSmall = "props/flora/bush_medit_sm.xml"
oBushSmallDry = "props/flora/bush_medit_sm_dry.xml"
oGrass = "props/flora/grass_medit_field.xml";
oGrassDry = "props/flora/grass_soft_dry_small.xml";
oRockLarge = "geology/stone_granite_greek_large.xml";
oRockMed = "geology/stone_granite_greek_med.xml";
oRockSmall = "geology/stone_granite_greek_small.xml";
oWaterLog = "props/flora/water_log.xml";
tPalmForest = [tForestFloor+"|"+oPalm, tGrass];
tPineForest = [tForestFloor+"|"+oPine, tGrass];
tMainForest = [tForestFloor+"|"+oCarob, tForestFloor+"|"+oBeech, tGrass, tGrass];
tPoplarForest = [tForestFloor+"|"+oLombardyPoplar, tGrass];
// Initialize world
init(SIZE, tGrass, 0);
// Create classes
clWater = createTileClass();
clCliff = createTileClass();
clForest = createTileClass();
clMetal = createTileClass();
clStone = createTileClass();
clFood = createTileClass();
clPlayer = createTileClass();
clBaseResource = createTileClass();
clSettlement = createTileClass();
// Place players
println("Placing players...");
playerX = new Array(NUM_PLAYERS+1);
playerY = new Array(NUM_PLAYERS+1);
numLeftPlayers = Math.floor(NUM_PLAYERS/2);
for(i=1; i<=numLeftPlayers; i++) {
playerX[i] = 0.28 + (2*randFloat()-1)*0.01;
playerY[i] = (0.5+i-1)/numLeftPlayers + (2*randFloat()-1)*0.01;
}
for(i=numLeftPlayers+1; i<=NUM_PLAYERS; i++) {
playerX[i] = 0.72 + (2*randFloat()-1)*0.01;
playerY[i] = (0.5+i-numLeftPlayers-1)/numLeftPlayers + (2*randFloat()-1)*0.01;
}
for(i=1; i<=NUM_PLAYERS; i++) {
// get fractional locations in tiles
ix = round(fractionToTiles(playerX[i]));
iy = round(fractionToTiles(playerY[i]));
addToClass(ix, iy, clPlayer);
// create TC and starting units
placeObject("special_settlement", i, ix, iy, PI*3/4);
placeObject("hele_civil_centre", i, ix, iy, PI*3/4);
group = new SimpleGroup(
[new SimpleObject("hele_infantry_javelinist_b", 3,3, 5,5)],
true, null, ix, iy
);
createObjectGroup(group, i);
// create starting berry bushes
bbAngle = randFloat()*2*PI;
bbDist = 9;
bbX = round(ix + bbDist * cos(bbAngle));
bbY = round(iy + bbDist * sin(bbAngle));
group = new SimpleGroup(
[new SimpleObject(oBerryBush, 5,5, 0,2)],
true, clBaseResource, bbX, bbY
);
createObjectGroup(group, 0);
// create starting mines
mAngle = bbAngle;
while(abs(mAngle - bbAngle) < PI/3) {
mAngle = randFloat()*2*PI;
}
mDist = 9;
mX = round(ix + mDist * cos(mAngle));
mY = round(iy + mDist * sin(mAngle));
group = new SimpleGroup(
[new SimpleObject(oStone, 2,2, 0,3),
new SimpleObject(oMetal, 2,2, 0,3)],
true, clBaseResource, mX, mY
);
createObjectGroup(group, 0);
// create starting straggler trees
group = new SimpleGroup(
[new SimpleObject(oPalm, 3,3, 7,10)],
true, clBaseResource, ix, iy
);
createObjectGroup(group, 0, avoidClasses(clBaseResource,2));
}
function distanceToPlayers(x, y) {
var r = 10000;
for(var i=1; i<=NUM_PLAYERS; i++) {
var dx = x-playerX[i];
var dy = y-playerY[i];
r = min(r, dx*dx + dy*dy);
}
return Math.sqrt(r);
}
function playerNearness(x, y) {
var d = fractionToTiles(distanceToPlayers(x,y));
if(d < 13) return 0;
else if(d < 19) return (d-13)/(19-13);
else return 1;
}
function max(x, y) {
return x > y ? x : y;
}
function min(x, y) {
return x < y ? x : y;
}
// Paint elevation
println("Painting elevation...");
noise0 = new Noise2D(4 * SIZE/128);
noise1 = new Noise2D(8 * SIZE/128);
noise2 = new Noise2D(15 * SIZE/128);
noise2a = new Noise2D(20 * SIZE/128);
noise2b = new Noise2D(35 * SIZE/128);
noise3 = new Noise2D(4 * SIZE/128);
noise4 = new Noise2D(6 * SIZE/128);
noise5 = new Noise2D(11 * SIZE/128);
for(ix=0; ix<=SIZE; ix++) {
for(iy=0; iy<=SIZE; iy++) {
x = ix / (SIZE + 1.0);
y = iy / (SIZE + 1.0);
pn = playerNearness(x, y);
h = 0;
distToWater = 0;
h = 32 * (x-.5);
// add the rough shape of the water
if(x < WATER_WIDTH) {
h = max(-16.0, -28.0*(WATER_WIDTH-x)/WATER_WIDTH);
}
else if(x > 1.0-WATER_WIDTH) {
h = max(-16.0, -28.0*(x-(1.0-WATER_WIDTH))/WATER_WIDTH);
}
else {
distToWater = (0.5 - WATER_WIDTH - Math.abs(x-0.5));
u = 1 - Math.abs(x-0.5) / (0.5-WATER_WIDTH);
h = 12*u;
}
// add some base noise
baseNoise = 16*noise0.eval(x,y) + 8*noise1.eval(x,y) + 4*noise2.eval(x,y) - (16+8+4)/2;
if( baseNoise < 0 ) {
baseNoise *= pn;
baseNoise *= max(0.1, distToWater / (0.5-WATER_WIDTH));
}
oldH = h;
h += baseNoise;
// add some higher-frequency noise on land
if( oldH > 0 )
{
h += (0.4*noise2a.eval(x,y) + 0.2*noise2b.eval(x,y)) * min(oldH/10.0, 1.0);
}
// create cliff noise
if( h > -10 )
{
cliffNoise = (1*noise3.eval(x,y) + 0.5*noise4.eval(x,y)) / 1.5;
if(h < 1) {
u = 1 - .3*((h-1)/-10);
cliffNoise *= u;
}
cliffNoise += .05 * distToWater / (0.5 - WATER_WIDTH);
if(cliffNoise > .6) {
u = 0.8 * (cliffNoise-.6);
cliffNoise += u * noise5.eval(x,y);
cliffNoise /= (1+u);
}
cliffNoise -= 0.59;
cliffNoise *= pn;
if(cliffNoise > 0) {
h += 19 * min(cliffNoise, 0.045) / 0.045;
}
}
// set the height
setHeight(ix, iy, h);
}
}
// Paint base terrain
println("Painting terrain...");
noise6 = new Noise2D(10 * SIZE/128);
noise7 = new Noise2D(20 * SIZE/128);
noise8 = new Noise2D(13 * SIZE/128);
noise9 = new Noise2D(26 * SIZE/128);
noise10 = new Noise2D(50 * SIZE/128);
for(ix=0; ix<SIZE; ix++) {
for(iy=0; iy<SIZE; iy++) {
x = ix / (SIZE + 1.0);
y = iy / (SIZE + 1.0);
pn = playerNearness(x, y);
// get heights of surrounding vertices
h00 = getHeight(ix, iy);
h01 = getHeight(ix, iy+1);
h10 = getHeight(ix+1, iy);
h11 = getHeight(ix+1, iy+1);
// find min and max height
maxH = max(h00, h01, h10, h11);
minH = min(h00, h01, h10, h11);
// figure out if we're at the top of a cliff using min adjacent height
minAdjHeight = minH;
if(maxH > 15) {
for(nx=max(ix-1, 0); nx<=min(ix+2, SIZE); nx++) {
for(ny=max(iy-1, 0); ny<=min(iy+2, SIZE); ny++) {
minAdjHeight = min(minAdjHeight, getHeight(nx, ny));
}
}
}
// choose a terrain based on elevation
t = tGrass;
// water
if(maxH < -12) {
t = tOceanDepths;
}
else if(maxH < -8.8) {
t = tOceanRockDeep;
}
else if(maxH < -4.7) {
t = tOceanCoral;
}
else if(maxH < -2.8) {
t = tOceanRockShallow;
}
else if(maxH < .9 && minH < .35) {
t = tBeachWet;
}
else if(maxH < 1.5 && minH < .9) {
t = tBeachDry;
}
else if(maxH < 2.3 && minH < 1.3) {
t = tBeachGrass;
}
if(minH < 0) {
addToClass(ix, iy, clWater);
}
// cliffs
if(maxH - minH > 2.9 && minH > -7) {
t = tCliff;
addToClass(ix, iy, clCliff);
}
else if((maxH - minH > 2.5 && minH > -5) || (maxH-minAdjHeight > 2.9 && minH > 0) ) {
if(minH < -1) t = tCliff;
else if(minH < .5) t = tBeachCliff;
else t = [tDirtCliff, tGrassCliff, tGrassCliff, tGrassRock, tCliff];
addToClass(ix, iy, clCliff);
}
// forests
if(maxH - minH < 1 && minH > 1) {
forestNoise = (noise6.eval(x,y) + 0.5*noise7.eval(x,y)) / 1.5 * pn;
forestNoise -= 0.59;
if(forestNoise > 0) {
if(minH > 5) {
typeNoise = noise10.eval(x,y);
if(typeNoise < .43 && forestNoise < .05) t = tPoplarForest;
else if(typeNoise < .63) t = tMainForest;
else t = tPineForest;
addToClass(ix, iy, clForest);
}
else if(minH < 3) {
t = tPalmForest;
addToClass(ix, iy, clForest);
}
}
}
// grass variations
if(t==tGrass)
{
grassNoise = (noise8.eval(x,y) + .6*noise9.eval(x,y)) / 1.6;
if(grassNoise < .3) {
t = (maxH - minH > 1.2) ? tDirtCliff : tDirt;
}
else if(grassNoise < .34) {
t = (maxH - minH > 1.2) ? tGrassCliff : tGrassDry;
if(maxH - minH < .5 && randFloat() < .03) {
placeObject(oGrassDry, 0, ix+randFloat(), iy+randFloat(), randFloat()*2*Math.PI);
}
}
else if(grassNoise > .61) {
t = (maxH - minH > 1.2) ? tGrassRock : tGrassShrubs;
}
else {
if(maxH - minH < .5 && randFloat() < .05) {
placeObject(oGrass, 0, ix+randFloat(), iy+randFloat(), randFloat()*2*Math.PI);
}
}
}
placeTerrain(ix, iy, t);
}
}
println("Placing object groups...");
// create settlements
group = new SimpleGroup([new SimpleObject("special_settlement", 1,1, 0,0)], true, clSettlement);
createObjectGroups(group, 0,
avoidClasses(clWater, 5, clForest, 4, clPlayer, 25, clCliff, 4, clSettlement, 35),
2 * NUM_PLAYERS, 50
);
// create straggler trees
trees = [oCarob, oBeech, oLombardyPoplar, oLombardyPoplar, oPine]
for(t in trees) {
group = new SimpleGroup([new SimpleObject(trees[t], 1,1, 0,1)], true, clForest);
createObjectGroups(group, 0,
avoidClasses(clWater, 5, clCliff, 0, clForest, 1, clSettlement, 4, clPlayer, 15),
SIZE*SIZE/7000, 50
);
}
// create cypresses
group = new SimpleGroup([
new SimpleObject("flora/trees/cypress2.xml", 1,3, 0,3),
new SimpleObject("flora/trees/cypress1.xml", 0,2, 0,2)]);
createObjectGroups(group, 0,
avoidClasses(clWater, 4, clCliff, 2, clForest, 1, clSettlement, 4, clPlayer, 15),
SIZE*SIZE/3500, 50
);
// create bushes
group = new SimpleGroup([
new SimpleObject(oBushSmall, 0,2, 0,2),
new SimpleObject(oBushSmallDry, 0,2, 0,2),
new SimpleObject(oBushMed, 0,1, 0,2),
new SimpleObject(oBushMedDry, 0,1, 0,2)]);
createObjectGroups(group, 0,
avoidClasses(clWater, 4, clCliff, 2),
SIZE*SIZE/1800, 50
);
// create rocks
group = new SimpleGroup([
new SimpleObject(oRockSmall, 0,3, 0,2),
new SimpleObject(oRockMed, 0,2, 0,2),
new SimpleObject(oRockLarge, 0,1, 0,2)]);
createObjectGroups(group, 0,
avoidClasses(clWater, 0, clCliff, 0),
SIZE*SIZE/1800, 50
);
// create stone
group = new SimpleGroup([new SimpleObject(oStone, 2,3, 0,2)], true, clStone);
createObjectGroups(group, 0,
[avoidClasses(clWater, 0, clForest, 0, clPlayer, 20, clStone, 15, clSettlement, 4),
new BorderTileClassConstraint(clCliff, 0, 5)],
3 * NUM_PLAYERS, 100
);
// create metal
group = new SimpleGroup([new SimpleObject(oMetal, 2,3, 0,2)], true, clMetal);
createObjectGroups(group, 0,
[avoidClasses(clWater, 0, clForest, 0, clPlayer, 20, clMetal, 15, clStone, 5, clSettlement, 4),
new BorderTileClassConstraint(clCliff, 0, 5)],
3 * NUM_PLAYERS, 100
);
// create sheep
group = new SimpleGroup([new SimpleObject(oSheep, 2,4, 0,2)], true, clFood);
createObjectGroups(group, 0,
avoidClasses(clWater, 5, clForest, 1, clCliff, 1, clPlayer, 20,
clMetal, 2, clStone, 2, clFood, 8, clSettlement, 4),
3 * NUM_PLAYERS, 100
);
// create berry bushes
group = new SimpleGroup([new SimpleObject(oBerryBush, 5,7, 0,3)], true, clFood);
createObjectGroups(group, 0,
avoidClasses(clWater, 5, clForest, 1, clCliff, 1, clPlayer, 20,
clMetal, 2, clStone, 2, clFood, 8, clSettlement, 4),
1.5 * NUM_PLAYERS, 100
);
RMS.LoadLibrary("rmgen");
const WATER_WIDTH = 0.2;
// terrain textures
const tOceanDepths = "medit_sea_depths";
const tOceanRockDeep = "medit_sea_coral_deep";
const tOceanRockShallow = "medit_rocks_wet";
const tOceanCoral = "medit_sea_coral_plants";
const tBeachWet = "medit_sand_wet";
const tBeachDry = "medit_sand";
const tBeachGrass = "beach_medit_grass_50";
const tBeachCliff = "cliff_medit_beach";
const tGrassDry = ["medit_grass_field_brown", "medit_grass_field_dry", "medit_grass_field_b"];
const tGrass = ["medit_grass_field", "medit_grass_field_a", "medit_grass_flowers"];
const tGrassLush = ["grass_temperate_dry_tufts", "medit_grass_flowers"];
const tGrassShrubs = ["medit_grass_shrubs", "medit_grass_flowers"];
const tGrassRock = ["medit_rocks_grass"];
const tDirt = "medit_dirt";
const tDirtGrass = "medit_dirt_b";
const tDirtCliff = "medit_cliff_italia";
const tGrassCliff = "medit_cliff_italia_grass";
const tCliff = ["medit_cliff_italia", "medit_cliff_italia", "medit_cliff_italia_grass"];
const tForestFloor = "forestfloor_medit_dirt";
// gaia entities
const oBeech = "gaia/flora_tree_euro_beech";
const oBerryBush = "gaia/flora_bush_berry";
const oCarob = "gaia/flora_tree_carob";
const oCypress1 = "gaia/flora_tree_cypress";
const oCypress2 = "gaia/flora_tree_cypress";
const oLombardyPoplar = "gaia/flora_tree_poplar_lombardy";
const oOak = "gaia/flora_tree_oak";
const oPalm = "gaia/flora_tree_medit_fan_palm";
const oPine = "gaia/flora_tree_aleppo_pine";
const oPoplar = "gaia/flora_tree_poplar";
const oSheep = "gaia/fauna_sheep";
const oStone = "gaia/geology_stone_greek";
const oMetal = "gaia/geology_metal_greek";
// decorative props
const aBushLargeDry = "actor|props/flora/bush_medit_la_dry.xml";
const aBushLarge = "actor|props/flora/bush_medit_la.xml";
const aBushMedDry = "actor|props/flora/bush_medit_me_dry.xml";
const aBushMed = "actor|props/flora/bush_medit_me.xml";
const aBushSmall = "actor|props/flora/bush_medit_sm.xml";
const aBushSmallDry = "actor|props/flora/bush_medit_sm_dry.xml";
const aGrass = "actor|props/flora/grass_medit_field.xml";
const aGrassDry = "actor|props/flora/grass_soft_dry_small.xml";
const aRockLarge = "actor|geology/stone_granite_greek_large.xml";
const aRockMed = "actor|geology/stone_granite_greek_med.xml";
const aRockSmall = "actor|geology/stone_granite_greek_small.xml";
const aWaterLog = "actor|props/flora/water_log.xml";
// terrain + entity (for painting)
var pPalmForest = tForestFloor+TERRAIN_SEPARATOR+oPalm;
var pPineForest = tForestFloor+TERRAIN_SEPARATOR+oPine;
var pCarobForest = tForestFloor+TERRAIN_SEPARATOR+oCarob;
var pBeechForest = tForestFloor+TERRAIN_SEPARATOR+oBeech;
var pPoplarForest = tForestFloor+TERRAIN_SEPARATOR+oLombardyPoplar;
var tPalmForest = [pPalmForest, tGrass];
var tPineForest = [pPineForest, tGrass];
var tMainForest = [pCarobForest, pBeechForest, tGrass, tGrass];
var tPoplarForest = [pPoplarForest, tGrass];
// initialize map
log("Initializing map...");
InitMap();
var numPlayers = getNumPlayers();
var mapSize = getMapSize();
// Create classes
var clWater = createTileClass();
var clCliff = createTileClass();
var clForest = createTileClass();
var clMetal = createTileClass();
var clStone = createTileClass();
var clFood = createTileClass();
var clPlayer = createTileClass();
var clBaseResource = createTileClass();
// Place players
log("Placing players...");
var playerX = new Array(numPlayers+1);
var playerY = new Array(numPlayers+1);
var numLeftPlayers = floor(numPlayers/2);
for (var i=1; i <= numLeftPlayers; i++)
{
playerX[i] = 0.28 + (2*randFloat()-1)*0.01;
playerY[i] = (0.5+i-1)/numLeftPlayers + (2*randFloat()-1)*0.01;
}
for (var i=numLeftPlayers+1; i <= numPlayers; i++)
{
playerX[i] = 0.72 + (2*randFloat()-1)*0.01;
playerY[i] = (0.5+i-numLeftPlayers-1)/numLeftPlayers + (2*randFloat()-1)*0.01;
}
for (var i=1; i <= numPlayers; i++)
{
log("Creating base for player " + i + "...");
// get fractional locations in tiles
var ix = round(fractionToTiles(playerX[i]));
var iy = round(fractionToTiles(playerY[i]));
addToClass(ix, iy, clPlayer);
// create TC and starting units
// TODO: Get civ specific starting units
var civ = getCivCode(i - 1);
placeObject("structures/"+civ + "_civil_centre", i, ix, iy, PI*3/4);
var group = new SimpleGroup(
[new SimpleObject("units/"+civ+"_support_female_citizen", 3,3, 5,5)],
true, null, ix, iy
);
createObjectGroup(group, i);
// create starting berry bushes
var bbAngle = randFloat()*2*PI;
var bbDist = 9;
var bbX = round(ix + bbDist * cos(bbAngle));
var bbY = round(iy + bbDist * sin(bbAngle));
group = new SimpleGroup(
[new SimpleObject(oBerryBush, 5,5, 0,2)],
true, clBaseResource, bbX, bbY
);
createObjectGroup(group, 0);
// create starting mines
var mAngle = bbAngle;
while(abs(mAngle - bbAngle) < PI/3) {
mAngle = randFloat()*2*PI;
}
var mDist = 9;
var mX = round(ix + mDist * cos(mAngle));
var mY = round(iy + mDist * sin(mAngle));
group = new SimpleGroup(
[new SimpleObject(oStone, 2,2, 0,3),
new SimpleObject(oMetal, 2,2, 0,3)],
true, clBaseResource, mX, mY
);
createObjectGroup(group, 0);
// create starting straggler trees
group = new SimpleGroup(
[new SimpleObject(oPalm, 3,3, 7,10)],
true, clBaseResource, ix, iy
);
createObjectGroup(group, 0, avoidClasses(clBaseResource,2));
}
function distanceToPlayers(x, y)
{
var r = 10000;
for (var i=1; i <= numPlayers; i++)
{
var dx = x - playerX[i];
var dy = y - playerY[i];
r = min(r, dx*dx + dy*dy);
}
return sqrt(r);
}
function playerNearness(x, y)
{
var d = fractionToTiles(distanceToPlayers(x,y));
if (d < 13)
return 0;
else if (d < 19)
return (d-13)/(19-13);
else
return 1;
}
// Paint elevation
log("Painting elevation...");
var noise0 = new Noise2D(4 * mapSize/128);
var noise1 = new Noise2D(8 * mapSize/128);
var noise2 = new Noise2D(15 * mapSize/128);
var noise2a = new Noise2D(20 * mapSize/128);
var noise2b = new Noise2D(35 * mapSize/128);
var noise3 = new Noise2D(4 * mapSize/128);
var noise4 = new Noise2D(6 * mapSize/128);
var noise5 = new Noise2D(11 * mapSize/128);
for (var ix=0; ix<=mapSize; ix++)
{
for (var iy=0; iy<=mapSize; iy++)
{
var x = ix / (mapSize + 1.0);
var y = iy / (mapSize + 1.0);
var pn = playerNearness(x, y);
var h = 0;
var distToWater = 0;
h = 32 * (x - 0.5);
// add the rough shape of the water
if (x < WATER_WIDTH)
{
h = max(-16.0, -28.0*(WATER_WIDTH-x)/WATER_WIDTH);
}
else if (x > 1.0-WATER_WIDTH)
{
h = max(-16.0, -28.0*(x-(1.0-WATER_WIDTH))/WATER_WIDTH);
}
else
{
distToWater = (0.5 - WATER_WIDTH - abs(x-0.5));
var u = 1 - abs(x-0.5) / (0.5-WATER_WIDTH);
h = 12*u;
}
// add some base noise
var baseNoise = 16*noise0.get(x,y) + 8*noise1.get(x,y) + 4*noise2.get(x,y) - (16+8+4)/2;
if ( baseNoise < 0 )
{
baseNoise *= pn;
baseNoise *= max(0.1, distToWater / (0.5-WATER_WIDTH));
}
var oldH = h;
h += baseNoise;
// add some higher-frequency noise on land
if ( oldH > 0 )
{
h += (0.4*noise2a.get(x,y) + 0.2*noise2b.get(x,y)) * min(oldH/10.0, 1.0);
}
// create cliff noise
if ( h > -10 )
{
var cliffNoise = (noise3.get(x,y) + 0.5*noise4.get(x,y)) / 1.5;
if (h < 1)
{
var u = 1 - 0.3*((h-1)/-10);
cliffNoise *= u;
}
cliffNoise += 0.05 * distToWater / (0.5 - WATER_WIDTH);
if (cliffNoise > 0.6)
{
var u = 0.8 * (cliffNoise - 0.6);
cliffNoise += u * noise5.get(x,y);
cliffNoise /= (1 + u);
}
cliffNoise -= 0.59;
cliffNoise *= pn;
if (cliffNoise > 0)
{
h += 19 * min(cliffNoise, 0.045) / 0.045;
}
}
// set the height
setHeight(ix, iy, h);
}
}
// Paint base terrain
log("Painting terrain...");
var noise6 = new Noise2D(10 * mapSize/128);
var noise7 = new Noise2D(20 * mapSize/128);
var noise8 = new Noise2D(13 * mapSize/128);
var noise9 = new Noise2D(26 * mapSize/128);
var noise10 = new Noise2D(50 * mapSize/128);
for (var ix=0; ix<mapSize; ix++)
{
for (var iy=0; iy<mapSize; iy++)
{
var x = ix / (mapSize + 1.0);
var y = iy / (mapSize + 1.0);
var pn = playerNearness(x, y);
// get heights of surrounding vertices
var h00 = getHeight(ix, iy);
var h01 = getHeight(ix, iy+1);
var h10 = getHeight(ix+1, iy);
var h11 = getHeight(ix+1, iy+1);
// find min and max height
var maxH = Math.max(h00, h01, h10, h11);
var minH = Math.min(h00, h01, h10, h11);
// figure out if we're at the top of a cliff using min adjacent height
var minAdjHeight = minH;
if (maxH > 15)
{
var maxNx = min(ix+2, mapSize);
var maxNy = min(iy+2, mapSize);
for (var nx=max(ix-1, 0); nx <= maxNx; nx++)
{
for (var ny=max(iy-1, 0); ny <= maxNy; ny++)
{
minAdjHeight = min(minAdjHeight, getHeight(nx, ny));
}
}
}
// choose a terrain based on elevation
var t = tGrass;
// water
if (maxH < -12)
{
t = tOceanDepths;
}
else if (maxH < -8.8)
{
t = tOceanRockDeep;
}
else if (maxH < -4.7)
{
t = tOceanCoral;
}
else if (maxH < -2.8)
{
t = tOceanRockShallow;
}
else if (maxH < 0.9 && minH < 0.35)
{
t = tBeachWet;
}
else if (maxH < 1.5 && minH < 0.9)
{
t = tBeachDry;
}
else if (maxH < 2.3 && minH < 1.3)
{
t = tBeachGrass;
}
if (minH < 0)
{
addToClass(ix, iy, clWater);
}
// cliffs
if (maxH - minH > 2.9 && minH > -7)
{
t = tCliff;
addToClass(ix, iy, clCliff);
}
else if ((maxH - minH > 2.5 && minH > -5) || (maxH-minAdjHeight > 2.9 && minH > 0) )
{
if (minH < -1)
t = tCliff;
else if (minH < 0.5)
t = tBeachCliff;
else
t = [tDirtCliff, tGrassCliff, tGrassCliff, tGrassRock, tCliff];
addToClass(ix, iy, clCliff);
}
// forests
if (maxH - minH < 1 && minH > 1)
{
var forestNoise = (noise6.get(x,y) + 0.5*noise7.get(x,y)) / 1.5 * pn;
forestNoise -= 0.59;
if (forestNoise > 0)
{
if (minH > 5)
{
var typeNoise = noise10.get(x,y);
if (typeNoise < 0.43 && forestNoise < 0.05)
t = tPoplarForest;
else if (typeNoise < 0.63)
t = tMainForest;
else
t = tPineForest;
addToClass(ix, iy, clForest);
}
else if (minH < 3)
{
t = tPalmForest;
addToClass(ix, iy, clForest);
}
}
}
// grass variations
if (t == tGrass)
{
var grassNoise = (noise8.get(x,y) + 0.6*noise9.get(x,y)) / 1.6;
if (grassNoise < 0.3)
{
t = (maxH - minH > 1.2) ? tDirtCliff : tDirt;
}
else if (grassNoise < 0.34)
{
t = (maxH - minH > 1.2) ? tGrassCliff : tGrassDry;
if (maxH - minH < 0.5 && randFloat() < 0.03)
{
placeObject(aGrassDry, 0, ix+randFloat(), iy+randFloat(), randFloat()*2*PI);
}
}
else if (grassNoise > 0.61)
{
t = ((maxH - minH) > 1.2 ? tGrassRock : tGrassShrubs);
}
else
{
if ((maxH - minH) < 0.5 && randFloat() < 0.05)
{
placeObject(aGrass, 0, ix+randFloat(), iy+randFloat(), randFloat()*2*PI);
}
}
}
placeTerrain(ix, iy, t);
}
}
log("Placing straggler trees...");
// create straggler trees
var trees = [oCarob, oBeech, oLombardyPoplar, oLombardyPoplar, oPine];
for (var t in trees)
{
group = new SimpleGroup([new SimpleObject(trees[t], 1,1, 0,1)], true, clForest);
createObjectGroups(group, 0,
avoidClasses(clWater, 5, clCliff, 0, clForest, 1, clPlayer, 15),
mapSize*mapSize/7000, 50
);
}
log("Placing cypress trees...");
// create cypresses
group = new SimpleGroup(
[new SimpleObject(oCypress2, 1,3, 0,3),
new SimpleObject(oCypress1, 0,2, 0,2)]
);
createObjectGroups(group, 0,
avoidClasses(clWater, 4, clCliff, 2, clForest, 1, clPlayer, 15),
mapSize*mapSize/3500, 50
);
log("Placing bushes...");
// create bushes
group = new SimpleGroup(
[new SimpleObject(aBushSmall, 0,2, 0,2), new SimpleObject(aBushSmallDry, 0,2, 0,2),
new SimpleObject(aBushMed, 0,1, 0,2), new SimpleObject(aBushMedDry, 0,1, 0,2)]
);
createObjectGroups(group, 0,
avoidClasses(clWater, 4, clCliff, 2),
mapSize*mapSize/1800, 50
);
log("Placing rocks...");
// create rocks
group = new SimpleGroup(
[new SimpleObject(aRockSmall, 0,3, 0,2), new SimpleObject(aRockMed, 0,2, 0,2),
new SimpleObject(aRockLarge, 0,1, 0,2)]
);
createObjectGroups(group, 0,
avoidClasses(clWater, 0, clCliff, 0),
mapSize*mapSize/1800, 50
);
log("Placing stone mines...");
// create stone
group = new SimpleGroup([new SimpleObject(oStone, 2,3, 0,2)], true, clStone);
createObjectGroups(group, 0,
[avoidClasses(clWater, 0, clForest, 0, clPlayer, 20, clStone, 15),
new BorderTileClassConstraint(clCliff, 0, 5)],
3 * numPlayers, 100
);
log("Placing metal mines...");
// create metal
group = new SimpleGroup([new SimpleObject(oMetal, 2,3, 0,2)], true, clMetal);
createObjectGroups(group, 0,
[avoidClasses(clWater, 0, clForest, 0, clPlayer, 20, clMetal, 15, clStone, 5),
new BorderTileClassConstraint(clCliff, 0, 5)],
3 * numPlayers, 100
);
log("Placing sheep...");
// create sheep
group = new SimpleGroup([new SimpleObject(oSheep, 2,4, 0,2)], true, clFood);
createObjectGroups(group, 0,
avoidClasses(clWater, 5, clForest, 1, clCliff, 1, clPlayer, 20, clMetal, 2, clStone, 2, clFood, 8),
3 * numPlayers, 100
);
log("Placing berry bushes...");
// create berry bushes
group = new SimpleGroup([new SimpleObject(oBerryBush, 5,7, 0,3)], true, clFood);
createObjectGroups(group, 0,
avoidClasses(clWater, 5, clForest, 1, clCliff, 1, clPlayer, 20, clMetal, 2, clStone, 2, clFood, 8),
1.5 * numPlayers, 100
);
// Export map data
ExportMap();

View File

@ -0,0 +1,10 @@
{
"settings" : {
"Name" : "Latium",
"Script" : "latium.js",
"Description" : "The Italian peninsula",
"BaseTerrain" : ["medit_grass_field", "medit_grass_field_a", "medit_grass_flowers"],
"BaseHeight" : 0,
"XXXXXX" : "Optionally define other things here, like we would for a scenario"
}
}

View File

@ -1,280 +1,277 @@
// constants
const SIZE = 208;
const NUM_PLAYERS = 4;
const tSand = "desert_rough";
const tDunes = "desert_wave";
const tFineSand = "desert_sahara";
const tCliff = "cliff_desert";
const tForest = "grass_sand_75|flora/trees/palm_b.xml";
const tGrassSand75 = "grass_sand_75";
const tGrassSand50 = "grass_sand_50";
const tGrassSand25 = "grass_sand_25_2";
const tDirt = "dirt_hard";
const tDirtCracks = "dirt_cracks";
const tShore = "sand";
const tWater = "water_2";
const tWaterDeep = "water_3";
const oTree = "flora/trees/palm_b.xml";
const oBerryBush = "flora_bush_berry";
const oBush = "props/flora/bush_dry_a.xml";
const oSheep = "fauna_sheep";
const oDeer = "fauna_deer";
const oMine = "geology_stone_light";
const oDecorativeRock = "geology/gray1.xml";
// some utility functions to save typing
function paintClass(cl) {
return new TileClassPainter(cl);
}
function avoidClasses(/*class1, dist1, class2, dist2, etc*/) {
var ar = new Array(arguments.length/2);
for(var i=0; i<arguments.length/2; i++) {
ar[i] = new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1]);
}
return ar;
}
// initialize map
println("Initializing map...");
init(SIZE, tSand, 10);
// create tile classes
clPlayer = createTileClass();
clHill1 = createTileClass();
clHill2 = createTileClass();
clHill3 = createTileClass();
clForest = createTileClass();
clWater = createTileClass();
clPatch = createTileClass();
clRock = createTileClass();
clFood = createTileClass();
clBaseResource = createTileClass();
// place players
playerX = new Array(NUM_PLAYERS);
playerY = new Array(NUM_PLAYERS);
playerAngle = new Array(NUM_PLAYERS);
startAngle = randFloat() * 2 * PI;
for(i=0; i<NUM_PLAYERS; i++) {
playerAngle[i] = startAngle + i*2*PI/NUM_PLAYERS;
playerX[i] = 0.5 + 0.39*cos(playerAngle[i]);
playerY[i] = 0.5 + 0.39*sin(playerAngle[i]);
}
for(i=0; i<NUM_PLAYERS; i++) {
println("Creating base for player " + i + "...");
// some constants
radius = 20;
cliffRadius = 2;
elevation = 30;
// get the x and y in tiles
fx = fractionToTiles(playerX[i]);
fy = fractionToTiles(playerY[i]);
ix = round(fx);
iy = round(fy);
// calculate size based on the radius
size = PI * radius * radius;
// create the hill
placer = new ClumpPlacer(size, 0.9, 0.5, 0, ix, iy);
createArea(placer, paintClass(clPlayer), null);
// create the central road patch
placer = new ClumpPlacer(PI*2*2, 0.6, 0.3, 0.5, ix, iy);
painter = new TerrainPainter(tDirt);
createArea(placer, painter, null);
// create the TC and the villies
group = new SimpleGroup(
[ // elements (type, count, distance)
new SimpleObject("hele_civil_centre", 1,1, 0,0),
new SimpleObject("hele_infantry_spearman_b", 3,3, 5,5)
],
true, null, ix, iy
);
createObjectGroup(group, i);
// create berry bushes
bbAngle = randFloat()*2*PI;
bbDist = 10;
bbX = round(fx + bbDist * cos(bbAngle));
bbY = round(fy + bbDist * sin(bbAngle));
group = new SimpleGroup(
[new SimpleObject(oSheep, 5,5, 0,2)],
true, clBaseResource, bbX, bbY
);
createObjectGroup(group, 0);
// create mines
mAngle = bbAngle;
while(abs(mAngle - bbAngle) < PI/3) {
mAngle = randFloat()*2*PI;
}
mDist = 12;
mX = round(fx + mDist * cos(mAngle));
mY = round(fy + mDist * sin(mAngle));
group = new SimpleGroup(
[new SimpleObject(oMine, 3,3, 0,2)],
true, clBaseResource, mX, mY
);
createObjectGroup(group, 0);
// create starting straggler trees
group = new SimpleGroup(
[new SimpleObject(oTree, 2,2, 6,12)],
true, null, ix, iy
);
createObjectGroup(group, 0, avoidClasses(clBaseResource,1));
}
// create patches
println("Creating sand patches...");
placer = new ClumpPlacer(30, 0.2, 0.1, 0);
painter = new LayeredPainter([1], [[tSand, tFineSand], tFineSand]);
createAreas(placer, [painter, paintClass(clPatch)],
avoidClasses(clPatch, 5),
(SIZE*SIZE)/600
);
println("Creating dirt patches...");
placer = new ClumpPlacer(10, 0.2, 0.1, 0);
painter = new TerrainPainter([tSand, tDirt]);
createAreas(placer, [painter, paintClass(clPatch)],
avoidClasses(clPatch, 5),
(SIZE*SIZE)/600
);
// create the oasis
println("Creating water...");
placer = new ClumpPlacer(1200, 0.6, 0.1, 0, SIZE/2, SIZE/2);
painter = new LayeredPainter([6,1], [[tSand, tForest], tShore, tWaterDeep]);
elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, -10, 5);
createArea(placer, [painter, elevationPainter, paintClass(clForest)], null);
// create hills
println("Creating level 1 hills...");
placer = new ClumpPlacer(150, 0.25, 0.1, 0.3);
terrainPainter = new LayeredPainter(
[1], // widths
[tCliff, tSand] // terrains
);
elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, 16, 1);
createAreas(placer, [terrainPainter, elevationPainter, paintClass(clHill1)],
avoidClasses(clForest, 2, clPlayer, 0, clHill1, 16),
(SIZE*SIZE)/3800, 100
);
println("Creating small level 1 hills...");
placer = new ClumpPlacer(60, 0.25, 0.1, 0.3);
terrainPainter = new LayeredPainter(
[1], // widths
[tCliff, tSand] // terrains
);
elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, 16, 1);
createAreas(placer, [terrainPainter, elevationPainter, paintClass(clHill1)],
avoidClasses(clForest, 2, clPlayer, 0, clHill1, 3),
(SIZE*SIZE)/2800, 100
);
println("Creating level 2 hills...");
placer = new ClumpPlacer(60, 0.2, 0.1, 0.9);
terrainPainter = new LayeredPainter(
[1], // widths
[tCliff, tSand] // terrains
);
elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, 16, 1);
createAreas(placer, [terrainPainter, elevationPainter, paintClass(clHill2)],
[avoidClasses(clHill2, 1), new StayInTileClassConstraint(clHill1, 0)],
(SIZE*SIZE)/2800, 200
);
println("Creating level 3 hills...");
placer = new ClumpPlacer(25, 0.2, 0.1, 0.9);
terrainPainter = new LayeredPainter(
[1], // widths
[tCliff, tSand] // terrains
);
elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, 16, 1);
createAreas(placer, [terrainPainter, elevationPainter, paintClass(clHill3)],
[avoidClasses(clHill3, 1), new StayInTileClassConstraint(clHill2, 0)],
(SIZE*SIZE)/9000, 300
);
// create forests
println("Creating forests...");
placer = new ClumpPlacer(25, 0.15, 0.1, 0.3);
painter = new TerrainPainter([tSand, tForest]);
createAreas(placer, [painter, paintClass(clForest)],
avoidClasses(clWater, 0, clPlayer, 1, clForest, 20, clHill1, 0),
(SIZE*SIZE)/4000, 50
);
// create mines
println("Creating mines...");
group = new SimpleGroup([new SimpleObject(oMine, 4,6, 0,2)], true, clRock);
createObjectGroups(group, 0,
[avoidClasses(clWater, 2, clForest, 2, clPlayer, 0, clRock, 13),
new BorderTileClassConstraint(clHill1, 0, 4)],
(SIZE*SIZE)/4000, 100
);
// create decorative rocks for hills
println("Creating decorative rocks...");
group = new SimpleGroup([new SimpleObject(oDecorativeRock, 1,1, 0,0)], true);
createObjectGroups(group, 0,
new BorderTileClassConstraint(clHill1, 0, 3),
(SIZE*SIZE)/2000, 100
);
// create deer
println("Creating deer...");
group = new SimpleGroup([new SimpleObject(oDeer, 5,7, 0,4)], true, clFood);
createObjectGroups(group, 0,
avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill1, 0, clFood, 25),
(SIZE*SIZE)/5000, 50
);
// create sheep
println("Creating sheep...");
group = new SimpleGroup([new SimpleObject(oSheep, 1,3, 0,2)], true, clFood);
createObjectGroups(group, 0,
avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill1, 0, clFood, 15),
(SIZE*SIZE)/5000, 50
);
// create straggler trees
println("Creating straggler trees...");
group = new SimpleGroup([new SimpleObject(oTree, 1,1, 0,0)], true);
createObjectGroups(group, 0,
avoidClasses(clWater, 0, clForest, 0, clHill1, 0, clPlayer, 0),
SIZE*SIZE/1500
);
// create bushes
println("Creating bushes...");
group = new SimpleGroup([new SimpleObject(oBush, 2,3, 0,2)]);
createObjectGroups(group, 0,
avoidClasses(clWater, 3, clHill1, 0, clPlayer, 0, clForest, 0),
SIZE*SIZE/1000
);
// create bushes
println("Creating more decorative rocks...");
group = new SimpleGroup([new SimpleObject(oDecorativeRock, 1,2, 0,2)]);
createObjectGroups(group, 0,
avoidClasses(clWater, 3, clHill1, 0, clPlayer, 0, clForest, 0),
SIZE*SIZE/1000
);
RMS.LoadLibrary("rmgen");
// terrain textures
const tSand = "desert_dirt_rough";
const tDunes = "desert_sand_dunes_100";
const tFineSand = "desert_sand_smooth";
const tCliff = "desert_cliff_badlands";
const tGrassSand75 = "desert_grass_a";
const tGrassSand50 = "desert_grass_a_sand";
const tGrassSand25 = "desert_grass_a_stones";
const tDirt = "desert_dirt_rough";
const tDirtCracks = "desert_dirt_cracks";
const tShore = "desert_sand_wet";
const tWater = "desert_shore_stones";
const tWaterDeep = "desert_shore_stones_wet";
// gaia entities
const oBerryBush = "gaia/flora_bush_berry";
const oSheep = "gaia/fauna_sheep";
const oDeer = "gaia/fauna_deer";
const oMine = "gaia/geology_stone_desert_small";
const oTree = "gaia/flora_tree_medit_fan_palm";
// decorative props
const aBush = "actor|props/flora/bush_dry_a.xml";
const aDecorativeRock = "actor|geology/gray1.xml";
// terrain + entity (for painting)
var pForest = tGrassSand75 + TERRAIN_SEPARATOR + oTree;
// initialize map
log("Initializing map...");
InitMap();
var numPlayers = getNumPlayers();
var mapSize = getMapSize();
// create tile classes
var clPlayer = createTileClass();
var clHill1 = createTileClass();
var clHill2 = createTileClass();
var clHill3 = createTileClass();
var clForest = createTileClass();
var clWater = createTileClass();
var clPatch = createTileClass();
var clRock = createTileClass();
var clFood = createTileClass();
var clBaseResource = createTileClass();
// place players
var playerX = new Array(numPlayers);
var playerY = new Array(numPlayers);
var playerAngle = new Array(numPlayers);
var startAngle = randFloat() * 2 * PI;
for (var i=0; i < numPlayers; i++)
{
playerAngle[i] = startAngle + i*2*PI/numPlayers;
playerX[i] = 0.5 + 0.39*cos(playerAngle[i]);
playerY[i] = 0.5 + 0.39*sin(playerAngle[i]);
}
for (var i=0; i < numPlayers; i++)
{
log("Creating base for player " + (i + 1) + "...");
// some constants
var radius = 20;
// get the x and y in tiles
var fx = fractionToTiles(playerX[i]);
var fy = fractionToTiles(playerY[i]);
var ix = round(fx);
var iy = round(fy);
// calculate size based on the radius
var size = PI * radius * radius;
// create the hill
var placer = new ClumpPlacer(size, 0.9, 0.5, 0, ix, iy);
createArea(placer, paintClass(clPlayer), null);
// create the central road patch
placer = new ClumpPlacer(PI*2*2, 0.6, 0.3, 0.5, ix, iy);
var painter = new TerrainPainter(tDirt);
createArea(placer, painter, null);
// create the TC and citizens
var civ = getCivCode(i);
var group = new SimpleGroup(
[ // elements (type, count, distance)
new SimpleObject("structures/"+civ+"_civil_centre", 1,1, 0,0),
new SimpleObject("units/"+civ+"_support_female_citizen", 3,3, 5,5)
],
true, null, ix, iy
);
createObjectGroup(group, i+1);
// create berry bushes
var bbAngle = randFloat()*2*PI;
var bbDist = 10;
var bbX = round(fx + bbDist * cos(bbAngle));
var bbY = round(fy + bbDist * sin(bbAngle));
group = new SimpleGroup(
[new SimpleObject(oSheep, 5,5, 0,2)],
true, clBaseResource, bbX, bbY
);
createObjectGroup(group, 0);
// create mines
var mAngle = bbAngle;
while(abs(mAngle - bbAngle) < PI/3) {
mAngle = randFloat()*2*PI;
}
var mDist = 12;
var mX = round(fx + mDist * cos(mAngle));
var mY = round(fy + mDist * sin(mAngle));
group = new SimpleGroup(
[new SimpleObject(oMine, 3,3, 0,2)],
true, clBaseResource, mX, mY
);
createObjectGroup(group, 0);
// create starting straggler trees
group = new SimpleGroup(
[new SimpleObject(oTree, 2,2, 6,12)],
true, null, ix, iy
);
createObjectGroup(group, 0, avoidClasses(clBaseResource,1));
}
// create patches
log("Creating sand patches...");
var placer = new ClumpPlacer(30, 0.2, 0.1, 0);
var painter = new LayeredPainter([[tSand, tFineSand], tFineSand], [1]);
createAreas(placer, [painter, paintClass(clPatch)],
avoidClasses(clPatch, 5),
(mapSize*mapSize)/600
);
log("Creating dirt patches...");
placer = new ClumpPlacer(10, 0.2, 0.1, 0);
painter = new TerrainPainter([tSand, tDirt]);
createAreas(placer, [painter, paintClass(clPatch)],
avoidClasses(clPatch, 5),
(mapSize*mapSize)/600
);
// create the oasis
log("Creating water...");
placer = new ClumpPlacer(1200, 0.6, 0.1, 0, mapSize/2, mapSize/2);
painter = new LayeredPainter([[tSand, pForest], tShore, tWaterDeep], [6,1]);
elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, -11, 5);
createArea(placer, [painter, elevationPainter, paintClass(clForest)], null);
// create hills
log("Creating level 1 hills...");
placer = new ClumpPlacer(150, 0.25, 0.1, 0.3);
var terrainPainter = new LayeredPainter(
[tCliff, tSand], // terrains
[1] // widths
);
var elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, 16, 1);
createAreas(placer, [terrainPainter, elevationPainter, paintClass(clHill1)],
avoidClasses(clForest, 2, clPlayer, 0, clHill1, 16),
(mapSize*mapSize)/3800, 100
);
log("Creating small level 1 hills...");
placer = new ClumpPlacer(60, 0.25, 0.1, 0.3);
terrainPainter = new LayeredPainter(
[tCliff, tSand], // terrains
[1] // widths
);
elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, 16, 1);
createAreas(placer, [terrainPainter, elevationPainter, paintClass(clHill1)],
avoidClasses(clForest, 2, clPlayer, 0, clHill1, 3),
(mapSize*mapSize)/2800, 100
);
log("Creating level 2 hills...");
placer = new ClumpPlacer(60, 0.2, 0.1, 0.9);
terrainPainter = new LayeredPainter(
[tCliff, tSand], // terrains
[1] // widths
);
elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, 16, 1);
createAreas(placer, [terrainPainter, elevationPainter, paintClass(clHill2)],
[avoidClasses(clHill2, 1), new StayInTileClassConstraint(clHill1, 0)],
(mapSize*mapSize)/2800, 200
);
log("Creating level 3 hills...");
placer = new ClumpPlacer(25, 0.2, 0.1, 0.9);
terrainPainter = new LayeredPainter(
[tCliff, tSand], // terrains
[1] // widths
);
elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, 16, 1);
createAreas(placer, [terrainPainter, elevationPainter, paintClass(clHill3)],
[avoidClasses(clHill3, 1), new StayInTileClassConstraint(clHill2, 0)],
(mapSize*mapSize)/9000, 300
);
// create forests
log("Creating forests...");
placer = new ClumpPlacer(25, 0.15, 0.1, 0.3);
painter = new TerrainPainter([tSand, pForest]);
createAreas(placer, [painter, paintClass(clForest)],
avoidClasses(clWater, 0, clPlayer, 1, clForest, 20, clHill1, 0),
(mapSize*mapSize)/4000, 50
);
// create mines
log("Creating mines...");
group = new SimpleGroup([new SimpleObject(oMine, 4,6, 0,2)], true, clRock);
createObjectGroups(group, 0,
[avoidClasses(clWater, 2, clForest, 2, clPlayer, 0, clRock, 13),
new BorderTileClassConstraint(clHill1, 0, 4)],
(mapSize*mapSize)/4000, 100
);
// create decorative rocks for hills
log("Creating decorative rocks...");
group = new SimpleGroup([new SimpleObject(aDecorativeRock, 1,1, 0,0)], true);
createObjectGroups(group, undefined,
new BorderTileClassConstraint(clHill1, 0, 3),
(mapSize*mapSize)/2000, 100
);
// create deer
log("Creating deer...");
group = new SimpleGroup([new SimpleObject(oDeer, 5,7, 0,4)], true, clFood);
createObjectGroups(group, 0,
avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill1, 0, clFood, 25),
(mapSize*mapSize)/5000, 50
);
// create sheep
log("Creating sheep...");
group = new SimpleGroup([new SimpleObject(oSheep, 1,3, 0,2)], true, clFood);
createObjectGroups(group, 0,
avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill1, 0, clFood, 15),
(mapSize*mapSize)/5000, 50
);
// create straggler trees
log("Creating straggler trees...");
group = new SimpleGroup([new SimpleObject(oTree, 1,1, 0,0)], true);
createObjectGroups(group, 0,
avoidClasses(clWater, 0, clForest, 0, clHill1, 0, clPlayer, 0),
mapSize*mapSize/1500
);
// create bushes
log("Creating bushes...");
group = new SimpleGroup([new SimpleObject(aBush, 2,3, 0,2)]);
createObjectGroups(group, undefined,
avoidClasses(clWater, 3, clHill1, 0, clPlayer, 0, clForest, 0),
mapSize*mapSize/1000
);
// create bushes
log("Creating more decorative rocks...");
group = new SimpleGroup([new SimpleObject(aDecorativeRock, 1,2, 0,2)]);
createObjectGroups(group, undefined,
avoidClasses(clWater, 3, clHill1, 0, clPlayer, 0, clForest, 0),
mapSize*mapSize/1000
);
// Export map data
ExportMap();

View File

@ -0,0 +1,10 @@
{
"settings" : {
"Name" : "Neareastern Badlands",
"Script" : "neareastern_badlands.js",
"Description" : "A jumbled maze of cliffs, canyons, and rugged terrain with an oasis in the center",
"BaseTerrain" : "desert_rough",
"BaseHeight" : 10,
"XXXXXX" : "Optionally define other things here, like we would for a scenario"
}
}

View File

@ -0,0 +1,60 @@
RMS.LoadLibrary("rmgen");
// initialize map
log("Initializing map...");
InitMap();
var numPlayers = getNumPlayers();
var mapSize = getMapSize();
// create tile classes
var clPlayer = createTileClass();
var clPath = createTileClass();
var clHill = createTileClass();
var clForest = createTileClass();
var clWater = createTileClass();
var clRock = createTileClass();
var clFood = createTileClass();
var clBaseResource = createTileClass();
// place players
var playerX = new Array(numPlayers);
var playerY = new Array(numPlayers);
var playerAngle = new Array(numPlayers);
var startAngle = randFloat() * 2 * PI;
for (var i=0; i < numPlayers; i++)
{
playerAngle[i] = startAngle + i*2*PI/numPlayers;
playerX[i] = 0.5 + 0.39*cos(playerAngle[i]);
playerY[i] = 0.5 + 0.39*sin(playerAngle[i]);
}
for (var i=0; i < numPlayers; i++)
{
log("Creating base for player " + (i + 1) + "...");
// get the x and y in tiles
var fx = fractionToTiles(playerX[i]);
var fy = fractionToTiles(playerY[i]);
var ix = round(fx);
var iy = round(fy);
// create the TC and citizens
var civ = getCivCode(i);
var group = new SimpleGroup(
[ // elements (type, count, distance)
new SimpleObject("structures/"+civ+"_civil_centre", 1,1, 0,0),
new SimpleObject("units/"+civ+"_support_female_citizen", 3,3, 5,5)
],
true, null, ix, iy
);
createObjectGroup(group, i+1);
}
// Export map data
ExportMap();

View File

@ -0,0 +1,10 @@
{
"settings" : {
"Name" : "New RMS Test",
"Script" : "new_rms_test.js",
"Description" : "A test of the new integrated random map generator!",
"BaseTerrain" : "grass1_spring",
"BaseHeight" : 0,
"XXXXXX" : "Optionally define other things here, like we would for a scenario"
}
}

View File

@ -0,0 +1,4 @@
function Area(points)
{
this.points = (points !== undefined ? points : []);
}

View File

@ -0,0 +1,99 @@
///////////////////////////////////////////////////////////////////////////
// NullConstraints: No constraint - always return true
///////////////////////////////////////////////////////////////////////////
function NullConstraint() {}
NullConstraint.prototype.allows = function(x, y)
{
return true;
};
///////////////////////////////////////////////////////////////////////////
// AndConstraints: Check multiple constraints
///////////////////////////////////////////////////////////////////////////
function AndConstraint(constraints)
{
this.constraints = constraints;
}
AndConstraint.prototype.allows = function(x, y)
{
for (var i=0; i < this.constraints.length; ++i)
{
if (!this.constraints[i].allows(x, y))
return false;
}
return true;
};
///////////////////////////////////////////////////////////////////////////
// AvoidAreaConstraint
///////////////////////////////////////////////////////////////////////////
function AvoidAreaConstraint(area)
{
this.area = area;
}
AvoidAreaConstraint.prototype.allows = function(x, y)
{
return g_Map.area[x][y] != this.area;
};
///////////////////////////////////////////////////////////////////////////
// AvoidTextureConstraint
///////////////////////////////////////////////////////////////////////////
function AvoidTextureConstraint(textureID)
{
this.textureID = textureID;
}
AvoidTextureConstraint.prototype.allows = function(x, y)
{
return g_Map.texture[x][y] != this.textureID;
};
///////////////////////////////////////////////////////////////////////////
// AvoidTileClassConstraint
///////////////////////////////////////////////////////////////////////////
function AvoidTileClassConstraint(tileClassID, distance)
{
this.tileClass = getTileClass(tileClassID);
this.distance = distance;
}
AvoidTileClassConstraint.prototype.allows = function(x, y)
{
return this.tileClass.countMembersInRadius(x, y, this.distance) == 0;
};
///////////////////////////////////////////////////////////////////////////
// StayInTileClassConstraint
///////////////////////////////////////////////////////////////////////////
function StayInTileClassConstraint(tileClassID, distance)
{
this.tileClass = getTileClass(tileClassID);
this.distance = distance;
}
StayInTileClassConstraint.prototype.allows = function(x, y)
{
return this.tileClass.countNonMembersInRadius(x, y, this.distance) == 0;
};
///////////////////////////////////////////////////////////////////////////
// BorderTileClassConstraint
///////////////////////////////////////////////////////////////////////////
function BorderTileClassConstraint(tileClassID, distanceInside, distanceOutside)
{
this.tileClass = getTileClass(tileClassID);
this.distanceInside = distanceInside;
this.distanceOutside = distanceOutside;
}
BorderTileClassConstraint.prototype.allows = function(x, y)
{
return (this.tileClass.countMembersInRadius(x, y, this.distanceOutside) > 0
&& this.tileClass.countNonMembersInRadius(x, y, this.distanceInside) > 0);
};

View File

@ -0,0 +1,22 @@
function Entity(name, player, x, y, angle)
{
// Get unique ID
this.id = g_Map.getEntityID();
this.name = name;
// Convert from tile coords to map coords
this.x = x;
this.y = y;
if (player !== undefined)
{
this.player = player;
this.isActor = false;
}
else
{ // Actors have no player ID
this.isActor = true;
}
this.orientation = (angle !== undefined ? angle : 0);
}

View File

@ -0,0 +1,319 @@
/////////////////////////////////////////////////////////////////////////////////////////////
// Constant definitions
/////////////////////////////////////////////////////////////////////////////////////////////
const PI = Math.PI;
const SEA_LEVEL = 20.0;
const TERRAIN_SEPARATOR = "|";
const TILES_PER_PATCH = 16;
/////////////////////////////////////////////////////////////////////////////////////////////
// Utility functions
/////////////////////////////////////////////////////////////////////////////////////////////
function fractionToTiles(f) {
return getMapSize() * f;
}
function tilesToFraction(t) {
return t / getMapSize();
}
function fractionToSize(f) {
return getMapSizeSqr() * f;
}
function sizeToFraction(s) {
return s / getMapSizeSqr();
}
function cos(x) {
return Math.cos(x);
}
function sin(x) {
return Math.sin(x);
}
function tan(x) {
return Math.tan(x);
}
function abs(x) {
return Math.abs(x);
}
function round(x) {
return Math.round(x);
}
function lerp(a, b, t) {
return a + (b-a) * t;
}
function sqrt(x) {
return Math.sqrt(x);
}
function ceil(x) {
return Math.ceil(x);
}
function floor(x) {
return Math.floor(x);
}
function max(x, y) {
return x > y ? x : y;
}
function min(x, y) {
return x < y ? x : y;
}
function println(x) {
print(x);
print("\n");
}
function argsToArray(x)
{
var numArgs = x.length;
if (numArgs != 1)
{
var ret = new Array(numArgs);
for (var i=0; i < numArgs; i++)
{
ret[i] = x[i];
}
return ret;
}
else
{
return x[0];
}
}
function chooseRand()
{
if (arguments.length==0)
{
error("chooseRand: requires at least 1 argument");
}
var ar = argsToArray(arguments);
return ar[randInt(ar.length)];
}
function createAreas(centeredPlacer, painter, constraint, num, retryFactor)
{
if (retryFactor === undefined)
retryFactor = 10;
var maxFail = num * retryFactor;
var good = 0;
var bad = 0;
var ret = [];
while(good < num && bad <= maxFail)
{
centeredPlacer.x = randInt(getMapSize());
centeredPlacer.y = randInt(getMapSize());
var r = g_Map.createArea(centeredPlacer, painter, constraint);
if (r !== undefined)
{
good++;
ret.push(r);
}
else
{
bad++;
}
}
return ret;
}
function createObjectGroups(placer, player, constraint, num, retryFactor)
{
if (retryFactor === undefined)
retryFactor = 10;
var maxFail = num * retryFactor;
var good = 0;
var bad = 0;
while(good < num && bad <= maxFail)
{
placer.x = randInt(getMapSize());
placer.y = randInt(getMapSize());
var r = createObjectGroup(placer, player, constraint);
if (r !== undefined)
{
good++;
}
else
{
bad++;
}
}
return good;
}
function createTerrain(terrain)
{
if (terrain instanceof Array)
{
var terrainList = [];
for (var i = 0; i < terrain.length; ++i)
terrainList.push(createTerrain(terrain[i]));
return new RandomTerrain(terrainList);
}
else
{
return createSimpleTerrain(terrain);
}
}
function createSimpleTerrain(terrain)
{
if (typeof(terrain) == "string")
{ // Split string by pipe | character, this allows specifying terrain + tree type in single string
var params = terrain.split(TERRAIN_SEPARATOR, 2);
if (params.length != 2)
{
return new SimpleTerrain(terrain);
}
else
{
return new SimpleTerrain(params[0], params[1]);
}
}
else
{
error("createSimpleTerrain expects string as input, received "+terrain);
return undefined;
}
}
function placeObject(type, player, x, y, angle)
{
g_Map.addObjects(new Entity(type, player, x, y, angle));
}
function placeTerrain(x, y, terrain)
{
// convert terrain param into terrain object
g_Map.placeTerrain(x, y, createTerrain(terrain));
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Access global map variable
/////////////////////////////////////////////////////////////////////////////////////////////
function createTileClass()
{
return g_Map.createTileClass();
}
function getTileClass(id)
{
// Check for valid class id
if (id < 1 || id > g_Map.tileClasses.length)
{
//error("Invalid tile class id: "+id);
return null;
}
return g_Map.tileClasses[id - 1];
}
function createArea(placer, painter, constraint)
{
return g_Map.createArea(placer, painter, constraint);
}
function createObjectGroup(placer, player, constraint)
{
return g_Map.createObjectGroup(placer, player, constraint);
}
function getMapSize()
{
return g_Map.size;
}
function getMapSizeSqr()
{
return g_Map.size*g_Map.size;
}
function getNumPlayers()
{
return g_MapSettings.PlayerData.length;
}
function getCivCode(player)
{
return g_MapSettings.PlayerData[player].Civ;
}
function getHeight(x, y)
{
g_Map.getHeight(x, y);
}
function setHeight(x, y, height)
{
g_Map.setHeight(x, y, height);
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Utility functions for classes
/////////////////////////////////////////////////////////////////////////////////////////////
// Add point to given class by id
function addToClass(x, y, id)
{
var tileClass = getTileClass(id);
if (tileClass !== null)
tileClass.add(x, y);
}
// Create a painter for the given class
function paintClass(id)
{
return new TileClassPainter(getTileClass(id));
}
// Create an avoid constraint for the given classes by the given distances
function avoidClasses(/*class1, dist1, class2, dist2, etc*/)
{
var ar = new Array(arguments.length/2);
for (var i=0; i < arguments.length/2; i++)
{
ar[i] = new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1]);
}
// Return single constraint
return new AndConstraint(ar);
}
// Create a stay constraint for the given classes by the given distances
function stayClasses(/*class1, dist1, class2, dist2, etc*/)
{
var ar = new Array(arguments.length/2);
for (var i=0; i < arguments.length/2; i++)
{
ar[i] = new StayInTileClassConstraint(arguments[2*i], arguments[2*i+1]);
}
// Return single constraint
return new AndConstraint(ar);
}

View File

@ -0,0 +1,325 @@
//////////////////////////////////////////////////////////////////////
// Map
//////////////////////////////////////////////////////////////////////
function Map(size, baseHeight)
{
// Size must be 0 to 1024, divisible by 16
this.size = size;
// Create 2D arrays for texture, object, and area maps
this.texture = new Array(size);
this.terrainObjects = new Array(size);
this.area = new Array(size);
for (var i = 0; i < size; i++)
{
this.texture[i] = new Uint16Array(size); // uint16
this.terrainObjects[i] = new Array(size); // entity
this.area[i] = new Array(size); // area
for (var j = 0; j < size; j++)
{
this.area[i][j] = {}; // undefined would cause a warning in strict mode
this.terrainObjects[i][j] = [];
}
}
var mapSize = size+1;
// Create 2D array for heightmap
this.height = new Array(mapSize);
for (var i=0; i < mapSize; i++)
{
this.height[i] = new Array(mapSize);
for (var j=0; j < mapSize; j++)
{ // Initialize height map to baseHeight
this.height[i][j] = baseHeight;
}
}
// Create name <-> id maps for textures
this.nameToID = {};
this.IDToName = []; //string
// Other arrays
this.objects = []; //object
this.areas = []; //area
this.tileClasses = []; //int
// Starting entity ID
this.entityCount = 150;
}
Map.prototype.initTerrain = function(baseTerrain)
{
// Initialize base terrain
var size = this.size;
for (var i=0; i < size; i++)
{
for (var j=0; j < size; j++)
{
baseTerrain.place(i, j);
}
}
};
// Return ID of texture (by name)
Map.prototype.getID = function(texture)
{
if (texture in (this.nameToID))
{
return this.nameToID[texture];
}
// Add new texture
var id = this.IDToName.length;
this.nameToID[texture] = id;
this.IDToName[id] = texture;
return id;
};
// Return next free entity ID
Map.prototype.getEntityID = function()
{
return this.entityCount++;
}
// Check bounds
Map.prototype.validT = function(x, y)
{
return x >= 0 && y >= 0 && x < this.size && y < this.size;
};
// Check bounds on height map (size + 1 by size + 1)
Map.prototype.validH = function(x, y)
{
return x >= 0 && y >= 0 && x <= this.size && y <= this.size;
};
// Check bounds on tile class
Map.prototype.validClass = function(c)
{
return c >= 0 && c < this.tileClasses.length;
};
Map.prototype.getTexture = function(x, y)
{
if (!this.validT(x, y))
error("getTexture: invalid tile position ("+x+", "+y+")");
return this.IDToName[this.texture[x][y]];
};
Map.prototype.setTexture = function(x, y, texture)
{
if (!this.validT(x, y))
error("setTexture: invalid tile position ("+x+", "+y+")");
this.texture[x][y] = this.getID(texture);
};
Map.prototype.getHeight = function(x, y)
{
if (!this.validH(x, y))
error("getHeight: invalid vertex position ("+x+", "+y+")");
return this.height[x][y];
};
Map.prototype.setHeight = function(x, y, height)
{
if (!this.validH(x, y))
error("setHeight: invalid vertex position ("+x+", "+y+")");
this.height[x][y] = height;
};
Map.prototype.getTerrainObjects = function(x, y)
{
if (!this.validT(x, y))
error("getTerrainObjects: invalid tile position ("+x+", "+y+")");
return this.terrainObjects[x][y];
};
Map.prototype.setTerrainObjects = function(x, y, objects)
{
if (!this.validT(x, y))
error("setTerrainObjects: invalid tile position ("+x+", "+y+")");
this.terrainObjects[x][y] = objects;
};
Map.prototype.placeTerrain = function(x, y, terrain)
{
terrain.place(x, y);
};
Map.prototype.addObjects = function(obj)
{
this.objects = this.objects.concat(obj);
};
Map.prototype.createArea = function(placer, painter, constraint)
{
// Check for multiple painters
if (painter instanceof Array)
{
var painterArray = painter;
painter = new MultiPainter(painterArray);
}
// Check for null constraint
if (constraint === undefined || constraint === null)
{
constraint = new NullConstraint();
}
else if (constraint instanceof Array)
{ // Check for multiple constraints
var constraintArray = constraint;
constraint = new AndConstraint(constraintArray);
}
var points = placer.place(constraint);
if (!points)
return undefined;
var a = new Area(points);
for (var i=0; i < points.length; i++)
{
this.area[points[i].x][points[i].y] = a;
}
painter.paint(a);
this.areas.push(a);
return a;
};
Map.prototype.createObjectGroup = function(placer, player, constraint)
{
// Check for null constraint
if (constraint === undefined || constraint === null)
{
constraint = new NullConstraint();
}
else if (constraint instanceof Array)
{ // Check for multiple constraints
var constraintArray = constraint;
constraint = new AndConstraint(constraintArray);
}
return placer.place(player, constraint);
};
Map.prototype.createTileClass = function()
{
this.tileClasses.push(new TileClass(this.size));
return this.tileClasses.length;
};
// Get height taking into account terrain curvature
Map.prototype.getExactHeight = function(x, y)
{
var xi = min(floor(x), this.size);
var yi = min(floor(y), this.size);
var xf = x - xi;
var yf = y - yi;
var h00 = this.height[xi][yi];
var h01 = this.height[xi][yi+1];
var h10 = this.height[xi+1][yi];
var h11 = this.height[xi+1][yi+1];
return ( 1 - yf ) * ( ( 1 - xf ) * h00 + xf * h10 ) + yf * ( ( 1 - xf ) * h01 + xf * h11 ) ;
};
Map.prototype.getMapData = function()
{
var data = {};
// Build entity array
var entities = [];
// Terrain objects first (trees)
var size = this.size;
for (var x=0; x < size; ++x)
{
for (var y=0; y < size; ++y)
{
if (this.terrainObjects[x][y].length)
entities = entities.concat(this.terrainObjects[x][y]);
}
}
// Now other entities
entities = entities.concat(this.objects);
// Convert from tiles to map coordinates
for (var n in entities)
{
var e = entities[n];
e.x *= 4;
e.y *= 4;
entities[n] = e;
}
data["entities"] = entities;
// Terrain
data["size"] = this.size;
// Convert 2D heightmap array to flat array
// Flat because it's easier to handle by the engine
var mapSize = size+1;
var height16 = new Array(mapSize*mapSize); // uint16
for (var x=0; x < mapSize; x++)
{
for (var y=0; y < mapSize; y++)
{
var intHeight = ((this.height[x][y] + SEA_LEVEL) * 256.0 / 0.35)|0; // floor
if (intHeight > 65000)
intHeight = 65000;
else if (intHeight < 0)
intHeight = 0;
height16[y*mapSize + x] = intHeight;
}
}
data["height"] = height16;
data["seaLevel"] = SEA_LEVEL;
// Get array of textures used in this map
var textureNames = [];
for (var name in this.nameToID)
textureNames.push(name);
data["textureNames"] = textureNames;
data["numTextures"] = textureNames.length;
// Convert 2D tile data to flat array, reodering into patches as expected by MapReader
var tiles = new Array(size*size);
var patches = size/16;
for (var x=0; x < size; x++)
{
var patchX = floor(x/16);
var offX = x%16;
for (var y=0; y < size; y++)
{
var patchY = floor(y/16);
var offY = y%16;
tiles[(patchY*patches + patchX)*256 + (offY*16 + offX)] =
{ "texIdx1" : this.texture[x][y],
"texIdx2" : 0xFFFF,
"priority" : 0
};
}
}
data["tileData"] = tiles;
return data;
};

View File

@ -0,0 +1,67 @@
var g_Map;
var g_Environment = {
SkySet: "cirrus",
SunColour: {r: 1.47461, g: 1.47461, b: 1.47461},
SunElevation: 0.951868,
SunRotation: -0.532844,
TerrainAmbientColour: {r: 0.337255, g: 0.403922, b: 0.466667},
UnitsAmbientColour: {r: 0.501961, g: 0.501961, b: 0.501961},
Water: {
WaterBody: {
Type: "default",
Colour: {r: 0.294118, g: 0.34902, b: 0.694118},
Height: 17.6262,
Shininess: 150,
Waviness: 8,
Murkiness: 0.458008,
Tint: {r: 0.447059, g: 0.411765, b: 0.321569},
ReflectionTint: {r: 0.619608, g: 0.584314, b: 0.47451},
ReflectionTintStrength: 0.298828
}
}
};
var g_Camera = {
Position: {x: 100, y: 150, z: -100},
Rotation: 0,
Declination: 0.523599
};
/////////////////////////////////////////////////////////////////////////////////////
function InitMap()
{
if (g_MapSettings === undefined || g_MapSettings == {})
{ // If settings missing, warn and use some defaults
warn("InitMapGen: settings missing");
g_MapSettings = {
Size : 13,
BaseTerrain: "grass1_spring",
BaseHeight: 0,
PlayerData : [ {}, {} ]
};
}
// Create new map
log("Creating new map...");
var terrain = createTerrain(g_MapSettings.BaseTerrain);
g_Map = new Map(g_MapSettings.Size * TILES_PER_PATCH, g_MapSettings.BaseHeight);
g_Map.initTerrain(terrain);
}
function ExportMap()
{ // Wrapper for engine function
log("Saving map...");
// Get necessary data from map
var data = g_Map.getMapData();
// Add environment and camera settings
g_Environment.Water.WaterBody.Height = SEA_LEVEL - 0.1;
data.Environment = g_Environment;
data.Camera = g_Camera;
RMS.ExportMap(data);
}

View File

@ -0,0 +1,141 @@
// Utility function used in both noises as an ease curve
function easeCurve(t)
{
return t*t*t*(t*(t*6-15)+10);
}
// Find mod of number but only positive values
function modPos(num, m)
{
var p = num % m;
if (p < 0)
p += m;
return p;
}
/////////////////////////////////////////////////////////////////////
// Noise2D
/////////////////////////////////////////////////////////////////////
function Noise2D(freq)
{
freq = floor(freq);
this.freq = freq;
this.grads = new Array(freq);
for (var i=0; i < freq; ++i)
{
this.grads[i] = new Array(freq);
for (var j=0; j < freq; ++j)
{
var a = randFloat() * 2 * PI;
this.grads[i][j] = new Vector2D(cos(a), sin(a));
}
}
}
Noise2D.prototype.get = function(x, y)
{
x *= this.freq;
y *= this.freq;
var ix = modPos(floor(x), this.freq);
var iy = modPos(floor(y), this.freq);
var fx = x - ix;
var fy = y - iy;
var ix1 = (ix+1) % this.freq;
var iy1 = (iy+1) % this.freq;
var s = this.grads[ix][iy].dot(new Vector2D(fx, fy));
var t = this.grads[ix1][iy].dot(new Vector2D(fx-1, fy));
var u = this.grads[ix][iy1].dot(new Vector2D(fx, fy-1));
var v = this.grads[ix1][iy1].dot(new Vector2D(fx-1, fy-1));
var ex = easeCurve(fx);
var ey = easeCurve(fy);
var a = s + ex*(t-s);
var b = u + ex*(v-u);
return (a + ey*(b-a)) * 0.5 + 0.5;
};
/////////////////////////////////////////////////////////////////////
// Noise3D
/////////////////////////////////////////////////////////////////////
function Noise3D(freq, vfreq)
{
freq = floor(freq);
vfreq = floor(vfreq);
this.freq = freq;
this.vfreq = vfreq;
this.grads = new Array(freq);
for (var i=0; i < freq; ++i)
{
this.grads[i] = new Array(freq);
for (var j=0; j < freq; ++j)
{
this.grads[i][j] = new Array(vfreq);
for(var k=0; k < vfreq; ++k)
{
var v = new Vector3D();
do
{
v.set(2*randFloat()-1, 2*randFloat()-1, 2*randFloat()-1);
}
while(v.lengthSquared() > 1 || v.lengthSquared() < 0.1);
v.normalize();
this.grads[i][j][k] = v;
}
}
}
}
Noise3D.prototype.get = function(x, y, z)
{
x *= this.freq;
y *= this.freq;
z *= this.vfreq;
var ix =modPos(floor(x), this.freq);
var iy = modPos(floor(y), this.freq);
var iz = modPos(floor(z), this.vfreq);
var fx = x - ix;
var fy = y - iy;
var fz = z - iz;
var ix1 = (ix+1) % this.freq;
var iy1 = (iy+1) % this.freq;
var iz1 = (iz+1) % this.vfreq;
var s0 = this.grads[ix][iy][iz].dot(new Vector3D(fx, fy, fz));
var t0 = this.grads[ix1][iy][iz].dot(new Vector3D(fx-1, fy, fz));
var u0 = this.grads[ix][iy1][iz].dot(new Vector3D(fx, fy-1, fz));
var v0 = this.grads[ix1][iy1][iz].dot(new Vector3D(fx-1, fy-1, fz));
var s1 = this.grads[ix][iy][iz1].dot(new Vector3D(fx, fy, fz-1));
var t1 = this.grads[ix1][iy][iz1].dot(new Vector3D(fx-1, fy, fz-1));
var u1 = this.grads[ix][iy1][iz1].dot(new Vector3D(fx, fy-1, fz-1));
var v1 = this.grads[ix1][iy1][iz1].dot(new Vector3D(fx-1, fy-1, fz-1));
var ex = easeCurve(fx);
var ey = easeCurve(fy);
var ez = easeCurve(fz);
var a0 = s0 + ex*(t0-s0);
var b0 = u0 + ex*(v0-u0);
var c0 = a0 + ey*(b0-a0);
var a1 = s1 + ex*(t1-s1);
var b1 = u1 + ex*(v1-u1);
var c1 = a1 + ey*(b1-a1);
return (c0 + ez*(c1-c0)) * 0.5 + 0.5;
};

View File

@ -0,0 +1,379 @@
const ELEVATION_SET = 0;
const ELEVATION_MODIFY = 1;
/////////////////////////////////////////////////////////////////////////////
// ElevationPainter
/////////////////////////////////////////////////////////////////////////////
function ElevationPainter(elevation)
{
this.elevation = elevation;
this.DX = [0, 1, 1, 0];
this.DY = [0, 0, 1, 1];
}
ElevationPainter.prototype.paint = function(area)
{
var length = area.points.length;
var elevation = this.elevation;
for (var i=0; i < length; i++)
{
var pt = area.points[i];
for (var j=0; j < 4; j++)
{
g_Map.height[pt.x+this.DX[j]][pt.y+this.DY[j]] = elevation;
}
}
};
/////////////////////////////////////////////////////////////////////////////
// LayeredPainter
/////////////////////////////////////////////////////////////////////////////
function LayeredPainter(terrainArray, widths)
{
if (!(terrainArray instanceof Array))
error("terrains must be an array!");
this.terrains = [];
for (var i = 0; i < terrainArray.length; ++i)
this.terrains.push(createTerrain(terrainArray[i]));
this.widths = widths;
}
LayeredPainter.prototype.paint = function(area)
{
var size = getMapSize();
var saw = new Array(size);
var dist = new Array(size);
// init typed arrays
for (var i = 0; i < size; ++i)
{
saw[i] = new Uint8Array(size); // bool / uint8
dist[i] = new Uint16Array(size); // uint16
}
// Point queue (implemented with array)
var pointQ = [];
// push edge points
var pts = area.points;
var length = pts.length;
for (var i=0; i < length; i++)
{
var x = pts[i].x;
var y = pts[i].y;
for (var dx=-1; dx <= 1; dx++)
{
var nx = x+dx;
for (var dy=-1; dy <= 1; dy++)
{
var ny = y+dy;
if (g_Map.validT(nx, ny) && g_Map.area[nx][ny] != area && !saw[nx][ny])
{
saw[nx][ny] = 1;
dist[nx][ny] = 0;
pointQ.push(new Point(nx, ny));
}
}
}
}
// do BFS inwards to find distances to edge
while (pointQ.length)
{
var pt = pointQ.shift(); // Pop queue
var px = pt.x;
var py = pt.y;
var d = dist[px][py];
// paint if in area
if (g_Map.area[px][py] == area)
{
var w=0;
var i=0;
for (; i < this.widths.length; i++)
{
w += this.widths[i];
if (w >= d)
{
break;
}
}
this.terrains[i].place(px, py);
}
// enqueue neighbours
for (var dx=-1; dx<=1; dx++)
{
var nx = px+dx;
for (var dy=-1; dy<=1; dy++)
{
var ny = py+dy;
if (g_Map.validT(nx, ny) && g_Map.area[nx][ny] == area && !saw[nx][ny])
{
saw[nx][ny] = 1;
dist[nx][ny] = d+1;
pointQ.push(new Point(nx, ny));
}
}
}
}
};
/////////////////////////////////////////////////////////////////////////////
// MultiPainter
/////////////////////////////////////////////////////////////////////////////
function MultiPainter(painters)
{
this.painters = painters;
}
MultiPainter.prototype.paint = function(area)
{
for (var i=0; i < this.painters.length; i++)
{
this.painters[i].paint(area);
}
};
/////////////////////////////////////////////////////////////////////////////
// SmoothElevationPainter
/////////////////////////////////////////////////////////////////////////////
function SmoothElevationPainter(type, elevation, blendRadius)
{
this.type = type;
this.elevation = elevation;
this.blendRadius = blendRadius;
if (type != ELEVATION_SET && type != ELEVATION_MODIFY)
error("SmoothElevationPainter: invalid type '"+type+"'");
}
SmoothElevationPainter.prototype.checkInArea = function(area, x, y)
{
if (g_Map.validT(x, y))
{
return (g_Map.area[x][y] == area);
}
else
{
return false;
}
};
SmoothElevationPainter.prototype.paint = function(area)
{
var pointQ = [];
var pts = area.points;
var heightPts = [];
var mapSize = getMapSize()+1;
var saw = new Array(mapSize);
var dist = new Array(mapSize);
var gotHeightPt = new Array(mapSize);
var newHeight = new Array(mapSize);
// init typed arrays
for (var i = 0; i < mapSize; ++i)
{
saw[i] = new Uint8Array(mapSize); // bool / uint8
dist[i] = new Uint16Array(mapSize); // uint16
gotHeightPt[i] = new Uint8Array(mapSize); // bool / uint8
newHeight[i] = new Float32Array(mapSize); // float32
}
var length = pts.length;
// get a list of all points
for (var i=0; i < length; i++)
{
var x = pts[i].x;
var y = pts[i].y;
for (var dx=-1; dx <= 2; dx++)
{
var nx = x+dx;
for (var dy=-1; dy <= 2; dy++)
{
var ny = y+dy;
if (g_Map.validH(nx, ny) && !gotHeightPt[nx][ny])
{
gotHeightPt[nx][ny] = 1;
heightPts.push(new Point(nx, ny));
newHeight[nx][ny] = g_Map.height[nx][ny];
}
}
}
}
// push edge points
for (var i=0; i < length; i++)
{
var x = pts[i].x, y = pts[i].y;
for (var dx=-1; dx <= 2; dx++)
{
var nx = x+dx;
for (var dy=-1; dy <= 2; dy++)
{
var ny = y+dy;
if (g_Map.validH(nx, ny)
&& !this.checkInArea(area, nx, ny)
&& !this.checkInArea(area, nx-1, ny)
&& !this.checkInArea(area, nx, ny-1)
&& !this.checkInArea(area, nx-1, ny-1)
&& !saw[nx][ny])
{
saw[nx][ny]= 1;
dist[nx][ny] = 0;
pointQ.push(new Point(nx, ny));
}
}
}
}
// do BFS inwards to find distances to edge
while(pointQ.length)
{
var pt = pointQ.shift();
var px = pt.x;
var py = pt.y;
var d = dist[px][py];
// paint if in area
if (g_Map.validH(px, py)
&& (this.checkInArea(area, px, py) || this.checkInArea(area, px-1, py)
|| this.checkInArea(area, px, py-1) || this.checkInArea(area, px-1, py-1)))
{
if (d <= this.blendRadius)
{
var a = (d-1) / this.blendRadius;
if (this.type == ELEVATION_SET)
{
newHeight[px][py] = a*this.elevation + (1-a)*g_Map.height[px][py];
}
else
{ // type == MODIFY
newHeight[px][py] += a*this.elevation;
}
}
else
{ // also happens when blendRadius == 0
if (this.type == ELEVATION_SET)
{
newHeight[px][py] = this.elevation;
}
else
{ // type == MODIFY
newHeight[px][py] += this.elevation;
}
}
}
// enqueue neighbours
for (var dx=-1; dx <= 1; dx++)
{
var nx = px+dx;
for (var dy=-1; dy <= 1; dy++)
{
var ny = py+dy;
if (g_Map.validH(nx, ny)
&& (this.checkInArea(area, nx, ny) || this.checkInArea(area, nx-1, ny)
|| this.checkInArea(area, nx, ny-1) || this.checkInArea(area, nx-1, ny-1))
&& !saw[nx][ny])
{
saw[nx][ny] = 1;
dist[nx][ny] = d+1;
pointQ.push(new Point(nx, ny));
}
}
}
}
length = heightPts.length;
// smooth everything out
for (var i = 0; i < length; ++i)
{
var pt = heightPts[i];
var px = pt.x;
var py = pt.y;
if ((this.checkInArea(area, px, py) || this.checkInArea(area, px-1, py)
|| this.checkInArea(area, px, py-1) || this.checkInArea(area, px-1, py-1)))
{
var sum = 8 * newHeight[px][py];
var count = 8;
for (var dx=-1; dx <= 1; dx++)
{
var nx = px+dx;
for (var dy=-1; dy <= 1; dy++)
{
var ny = py+dy;
if (g_Map.validH(nx, ny))
{
sum += newHeight[nx][ny];
count++;
}
}
}
g_Map.height[px][py] = sum/count;
}
}
};
/////////////////////////////////////////////////////////////////////////////
// TerrainPainter
/////////////////////////////////////////////////////////////////////////////
function TerrainPainter(terrain)
{
this.terrain = createTerrain(terrain);
}
TerrainPainter.prototype.paint = function(area)
{
var length = area.points.length;
for (var i=0; i < length; i++)
{
var pt = area.points[i];
this.terrain.place(pt.x, pt.y);
}
};
/////////////////////////////////////////////////////////////////////////////
// TileClassPainter
/////////////////////////////////////////////////////////////////////////////
function TileClassPainter(tileClass)
{
this.tileClass = tileClass;
}
TileClassPainter.prototype.paint = function(area)
{
var length = area.points.length;
for (var i=0; i < length; i++)
{
var pt = area.points[i];
this.tileClass.add(pt.x, pt.y);
}
};

View File

@ -0,0 +1,291 @@
/////////////////////////////////////////////////////////////////////////////////////////
// ClumpPlacer
/////////////////////////////////////////////////////////////////////////////////////////
function ClumpPlacer(size, coherence, smoothness, failFraction, x, y)
{
this.size = size;
this.coherence = coherence;
this.smoothness = smoothness;
this.failFraction = (failFraction !== undefined ? failFraction : 0);
this.x = (x !== undefined ? x : -1);
this.y = (y !== undefined ? y : -1);
}
ClumpPlacer.prototype.place = function(constraint)
{
if (!g_Map.validT(this.x, this.y) || !constraint.allows(this.x, this.y))
{
return false;
}
var retVec = [];
var size = getMapSize();
var gotRet = new Array(size);
for (var i = 0; i < size; ++i)
{
gotRet[i] = new Uint8Array(size); // bool / uint8
}
var radius = sqrt(this.size / PI);
var perim = 4 * radius * 2 * PI;
var intPerim = ceil(perim);
var ctrlPts = 1 + Math.floor(1.0/Math.max(this.smoothness,1.0/intPerim));
if (ctrlPts > radius * 2 * PI)
ctrlPts = Math.floor(radius * 2 * PI) + 1;
var noise = new Float32Array(intPerim); //float32
var ctrlCoords = new Float32Array(ctrlPts+1); //float32
var ctrlVals = new Float32Array(ctrlPts+1); //float32
for (var i=0; i < ctrlPts; i++)
{
ctrlCoords[i] = i * perim / ctrlPts;
ctrlVals[i] = 2.0*randFloat();
}
var c = 0;
var looped = 0;
for (var i=0; i < intPerim; i++)
{
if (ctrlCoords[(c+1) % ctrlPts] < i && !looped)
{
c = (c+1) % ctrlPts;
if (c == ctrlPts-1)
looped = 1;
}
var t = (i - ctrlCoords[c]) / ((looped ? perim : ctrlCoords[(c+1)%ctrlPts]) - ctrlCoords[c]);
var v0 = ctrlVals[(c+ctrlPts-1)%ctrlPts];
var v1 = ctrlVals[c];
var v2 = ctrlVals[(c+1)%ctrlPts];
var v3 = ctrlVals[(c+2)%ctrlPts];
var P = (v3 - v2) - (v0 - v1);
var Q = (v0 - v1) - P;
var R = v2 - v0;
var S = v1;
noise[i] = P*t*t*t + Q*t*t + R*t + S;
if (noise[i] > 4294967296)
warn("noise["+i+"] is beyond max_uint32 bounds");
}
var failed = 0;
for (var p=0; p < intPerim; p++)
{
var th = 2 * PI * p / perim;
var r = radius * (1 + (1-this.coherence)*noise[p]);
var s = sin(th);
var c = cos(th);
var xx=this.x;
var yy=this.y;
for (var k=0; k < ceil(r); k++)
{
var i = Math.floor(xx);
var j = Math.floor(yy);
if (g_Map.validT(i, j) && constraint.allows(i, j))
{
if (!gotRet[i][j])
{ // Only include each point once
gotRet[i][j] = 1;
retVec.push(new Point(i, j));
}
}
else
{
failed++;
}
xx += s;
yy += c;
}
}
return ((failed > this.size*this.failFraction) ? undefined : retVec);
};
/////////////////////////////////////////////////////////////////////////////////////////
// RectPlacer
/////////////////////////////////////////////////////////////////////////////////////////
function RectPlacer(x1, y1, x2, y2)
{
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
if (x1 > x2 || y1 > y2)
error("RectPlacer: incorrect bounds on rect");
}
RectPlacer.prototype.place = function(constraint)
{
var ret = [];
var x2 = this.x2;
var y2 = this.y2;
for (var x=this.x1; x < x2; x++)
{
for (var y=this.y1; y < y2; y++)
{
if (g_Map.validT(x, y) && constraint.allows(x, y))
{
ret.push(new Point(x, y));
}
else
{
return undefined;
}
}
}
return ret;
};
/////////////////////////////////////////////////////////////////////////////////////////
// ObjectGroupPlacer
/////////////////////////////////////////////////////////////////////////////////////////
function ObjectGroupPlacer() {}
/////////////////////////////////////////////////////////////////////////////////////////
// SimpleGroup
/////////////////////////////////////////////////////////////////////////////////////////
function SimpleObject(type, minCount, maxCount, minDistance, maxDistance, minAngle, maxAngle)
{
this.type = type;
this.minCount = minCount;
this.maxCount = maxCount;
this.minDistance = minDistance;
this.maxDistance = maxDistance;
this.minAngle = (minAngle !== undefined ? minAngle : 0);
this.maxAngle = (maxAngle !== undefined ? maxAngle : 2*PI);
if (minCount > maxCount)
error("SimpleObject: minCount must be less than or equal to maxCount");
if (minDistance > maxDistance)
error("SimpleObject: minDistance must be less than or equal to maxDistance");
if (minAngle > maxAngle)
error("SimpleObject: minAngle must be less than or equal to maxAngle");
}
SimpleObject.prototype.place = function(cx, cy, player, avoidSelf, constraint)
{
var failCount = 0;
var count = randInt(this.minCount, this.maxCount);
var resultObjs = [];
for (var i=0; i < count; i++)
{
while(true)
{
var distance = randFloat(this.minDistance, this.maxDistance);
var direction = randFloat(0, 2*PI);
var x = cx + 0.5 + distance*cos(direction);
var y = cy + 0.5 + distance*sin(direction);
var fail = false; // reset place failure flag
if (x < 0 || y < 0 || x > g_Map.size || y > g_Map.size)
{
fail = true;
}
else
{
if (avoidSelf)
{
var length = resultObjs.length;
for (var i = 0; (i < length) && !fail; i++)
{
var dx = x - resultObjs[i].x;
var dy = y - resultObjs[i].y;
if ((dx*dx + dy*dy) < 1)
{
fail = true;
}
}
}
if (!fail)
{
if (!constraint.allows(Math.floor(x), Math.floor(y)))
{
fail = true;
}
else
{ // if we got here, we're good
var angle = randFloat(this.minAngle, this.maxAngle);
resultObjs.push(new Entity(this.type, player, x, y, angle));
break;
}
}
}
if (fail)
{
failCount++;
if (failCount > 20) // TODO: Make this adjustable
{
return undefined;
}
}
}
}
return resultObjs;
};
function SimpleGroup(elements, avoidSelf, tileClass, x, y)
{
this.elements = elements;
this.tileClass = (tileClass !== undefined ? getTileClass(tileClass) : null);
this.avoidSelf = (avoidSelf !== undefined ? avoidSelf : false);
this.x = (x !== undefined ? x : -1);
this.y = (y !== undefined ? y : -1);
}
SimpleGroup.prototype.place = function(player, constraint)
{
var resultObjs = [];
// Try placement of objects
var length = this.elements.length;
for (var i=0; i < length; i++)
{
var objs = this.elements[i].place(this.x, this.y, player, this.avoidSelf, constraint);
if (objs === undefined)
{ // Failure
return false;
}
else
{
resultObjs = resultObjs.concat(objs);
}
}
// Add placed objects to map
length = resultObjs.length;
for (var i=0; i < length; i++)
{
g_Map.addObjects(resultObjs[i]);
if (this.tileClass !== null)
{ // Round object position to integer
this.tileClass.add(Math.floor(resultObjs[i].x), Math.floor(resultObjs[i].y));
}
}
return true;
};

View File

@ -0,0 +1,5 @@
function Point(x, y)
{
this.x = (x !== undefined ? x : 0);
this.y = (y !== undefined ? y : 0);
}

View File

@ -0,0 +1,53 @@
/*
* Return a random floating point number using Math.random library
*
* If no parameter given, the returned float is in the interval [0, 1)
* If two parameters are given, they are minval and maxval, and the returned float is in the interval [minval, maxval)
*/
function randFloat()
{
if (arguments.length == 0)
{
return Math.random();
}
else if (arguments.length == 2)
{
var minVal = arguments[0];
var maxVal = arguments[1];
return minVal + randFloat() * (maxVal - minVal);
}
else
{
error("randFloat() received invalid number of arguments: "+arguments.length);
return undefined;
}
}
/*
* Return a random integer using Math.random library
*
* If one parameter given, it's maxval, and the returned integer is in the interval [0, maxval)
* If two parameters are given, they are minval and maxval, and the returned integer is in the interval [minval, maxval]
*/
function randInt()
{
if (arguments.length == 1)
{
var maxVal = arguments[0];
return Math.floor(Math.random() * maxVal);
}
else if (arguments.length == 2)
{
var minVal = arguments[0];
var maxVal = arguments[1];
return minVal + randInt(maxVal - minVal + 1);
}
else
{
error("randInt() received invalid number of arguments: "+arguments.length);
return undefined;
}
}

View File

@ -0,0 +1,57 @@
//////////////////////////////////////////////////////////////////////
// Terrain
//////////////////////////////////////////////////////////////////////
function Terrain() {}
Terrain.prototype.place = function(x, y)
{
// Clear old array
g_Map.terrainObjects[x][y] = [];
this.placeNew(x, y);
};
Terrain.prototype.placeNew = function() {};
//////////////////////////////////////////////////////////////////////
// SimpleTerrain
//////////////////////////////////////////////////////////////////////
function SimpleTerrain(texture, treeType)
{
if (texture === undefined)
error("SimpleTerrain: texture not defined");
this.texture = texture;
this.treeType = treeType;
}
SimpleTerrain.prototype = new Terrain();
SimpleTerrain.prototype.constructor = SimpleTerrain;
SimpleTerrain.prototype.placeNew = function(x, y)
{
if (this.treeType !== undefined)
g_Map.terrainObjects[x][y].push(new Entity(this.treeType, 0, x+0.5, y+0.5, randFloat()*PI));
g_Map.texture[x][y] = g_Map.getID(this.texture);
};
//////////////////////////////////////////////////////////////////////
// RandomTerrain
//////////////////////////////////////////////////////////////////////
function RandomTerrain(terrains)
{
if (!(terrains instanceof Array) || !terrains.length)
error("Invalid terrains array");
this.terrains = terrains;
}
RandomTerrain.prototype = new Terrain();
RandomTerrain.prototype.constructor = RandomTerrain;
RandomTerrain.prototype.placeNew = function(x, y)
{
this.terrains[randInt(this.terrains.length)].placeNew(x, y);
};

View File

@ -0,0 +1,141 @@
//////////////////////////////////////////////////////////////////////
// RangeOp
//////////////////////////////////////////////////////////////////////
function RangeOp(size)
{
// Get smallest power of 2 which is greater than or equal to size
this.nn = 1;
while (this.nn < size) {
this.nn *= 2;
}
this.vals = new Int16Array(2*this.nn); // int16
}
RangeOp.prototype.set = function(pos, amt)
{
this.add(pos, amt - this.vals[this.nn + pos]);
};
RangeOp.prototype.add = function(pos, amt)
{
for(var s = this.nn; s >= 1; s /= 2)
{
this.vals[s + pos] += amt;
pos = Math.floor(pos/2);
}
};
RangeOp.prototype.get = function(start, end)
{
var ret = 0;
var i;
var nn = this.nn;
// Count from start to end by powers of 2
for (i = 1; start+i <= end; i *= 2)
{
if (start & i)
{ // For each bit in start
ret += this.vals[nn/i + Math.floor(start/i)];
start += i;
}
}
//
while(i >= 1)
{
if(start+i <= end)
{
ret += this.vals[nn/i + Math.floor(start/i)];
start += i;
}
i /= 2;
}
return ret;
};
//////////////////////////////////////////////////////////////////////
// TileClass
//////////////////////////////////////////////////////////////////////
function TileClass(size)
{
this.size = size;
this.inclusionCount = new Array(size);
this.rangeCount = new Array(size);
for (var i=0; i < size; ++i)
{
this.inclusionCount[i] = new Int16Array(size); //int16
this.rangeCount[i] = new RangeOp(size);
}
}
TileClass.prototype.add = function(x, y)
{
if (!this.inclusionCount[x][y])
{
this.rangeCount[y].add(x, 1);
}
this.inclusionCount[x][y]++;
};
TileClass.prototype.remove = function(x, y)
{
this.inclusionCount[x][y]--;
if(!this.inclusionCount[x][y])
{
this.rangeCount[y].add(x, -1);
}
};
TileClass.prototype.countInRadius = function(cx, cy, radius, returnMembers)
{
var members = 0;
var nonMembers = 0;
var size = this.size;
var ymax = cy+radius;
for (var y = cy-radius; y <= ymax; y++)
{
var iy = Math.floor(y);
if(iy >= 0 && iy < size)
{
var dy = y - cy;
var dx = Math.sqrt(radius*radius - dy*dy);
var lowerX = Math.floor(cx - dx);
var upperX = Math.floor(cx + dx);
var minX = (lowerX > 0 ? lowerX : 0);
var maxX = (upperX < size ? upperX+1 : size);
var total = maxX - minX;
var mem = this.rangeCount[iy].get(minX, maxX);
members += mem;
nonMembers += total - mem;
}
}
if (returnMembers)
return members;
else
return nonMembers;
};
TileClass.prototype.countMembersInRadius = function(cx, cy, radius)
{
return this.countInRadius(cx, cy, radius, true);
};
TileClass.prototype.countNonMembersInRadius = function(cx, cy, radius)
{
return this.countInRadius(cx, cy, radius, false);
};

View File

@ -0,0 +1,135 @@
/////////////////////////////////////////////////////////////////////
// Vector2D
/////////////////////////////////////////////////////////////////////
// TODO: Type errors if v not instanceof Vector classes
// TODO: Possible implement in C++
function Vector2D(x, y)
{
if (arguments.length == 2)
{
this.set(x, y);
}
else
{
this.set(0, 0);
}
}
Vector2D.prototype.set = function(x, y)
{
this.x = x;
this.y = y;
};
Vector2D.prototype.add = function(v)
{
return new Vector2D(this.x + v.x, this.y + v.y);
};
Vector2D.prototype.sub = function(v)
{
return new Vector2D(this.x - v.x, this.y - v.y);
};
Vector2D.prototype.mult = function(f)
{
return new Vector2D(this.x * f, this.y * f);
};
Vector2D.prototype.div = function(f)
{
return new Vector2D(this.x / f, this.y / f);
};
Vector2D.prototype.dot = function(v)
{
return this.x * v.x + this.y * v.y;
};
Vector2D.prototype.lengthSquared = function()
{
return this.dot(this);
};
Vector2D.prototype.length = function()
{
return sqrt(this.lengthSquared());
};
Vector2D.prototype.normalize = function()
{
var mag = this.length();
this.x /= mag;
this.y /= mag;
};
/////////////////////////////////////////////////////////////////////
// Vector3D
/////////////////////////////////////////////////////////////////////
function Vector3D(x, y, z)
{
if (arguments.length == 3)
{
this.set(x, y, z);
}
else
{
this.set(0, 0, 0);
}
}
Vector3D.prototype.set = function(x, y, z)
{
this.x = x;
this.y = y;
this.z = z;
};
Vector3D.prototype.add = function(v)
{
return new Vector3D(this.x + v.x, this.y + v.y, this.z + v.z);
};
Vector3D.prototype.sub = function(v)
{
return new Vector3D(this.x - v.x, this.y - v.y, this.z - v.z);
};
Vector3D.prototype.mult = function(f)
{
return new Vector3D(this.x * f, this.y * f, this.z * f);
};
Vector3D.prototype.div = function(f)
{
return new Vector3D(this.x / f, this.y / f, this.z / f);
};
Vector3D.prototype.dot = function(v)
{
return this.x * v.x + this.y * v.y + this.z * v.z;
};
Vector3D.prototype.lengthSquared = function()
{
return this.dot(this);
};
Vector3D.prototype.length = function()
{
return sqrt(this.lengthSquared());
};
Vector3D.prototype.normalize = function()
{
var mag = this.length();
this.x /= mag;
this.y /= mag;
this.z /= mag;
};

View File

@ -1,137 +0,0 @@
const SIZE = 160;
const sand = "beach_medit_dry";
const grass1 = "grass_temperate_a";
const grass2 = "grass_mediterranean_green_flowers";
const forestFloor = "forrestfloor";
const dirt1 = "grass_sand_75";
const dirt2 = "grass_sand_50";
const dirt3 = "dirt_brown_e";
const cliffBase = "cliff base a";
const cliffBeach = "beech_cliff_a_75";
const cliff = "cliff_face3";
const oTree = "flora_tree_oak";
const oGrass = "props/flora/grass_soft_small.xml"
const oMine = "geology_stone_light";
// Initialize
init(SIZE, grass1, 0);
// Create classes
clImpassable = createTileClass();
clRock = createTileClass();
// Paint elevation
noise0 = new Noise2D(4 * SIZE/128.0);
noise1 = new Noise2D(8 * SIZE/128.0);
noise2 = new Noise2D(11 * SIZE/128.0);
noise3 = new Noise2D(30 * SIZE/128.0);
noise4 = new Noise2D(60 * SIZE/128.0);
for(ix=0; ix<SIZE+1; ix++) {
for(iy=0; iy<SIZE+1; iy++) {
x = ix / (SIZE + 1.0);
y = iy / (SIZE + 1.0);
// Calculate base noise
n = (noise0.eval(x, y) + 0.4 * noise1.eval(x, y)) / 1.4;
T = .4; // Water cutoff
if(n < T) {
// Tile is underwater - just scale the height down a bit
h = Math.max(-50 * (T-n)/T, -8);
}
else {
// Tile is above water - add some land noise depending on how far we are from the shoreline
u = 27*noise1.eval(x, y) + 14*noise2.eval(x,y) + 9 * noise3.eval(x,y) - 14;
h = 8*(n-T) + Math.max(0, lerp(0, u, Math.min(.1, n-T)*10));
h += 0.4*noise4.eval(x, y);
}
setHeight(ix, iy, h);
}
}
// Paint terrains
for(ix=0; ix<SIZE; ix++) {
for(iy=0; iy<SIZE; iy++) {
h00 = getHeight(ix, iy);
h01 = getHeight(ix, iy+1);
h10 = getHeight(ix+1, iy);
h11 = getHeight(ix+1, iy+1);
maxH = Math.max(h00, h01, h10, h11);
minH = Math.min(h00, h01, h10, h11);
if(maxH <= 0) {
setTexture(ix, iy, sand);
addToClass(ix, iy, clImpassable);
}
else if(maxH - minH > 3.2) {
setTexture(ix, iy, cliff);
addToClass(ix, iy, clImpassable);
}
else if(maxH - minH > 2.7) {
setTexture(ix, iy, cliffBase);
addToClass(ix, iy, clImpassable);
}
else if(minH <= 0) {
setTexture(ix, iy, sand);
addToClass(ix, iy, clImpassable);
}
else {
setTexture(ix, iy, grass1);
}
}
}
// Paint forest and dirt
forestNoise1 = new Noise2D(20 * SIZE/128.0);
forestNoise2 = new Noise2D(40 * SIZE/128.0);
dirtNoise = new Noise2D(80 * SIZE/128.0);
for(ix=0; ix<SIZE; ix++) {
for(iy=0; iy<SIZE; iy++) {
x = ix / (SIZE + 1.0);
y = iy / (SIZE + 1.0);
h00 = getHeight(ix, iy);
h01 = getHeight(ix, iy+1);
h10 = getHeight(ix+1, iy);
h11 = getHeight(ix+1, iy+1);
maxH = Math.max(h00, h01, h10, h11);
minH = Math.min(h00, h01, h10, h11);
if(maxH - minH < 1.7 && minH > 0) {
fn = (forestNoise1.eval(x,y) + .5*forestNoise1.eval(x,y)) / 1.5;
if(minH > .5 && fn < .38 && dirtNoise.eval(x,y) > .55) {
if(dirtNoise.eval(x,y) > .72) {
setTexture(ix, iy, dirt2);
}
else {
setTexture(ix, iy, dirt1);
}
}
if(fn > .6 && randFloat() < (.3 + .7 * Math.min(fn-.6, .1) / .1) ) {
placeObject(oTree, 0, ix+.4+.2*randFloat(), iy+.4+.2*randFloat(), randFloat()*2*Math.PI);
addToClass(ix, iy, clImpassable);
if(randFloat() < .7) {
setTexture(ix, iy, forestFloor);
}
}
}
}
}
println("Creating mines...");
group = new SimpleGroup([new SimpleObject(oMine, 3,4, 0,2)], true, clRock);
createObjectGroups(group, 0,
new AvoidTileClassConstraint(clImpassable, 2, clRock, 13),
12, 100
);

View File

@ -1,5 +0,0 @@
//TODO: Move to some library file?
initFromScenario("mediterannean", LOAD_ALL);

View File

@ -1,272 +0,0 @@
// Object type constants
const
TYPE_RECT_PLACER = 1,
TYPE_TERRAIN_PAINTER = 2,
TYPE_NULL_CONSTRAINT = 3,
TYPE_LAYERED_PAINTER = 4,
TYPE_AVOID_AREA_CONSTRAINT = 5,
TYPE_CLUMP_PLACER = 6,
TYPE_AVOID_TEXTURE_CONSTRAINT = 7,
TYPE_ELEVATION_PAINTER = 8,
TYPE_SMOOTH_ELEVATION_PAINTER = 9,
TYPE_SIMPLE_GROUP = 10,
TYPE_AVOID_TILE_CLASS_CONSTRAINT = 11,
TYPE_TILE_CLASS_PAINTER = 12,
TYPE_STAY_IN_TILE_CLASS_CONSTRAINT = 13,
TYPE_BORDER_TILE_CLASS_CONSTRAINT = 14;
// SmoothElevationPainter constants
const ELEVATION_SET = 0;
const ELEVATION_MODIFY = 1;
// PI
const PI = Math.PI;
// initFromScenario constants
const LOAD_NOTHING = 0;
const LOAD_TERRAIN = 1;
const LOAD_INTERACTIVES = 2;
const LOAD_NON_INTERACTIVES = 4;
const LOAD_ALL = LOAD_TERRAIN | LOAD_INTERACTIVES | LOAD_NON_INTERACTIVES;
// Utility functions
function fractionToTiles(f) {
return getMapSize() * f;
}
function tilesToFraction(t) {
return t / getMapSize();
}
function fractionToSize(f) {
return getMapSize() * getMapSize() * f;
}
function sizeToFraction(s) {
return s / getMapSize() / getMapSize();
}
function cos(x) {
return Math.cos(x);
}
function sin(x) {
return Math.sin(x);
}
function tan(x) {
return Math.tan(x);
}
function abs(x) {
return Math.abs(x);
}
function round(x) {
return Math.round(x);
}
function lerp(a, b, t) {
return a + (b-a) * t;
}
function println(x) {
print(x);
print("\n");
}
function argsToArray(x) {
if(x.length!=1) {
var ret = new Array();
for(var i=0; i<x.length; i++) {
ret[i] = x[i];
}
return ret;
}
else {
return x[0];
}
}
function chooseRand() {
if(arguments.length==0) {
error("chooseRand: requires at least 1 argument");
}
var ar = argsToArray(arguments);
return ar[randInt(ar.length)];
}
function createAreas(centeredPlacer, painter, constraint, num, retryFactor) {
if(retryFactor == undefined) {
retryFactor = 10;
}
var maxFail = num * retryFactor;
var good = 0;
var bad = 0;
var ret = new Array();
while(good < num && bad <= maxFail) {
centeredPlacer.x = randInt(SIZE);
centeredPlacer.y = randInt(SIZE);
var r = createArea(centeredPlacer, painter, constraint);
if(r) {
good++;
ret[ret.length] = r;
}
else {
bad++;
}
}
return ret;
}
function createObjectGroups(placer, player, constraint, num, retryFactor) {
if(retryFactor == undefined) {
retryFactor = 10;
}
var maxFail = num * retryFactor;
var good = 0;
var bad = 0;
while(good < num && bad <= maxFail) {
placer.x = randInt(SIZE);
placer.y = randInt(SIZE);
var r = createObjectGroup(placer, player, constraint);
if(r) {
good++;
}
else {
bad++;
}
}
return good;
}
// Area placers
function RectPlacer(x1, y1, x2, y2) {
this.TYPE = TYPE_RECT_PLACER;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
function TerrainPainter(terrain) {
this.TYPE = TYPE_TERRAIN_PAINTER;
this.terrain = terrain;
}
function ClumpPlacer(size, coherence, smoothness, failFraction, x, y) {
this.TYPE = TYPE_CLUMP_PLACER;
this.size = size;
this.coherence = coherence;
this.smoothness = smoothness;
this.failFraction = failFraction!=undefined ? failFraction : 0;
this.x = x!=undefined ? x : -1;
this.y = y!=undefined ? y : -1;
}
// Area painters
function LayeredPainter(widths, terrains) {
this.TYPE = TYPE_LAYERED_PAINTER;
this.widths = widths;
this.terrains = terrains;
}
function ElevationPainter(elevation) {
this.TYPE = TYPE_ELEVATION_PAINTER;
this.elevation = elevation;
}
function TileClassPainter(tileClass) {
this.TYPE = TYPE_TILE_CLASS_PAINTER;
this.tileClass = tileClass;
}
function SmoothElevationPainter(type, elevation, blendRadius) {
this.TYPE = TYPE_SMOOTH_ELEVATION_PAINTER;
this.type = type;
this.elevation = elevation;
this.blendRadius = blendRadius;
}
// Constraints
function NullConstraint() {
this.TYPE = TYPE_NULL_CONSTRAINT;
}
function AvoidAreaConstraint(area) {
this.TYPE = TYPE_AVOID_AREA_CONSTRAINT;
this.area = area;
}
function AvoidTextureConstraint(texture) {
this.TYPE = TYPE_AVOID_TEXTURE_CONSTRAINT;
this.texture = texture;
}
function AvoidTileClassConstraint(tileClass, distance) {
this.TYPE = TYPE_AVOID_TILE_CLASS_CONSTRAINT;
this.tileClass = tileClass;
this.distance = distance;
}
function StayInTileClassConstraint(tileClass, distance) {
this.TYPE = TYPE_STAY_IN_TILE_CLASS_CONSTRAINT;
this.tileClass = tileClass;
this.distance = distance;
}
function BorderTileClassConstraint(tileClass, distanceInside, distanceOutside) {
this.TYPE = TYPE_BORDER_TILE_CLASS_CONSTRAINT;
this.tileClass = tileClass;
this.distanceInside = distanceInside;
this.distanceOutside = distanceOutside;
}
// Object groups
function SimpleObject(type, minCount, maxCount, minDistance, maxDistance,
minAngle, maxAngle) {
this.type = type;
this.minCount = minCount;
this.maxCount = maxCount;
this.minDistance = minDistance;
this.maxDistance = maxDistance;
this.minAngle = minAngle!=undefined ? minAngle : 0;
this.maxAngle = maxAngle!=undefined ? maxAngle : 2*PI;
}
function SimpleGroup(elements, avoidSelf, tileClass, x, y) {
this.TYPE = TYPE_SIMPLE_GROUP;
this.elements = elements;
this.avoidSelf = avoidSelf!=undefined ? avoidSelf : false;
this.tileClass = tileClass!=undefined ? tileClass : null;
this.x = x!=undefined ? x : -1;
this.y = x!=undefined ? y : -1;
}
// Utility functions for classes
// Create a painter for the given class
function paintClass(cl) {
return new TileClassPainter(cl);
}
// Create an avoid constraint for the given classes by the given distances
function avoidClasses(/*class1, dist1, class2, dist2, etc*/) {
var ar = new Array(arguments.length/2);
for(var i=0; i<arguments.length/2; i++) {
ar[i] = new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1]);
}
return ar;
}

View File

@ -10,7 +10,7 @@ function InitGame(settings)
var cmpAIManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIManager);
for (var i = 0; i < settings.PlayerData.length; ++i)
{
if (settings.PlayerData[i] && settings.PlayerData[i].AI != "")
if (settings.PlayerData[i] && settings.PlayerData[i].AI && settings.PlayerData[i].AI != "")
cmpAIManager.AddPlayer(settings.PlayerData[i].AI, i+1);
}
}

34
source/graphics/Entity.h Normal file
View File

@ -0,0 +1,34 @@
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_ENTITY
#define INCLUDED_ENTITY
// Struct for parsing random map data
struct Entity
{
std::wstring templateName;
u16 entityID;
u16 playerID;
float positionX;
float positionZ;
float orientationY;
bool isActor;
};
#endif

View File

@ -0,0 +1,135 @@
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "MapGenerator.h"
#include "lib/timer.h"
#include "ps/CLogger.h"
#include <boost/random/linear_congruential.hpp>
// TODO: what's a good default? perhaps based on map size
#define RMS_RUNTIME_SIZE 96 * 1024 * 1024
boost::rand48 g_MapGenRNG;
CMapGenerator::CMapGenerator() : m_ScriptInterface("RMS", "MapGenerator", ScriptInterface::CreateRuntime(RMS_RUNTIME_SIZE))
{
m_ScriptInterface.SetCallbackData(static_cast<void*> (this));
// Replace RNG with a seeded deterministic function
m_ScriptInterface.ReplaceNondeterministicFunctions(g_MapGenRNG);
// functions for RMS
m_ScriptInterface.RegisterFunction<bool, std::wstring, CMapGenerator::LoadLibrary>("LoadLibrary");
m_ScriptInterface.RegisterFunction<void, CScriptValRooted, CMapGenerator::ExportMap>("ExportMap");
}
CMapGenerator::~CMapGenerator()
{
// Clean up rooted objects before destroying their script context
m_MapData = CScriptValRooted();
}
bool CMapGenerator::GenerateMap(const VfsPath& scriptFile, const CScriptValRooted& settings)
{
TIMER(L"GenerateMap");
// Init RNG seed
uint32 seed;
if (!m_ScriptInterface.GetProperty(settings.get(), "Seed", seed))
{ // No seed specfified
LOGWARNING(L"GenerateMap: No seed value specified - using 0");
seed = 0;
}
g_MapGenRNG.seed(seed);
// Copy settings to script context
if (!m_ScriptInterface.SetProperty(m_ScriptInterface.GetGlobalObject(), "g_MapSettings", settings))
return false;
// Load RMS
LOGMESSAGE(L"Loading RMS '%ls'", scriptFile.c_str());
if (!m_ScriptInterface.LoadGlobalScriptFile(scriptFile))
{
LOGERROR(L"Failed to load RMS '%ls'", scriptFile.c_str());
return false;
}
return true;
}
ScriptInterface& CMapGenerator::GetScriptInterface()
{
return m_ScriptInterface;
}
CScriptValRooted& CMapGenerator::GetMapData()
{
return m_MapData;
}
bool CMapGenerator::LoadLibrary(void* cbdata, std::wstring name)
{
CMapGenerator* self = static_cast<CMapGenerator*> (cbdata);
return self->LoadScripts(name);
}
void CMapGenerator::ExportMap(void* cbdata, CScriptValRooted data)
{
CMapGenerator* self = static_cast<CMapGenerator*> (cbdata);
// Copy results
self->m_MapData = data;
}
bool CMapGenerator::LoadScripts(const std::wstring& libraryName)
{
// Ignore libraries that are already loaded
if (m_LoadedLibraries.find(libraryName) != m_LoadedLibraries.end())
return true;
// Mark this as loaded, to prevent it recursively loading itself
m_LoadedLibraries.insert(libraryName);
VfsPath path = L"maps/random/" + libraryName + L"/";
VfsPaths pathnames;
// Load all scripts in mapgen directory
if (fs_util::GetPathnames(g_VFS, path, L"*.js", pathnames) < 0)
{
LOGERROR(L"Error reading scripts in directory '%ls'", path.c_str());
return false;
}
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
{
LOGMESSAGE(L"Loading map generator script '%ls'", it->c_str());
if (!m_ScriptInterface.LoadGlobalScriptFile(*it))
{
LOGERROR(L"Failed to load script '%ls'", it->c_str());
return false;
}
}
return true;
}

View File

@ -0,0 +1,58 @@
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_MAPGENERATOR
#define INCLUDED_MAPGENERATOR
#include "ps/CStr.h"
#include "ps/FileIo.h"
#include "scriptinterface/ScriptInterface.h"
class CMapGenerator
{
public:
// constructor
CMapGenerator();
// destructor
~CMapGenerator();
// return success of map generation
bool GenerateMap(const VfsPath& scriptFile, const CScriptValRooted& settings);
// accessors
ScriptInterface& GetScriptInterface();
CScriptValRooted& GetMapData();
// callbacks for script functions
static bool LoadLibrary(void* cbdata, std::wstring name);
static void ExportMap(void* cbdata, CScriptValRooted data);
private:
bool LoadScripts(const std::wstring& libraryName);
ScriptInterface m_ScriptInterface;
CScriptValRooted m_MapData;
std::set<std::wstring> m_LoadedLibraries;
};
#endif //INCLUDED_MAPGENERATOR

View File

@ -24,7 +24,7 @@ public:
// current file version given to saved maps
enum { FILE_VERSION = 5 };
// supported file read version - file with version less than this will be reject
enum { FILE_READ_VERSION = 1 };
enum { FILE_READ_VERSION = 5 };
#pragma pack(push, 1)
// description of a tile for I/O purposes

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -21,7 +21,9 @@
#include "graphics/Camera.h"
#include "graphics/CinemaTrack.h"
#include "graphics/Entity.h"
#include "graphics/GameView.h"
#include "graphics/MapGenerator.h"
#include "graphics/Patch.h"
#include "graphics/Terrain.h"
#include "graphics/TerrainTextureEntry.h"
@ -109,10 +111,66 @@ void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_,
if (!only_xml)
RegMemFun(this, &CMapReader::UnpackMap, L"CMapReader::UnpackMap", 1200);
if (file_format_version >= 3) {
// read the corresponding XML file
RegMemFun(this, &CMapReader::ReadXML, L"CMapReader::ReadXML", 5800);
}
// read the corresponding XML file
RegMemFun(this, &CMapReader::ReadXML, L"CMapReader::ReadXML", 5800);
// apply data to the world
RegMemFun(this, &CMapReader::ApplyData, L"CMapReader::ApplyData", 5);
// load map settings script (must be done after reading map)
RegMemFun(this, &CMapReader::LoadMapSettings, L"CMapReader::LoadMapSettings", 5);
RegMemFun(this, &CMapReader::DelayLoadFinished, L"CMapReader::DelayLoadFinished", 5);
}
// LoadRandomMap: try to load the map data; reinitialise the scene to new data if successful
void CMapReader::LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted& settings, CTerrain *pTerrain_,
WaterManager* pWaterMan_, SkyManager* pSkyMan_,
CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_,
CSimulation2 *pSimulation2_, int playerID_)
{
// latch parameters (held until DelayedLoadFinished)
m_ScriptFile = scriptFile;
m_ScriptSettings = settings;
pTerrain = pTerrain_;
pLightEnv = pLightEnv_;
pGameView = pGameView_;
pWaterMan = pWaterMan_;
pSkyMan = pSkyMan_;
pCinema = pCinema_;
pTrigMan = pTrigMan_;
pSimulation2 = pSimulation2_;
m_PlayerID = playerID_;
m_CameraStartupTarget = INVALID_ENTITY;
// delete all existing entities
if (pSimulation2)
pSimulation2->ResetState();
only_xml = false;
// copy random map settings (before entity creation)
RegMemFun(this, &CMapReader::LoadRMSettings, L"CMapReader::LoadRMSettings", 50);
// load player settings script (must be done before reading map)
RegMemFun(this, &CMapReader::LoadPlayerSettings, L"CMapReader::LoadPlayerSettings", 50);
// load map generator with random map script
RegMemFun(this, &CMapReader::GenerateMap, L"CMapReader::GenerateMap", 2000);
// parse RMS results into terrain structure
RegMemFun(this, &CMapReader::ParseTerrain, L"CMapReader::ParseTerrain", 500);
// parse RMS results into environment settings
RegMemFun(this, &CMapReader::ParseEnvironment, L"CMapReader::ParseEnvironment", 5);
// parse RMS results into camera settings
RegMemFun(this, &CMapReader::ParseCamera, L"CMapReader::ParseCamera", 5);
// parse RMS results into entities
RegMemFun(this, &CMapReader::ParseEntities, L"CMapReader::ParseEntities", 1000);
// apply data to the world
RegMemFun(this, &CMapReader::ApplyData, L"CMapReader::ApplyData", 5);
@ -220,12 +278,9 @@ int CMapReader::ApplyData()
}
}
if (file_format_version >= 4)
{
// copy over the lighting parameters
if (pLightEnv)
*pLightEnv = m_LightEnv;
}
// copy over the lighting parameters
if (pLightEnv)
*pLightEnv = m_LightEnv;
if (pGameView)
{
@ -351,7 +406,6 @@ private:
void ReadCamera(XMBElement parent);
void ReadCinema(XMBElement parent);
void ReadTriggers(XMBElement parent);
// void ReadTriggerGroup(XMBElement parent, MapTriggerGroup& group);
int ReadEntities(XMBElement parent, double end_time);
int ReadOldEntities(XMBElement parent, double end_time);
int ReadNonEntities(XMBElement parent, double end_time);
@ -783,174 +837,7 @@ void CXMLReader::ReadCinema(XMBElement parent)
void CXMLReader::ReadTriggers(XMBElement UNUSED(parent))
{
// MapTriggerGroup rootGroup( L"Triggers", L"" );
// if (m_MapReader.pTrigMan)
// m_MapReader.pTrigMan->DestroyEngineTriggers();
// ReadTriggerGroup(parent, rootGroup);
}
/*
void CXMLReader::ReadTriggerGroup(XMBElement parent, MapTriggerGroup& group)
{
#define EL(x) int el_##x = xmb_file.GetElementID(#x)
#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
EL(group);
EL(trigger);
EL(active);
EL(delay);
EL(maxruncount);
EL(conditions);
EL(logicblock);
EL(logicblockend);
EL(condition);
EL(parameter);
EL(linklogic);
EL(effects);
EL(effect);
EL(function);
EL(display);
AT(name);
AT(function);
AT(display);
AT(not);
#undef EL
#undef AT
CStrW name = parent.GetAttributes().GetNamedItem(at_name), parentName = group.parentName;
if ( group.name == L"Triggers" )
name = group.name;
MapTriggerGroup mapGroup(name, parentName);
XERO_ITER_EL(parent, groupChild)
{
int elementName = groupChild.GetNodeName();
if ( elementName == el_group )
ReadTriggerGroup(groupChild, mapGroup);
else if ( elementName == el_trigger )
{
MapTrigger mapTrigger;
mapTrigger.name = CStrW( groupChild.GetAttributes().GetNamedItem(at_name) );
//Read everything in this trigger
XERO_ITER_EL(groupChild, triggerChild)
{
elementName = triggerChild.GetNodeName();
if ( elementName == el_active )
{
if ( CStr("false") == CStr( triggerChild.GetText() ) )
mapTrigger.active = false;
else
mapTrigger.active = true;
}
else if ( elementName == el_maxruncount )
mapTrigger.maxRunCount = CStr( triggerChild.GetText() ).ToInt();
else if ( elementName == el_delay )
mapTrigger.timeValue = CStr( triggerChild.GetText() ).ToFloat();
else if ( elementName == el_conditions )
{
//Read in all conditions for this trigger
XERO_ITER_EL(triggerChild, condition)
{
elementName = condition.GetNodeName();
if ( elementName == el_condition )
{
MapTriggerCondition mapCondition;
mapCondition.name = condition.GetAttributes().GetNamedItem(at_name);
mapCondition.functionName = condition.GetAttributes().GetNamedItem(at_function);
mapCondition.displayName = condition.GetAttributes().GetNamedItem(at_display);
CStr notAtt(condition.GetAttributes().GetNamedItem(at_not));
if ( notAtt == CStr("true") )
mapCondition.negated = true;
//Read in each condition child
XERO_ITER_EL(condition, conditionChild)
{
elementName = conditionChild.GetNodeName();
if ( elementName == el_function )
mapCondition.functionName = CStrW(conditionChild.GetText());
else if ( elementName == el_display )
mapCondition.displayName = CStrW(conditionChild.GetText());
else if ( elementName == el_parameter )
mapCondition.parameters.push_back( conditionChild.GetText() );
else if ( elementName == el_linklogic )
{
CStr logic = conditionChild.GetText();
if ( logic == CStr("AND") )
mapCondition.linkLogic = 1;
else
mapCondition.linkLogic = 2;
}
}
mapTrigger.conditions.push_back(mapCondition);
} //Read all conditions
else if ( elementName == el_logicblock)
{
if ( CStr(condition.GetAttributes().GetNamedItem(at_not)) == CStr("true") )
mapTrigger.AddLogicBlock(true);
else
mapTrigger.AddLogicBlock(false);
}
else if ( elementName == el_logicblockend)
mapTrigger.AddLogicBlockEnd();
} //Read all conditions
}
else if ( elementName == el_effects )
{
//Read all effects
XERO_ITER_EL(triggerChild, effect)
{
if ( effect.GetNodeName() != el_effect )
{
debug_warn(L"Invalid effect tag in trigger XML file");
return;
}
MapTriggerEffect mapEffect;
mapEffect.name = effect.GetAttributes().GetNamedItem(at_name);
//Read parameters
XERO_ITER_EL(effect, effectChild)
{
elementName = effectChild.GetNodeName();
if ( elementName == el_function )
mapEffect.functionName = effectChild.GetText();
else if ( elementName == el_display )
mapEffect.displayName = effectChild.GetText();
else if ( elementName == el_parameter )
mapEffect.parameters.push_back( effectChild.GetText() );
else
{
debug_warn(L"Invalid parameter tag in trigger XML file");
return;
}
}
mapTrigger.effects.push_back(mapEffect);
}
}
else
debug_warn(L"Invalid trigger node child in trigger XML file");
} //Read trigger children
m_MapReader.pTrigMan->AddTrigger(mapGroup, mapTrigger);
}
else
debug_warn(L"Invalid group node child in XML file");
} //Read group children
if (m_MapReader.pTrigMan)
m_MapReader.pTrigMan->AddGroup(mapGroup);
}
*/
int CXMLReader::ReadEntities(XMBElement parent, double end_time)
{
@ -1046,172 +933,6 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time)
return 0;
}
int CXMLReader::ReadOldEntities(XMBElement parent, double end_time)
{
XMBElementList entities = parent.GetChildNodes();
while (entity_idx < entities.Count)
{
// all new state at this scope and below doesn't need to be
// wrapped, since we only yield after a complete iteration.
XMBElement entity = entities.Item(entity_idx++);
debug_assert(entity.GetNodeName() == el_entity);
XMBAttributeList attrs = entity.GetAttributes();
CStrW TemplateName;
int PlayerID = 0;
CFixedVector3D Position;
fixed Orientation;
XERO_ITER_EL(entity, setting)
{
int element_name = setting.GetNodeName();
// <template>
if (element_name == el_template)
{
TemplateName = setting.GetText().FromUTF8();
}
// <player>
else if (element_name == el_player)
{
PlayerID = setting.GetText().ToInt();
}
// <position>
else if (element_name == el_position)
{
XMBAttributeList attrs = setting.GetAttributes();
Position = CFixedVector3D(
fixed::FromString(attrs.GetNamedItem(at_x)),
fixed::FromString(attrs.GetNamedItem(at_y)),
fixed::FromString(attrs.GetNamedItem(at_z)));
}
// <orientation>
else if (element_name == el_orientation)
{
XMBAttributeList attrs = setting.GetAttributes();
Orientation = fixed::FromString(attrs.GetNamedItem(at_angle));
}
else
debug_warn(L"Invalid map XML data");
}
// The old version uses a flat entity naming system, so we need
// to translate it into the hierarchical filename
if (TemplateName.Find(L"flora") == 0 || TemplateName.Find(L"fauna") == 0 || TemplateName.Find(L"geology") == 0 || TemplateName.Find(L"special") == 0)
TemplateName = L"gaia/" + TemplateName;
else if (TemplateName.Find(L"cart") == 0 || TemplateName.Find(L"celt") == 0 || TemplateName.Find(L"hele") == 0 ||
TemplateName.Find(L"iber") == 0 || TemplateName.Find(L"pers") == 0 || TemplateName.Find(L"rome") == 0)
{
if (TemplateName.Find(L"cavalry") == 5 || TemplateName.Find(L"hero") == 5 || TemplateName.Find(L"infantry") == 5 ||
TemplateName.Find(L"mechanical") == 5 || TemplateName.Find(L"ship") == 5 || TemplateName.Find(L"super") == 5 || TemplateName.Find(L"support") == 5)
TemplateName = L"units/" + TemplateName;
else
TemplateName = L"structures/" + TemplateName;
}
else if (TemplateName.Find(L"skeleton") == 0)
TemplateName = L"units/" + TemplateName;
else if (TemplateName.Find(L"camp") == 0 || TemplateName.Find(L"fence") == 0 || TemplateName.Find(L"temp") == 0)
TemplateName = L"other/" + TemplateName;
entity_id_t ent = m_MapReader.pSimulation2->AddEntity(TemplateName);
if (ent != INVALID_ENTITY)
{
CmpPtr<ICmpPosition> cmpPosition(*m_MapReader.pSimulation2, ent);
if (!cmpPosition.null())
{
cmpPosition->JumpTo(Position.X, Position.Z);
cmpPosition->SetYRotation(Orientation);
}
CmpPtr<ICmpOwnership> cmpOwner(*m_MapReader.pSimulation2, ent);
if (!cmpOwner.null())
cmpOwner->SetOwner(PlayerID);
if (m_MapReader.m_CameraStartupTarget == INVALID_ENTITY && !cmpPosition.null())
{
// Special-case civil centre files to initialise the camera.
if (PlayerID == m_MapReader.m_PlayerID && boost::algorithm::ends_with(TemplateName, L"civil_centre"))
{
m_MapReader.m_CameraStartupTarget = ent;
}
}
}
completed_jobs++;
LDR_CHECK_TIMEOUT(completed_jobs, total_jobs);
}
return 0;
}
int CXMLReader::ReadNonEntities(XMBElement parent, double end_time)
{
XMBElementList nonentities = parent.GetChildNodes();
while (nonentity_idx < nonentities.Count)
{
// all new state at this scope and below doesn't need to be
// wrapped, since we only yield after a complete iteration.
XMBElement nonentity = nonentities.Item(nonentity_idx++);
debug_assert(nonentity.GetNodeName() == el_nonentity);
CStrW ActorName;
CFixedVector3D Position;
fixed Orientation;
XERO_ITER_EL(nonentity, setting)
{
int element_name = setting.GetNodeName();
// <actor>
if (element_name == el_actor)
{
ActorName = setting.GetText().FromUTF8();
}
// <position>
else if (element_name == el_position)
{
XMBAttributeList attrs = setting.GetAttributes();
Position = CFixedVector3D(
fixed::FromString(attrs.GetNamedItem(at_x)),
fixed::FromString(attrs.GetNamedItem(at_y)),
fixed::FromString(attrs.GetNamedItem(at_z)));
}
// <orientation>
else if (element_name == el_orientation)
{
XMBAttributeList attrs = setting.GetAttributes();
Orientation = fixed::FromString(attrs.GetNamedItem(at_angle));
}
else
debug_warn(L"Invalid map XML data");
}
std::set<CStr> selections; // TODO: read from file
entity_id_t ent = m_MapReader.pSimulation2->AddEntity(L"actor|" + ActorName);
if (ent != INVALID_ENTITY)
{
CmpPtr<ICmpPosition> cmpPos(*m_MapReader.pSimulation2, ent);
if (!cmpPos.null())
{
cmpPos->JumpTo(Position.X, Position.Z);
cmpPos->SetYRotation(Orientation);
}
}
completed_jobs++;
LDR_CHECK_TIMEOUT(completed_jobs, total_jobs);
}
return 0;
}
int CXMLReader::ProgressiveRead()
{
// yield after this time is reached. balances increased progress bar
@ -1240,18 +961,6 @@ int CXMLReader::ProgressiveRead()
{
//Already loaded - this is to prevent an assertion
}
else if (m_MapReader.file_format_version <= 4 && name == "Entities")
{
ret = ReadOldEntities(node, end_time);
if (ret != 0) // error or timed out
return ret;
}
else if (m_MapReader.file_format_version <= 4 && name == "Nonentities")
{
ret = ReadNonEntities(node, end_time);
if (ret != 0) // error or timed out
return ret;
}
else if (name == "Entities")
{
ret = ReadEntities(node, end_time);
@ -1338,3 +1047,264 @@ int CMapReader::DelayLoadFinished()
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int CMapReader::LoadRMSettings()
{
// copy random map settings over to sim
pSimulation2->SetMapSettings(m_ScriptSettings);
return 0;
}
int CMapReader::GenerateMap()
{
CMapGenerator mapGen;
VfsPath scriptPath;
if (m_ScriptFile.length())
scriptPath = L"maps/random/"+m_ScriptFile;
// Copy map settings from simulator to mapgen context
CScriptValRooted scriptSettings(mapGen.GetScriptInterface().GetContext(), mapGen.GetScriptInterface().CloneValueFromOtherContext(pSimulation2->GetScriptInterface(), m_ScriptSettings.get()));
// Try to generate map
if (!mapGen.GenerateMap(scriptPath, scriptSettings))
{ // RMS failed
// TODO: Need to do something safe here, like cancel loading and return to main menu
LOGERROR(L"Map generation failed: RMS returned undefined");
return -1;
}
if (mapGen.GetMapData().undefined())
{
LOGERROR(L"undefined map data");
}
// Copy data from mapgen to simulator context
m_MapData = CScriptValRooted(pSimulation2->GetScriptInterface().GetContext(), pSimulation2->GetScriptInterface().CloneValueFromOtherContext(mapGen.GetScriptInterface(), mapGen.GetMapData().get()));
return 0;
};
int CMapReader::ParseTerrain()
{
// parse terrain from map data
#define GET_TERRAIN_PROPERTY(prop, out)\
if (!pSimulation2->GetScriptInterface().GetProperty(m_MapData.get(), #prop, out))\
LOGERROR(L"CMapReader::ParseTerrain() failed to get '%hs' property", #prop);\
size_t size;
GET_TERRAIN_PROPERTY(size, size)
m_PatchesPerSide = size / PATCH_SIZE;
// flat heightmap of u16 data
GET_TERRAIN_PROPERTY(height, m_Heightmap)
// load textures
GET_TERRAIN_PROPERTY(numTextures, num_terrain_tex)
std::vector<std::string> textureNames;
GET_TERRAIN_PROPERTY(textureNames, textureNames)
while (cur_terrain_tex < num_terrain_tex)
{
debug_assert(CTerrainTextureManager::IsInitialised()); // we need this for the terrain properties (even when graphics are disabled)
CTerrainTextureEntry* texentry = g_TexMan.FindTexture(textureNames[cur_terrain_tex]);
m_TerrainTextures.push_back(texentry);
cur_terrain_tex++;
}
// build tile data
m_Tiles.resize(SQR(size));
std::vector<CMapIO::STileDesc> tileData;
GET_TERRAIN_PROPERTY(tileData, tileData)
for (size_t i = 0; i < tileData.size(); ++i)
{
m_Tiles[i] = tileData[i];
}
// reset generator state
cur_terrain_tex = 0;
#undef GET_TERRAIN_PROPERTY
return 0;
}
int CMapReader::ParseEntities()
{
// parse entities from map data
std::vector<Entity> entities;
if (!pSimulation2->GetScriptInterface().GetProperty(m_MapData.get(), "entities", entities))
LOGWARNING(L"CMapReader::ParseEntities() failed to get 'entities' property");
size_t entity_idx = 0;
size_t num_entities = entities.size();
Entity currEnt;
while (entity_idx < num_entities)
{
// Get current entity struct
currEnt = entities[entity_idx];
entity_id_t ent = pSimulation2->AddEntity(currEnt.templateName, currEnt.entityID);
// Check that entity was added
if (ent == INVALID_ENTITY)
{
LOGERROR(L"Failed to load entity template '%ls'", currEnt.templateName.c_str());
}
else
{
CmpPtr<ICmpPosition> cmpPosition(*pSimulation2, ent);
if (!cmpPosition.null())
{
cmpPosition->JumpTo(entity_pos_t::FromFloat(currEnt.positionX), entity_pos_t::FromFloat(currEnt.positionZ));
cmpPosition->SetYRotation(entity_angle_t::FromFloat(currEnt.orientationY));
// TODO: other parts of the position
}
CmpPtr<ICmpOwnership> cmpOwner(*pSimulation2, ent);
if (!cmpOwner.null())
cmpOwner->SetOwner(currEnt.playerID);
if (boost::algorithm::ends_with(currEnt.templateName, L"civil_centre"))
{
// HACK: we special-case civil centre files to initialise the camera.
// This ought to be based on a more generic mechanism for indicating
// per-player camera start locations.
if (m_CameraStartupTarget == INVALID_ENTITY && currEnt.playerID == m_PlayerID && !cmpPosition.null())
m_CameraStartupTarget = ent;
}
}
entity_idx++;
}
return 0;
}
int CMapReader::ParseEnvironment()
{
// parse environment settings from map data
#define GET_ENVIRONMENT_PROPERTY(val, prop, out)\
if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\
LOGWARNING(L"CMapReader::ParseEnvironment() failed to get '%hs' property", #prop);\
CScriptValRooted envObj;
GET_ENVIRONMENT_PROPERTY(m_MapData.get(), Environment, envObj)
if (envObj.undefined())
{
LOGWARNING(L"CMapReader::ParseEnvironment(): Environment settings not found");
return 0;
}
std::wstring skySet;
GET_ENVIRONMENT_PROPERTY(envObj.get(), SkySet, skySet)
pSkyMan->SetSkySet(skySet);
GET_ENVIRONMENT_PROPERTY(envObj.get(), SunColour, m_LightEnv.m_SunColor)
GET_ENVIRONMENT_PROPERTY(envObj.get(), SunElevation, m_LightEnv.m_Elevation)
GET_ENVIRONMENT_PROPERTY(envObj.get(), SunRotation, m_LightEnv.m_Rotation)
GET_ENVIRONMENT_PROPERTY(envObj.get(), TerrainAmbientColour, m_LightEnv.m_TerrainAmbientColor)
GET_ENVIRONMENT_PROPERTY(envObj.get(), UnitsAmbientColour, m_LightEnv.m_UnitsAmbientColor)
// Water properties
CScriptValRooted waterObj;
GET_ENVIRONMENT_PROPERTY(envObj.get(), Water, waterObj)
CScriptValRooted waterBodyObj;
GET_ENVIRONMENT_PROPERTY(waterObj.get(), WaterBody, waterBodyObj)
// Water level - necessary
float waterHeight;
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Height, waterHeight)
CmpPtr<ICmpWaterManager> cmpWaterMan(*pSimulation2, SYSTEM_ENTITY);
debug_assert(!cmpWaterMan.null());
cmpWaterMan->SetWaterLevel(entity_pos_t::FromFloat(waterHeight));
// If we have graphics, get rest of settings
if (pWaterMan)
{
std::wstring waterType;
// TODO: Water type not implemented
//GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Type, waterType)
RGBColor waterColour;
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Colour, waterColour)
pWaterMan->m_WaterColor = CColor(waterColour.X, waterColour.Y, waterColour.Z, 1.0f);
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Shininess, pWaterMan->m_Shininess)
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Waviness, pWaterMan->m_Waviness)
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Murkiness, pWaterMan->m_Murkiness)
RGBColor waterTint;
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Tint, waterTint)
pWaterMan->m_WaterTint = CColor(waterTint.X, waterTint.Y, waterTint.Z, 1.0f);
RGBColor reflectTint;
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), ReflectionTint, reflectTint)
pWaterMan->m_ReflectionTint = CColor(reflectTint.X, reflectTint.Y, reflectTint.Z, 1.0f);
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), ReflectionTintStrength, pWaterMan->m_ReflectionTintStrength)
}
m_LightEnv.CalculateSunDirection();
#undef GET_ENVIRONMENT_PROPERTY
return 0;
}
int CMapReader::ParseCamera()
{
// parse camera settings from map data
// defaults if we don't find camera
float declination = DEGTORAD(30.f), rotation = DEGTORAD(-45.f);
CVector3D translation = CVector3D(100, 150, -100);
#define GET_CAMERA_PROPERTY(val, prop, out)\
if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\
LOGWARNING(L"CMapReader::ParseCamera() failed to get '%hs' property", #prop);\
CScriptValRooted cameraObj;
GET_CAMERA_PROPERTY(m_MapData.get(), Camera, cameraObj)
if (!cameraObj.undefined())
{ // If camera property exists, read values
CFixedVector3D pos;
GET_CAMERA_PROPERTY(cameraObj.get(), Position, pos)
translation = pos;
GET_CAMERA_PROPERTY(cameraObj.get(), Rotation, rotation)
GET_CAMERA_PROPERTY(cameraObj.get(), Declination, declination)
}
#undef GET_CAMERA_PROPERTY
if (pGameView)
{
pGameView->GetCamera()->m_Orientation.SetXRotation(declination);
pGameView->GetCamera()->m_Orientation.RotateY(rotation);
pGameView->GetCamera()->m_Orientation.Translate(translation);
pGameView->GetCamera()->UpdateFrustum();
}
return 0;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -81,6 +81,25 @@ private:
// clean up everything used during delayed load
int DelayLoadFinished();
// Copy random map settings over to sim
int LoadRMSettings();
// Generate random map
int GenerateMap();
// Parse script data into terrain
int ParseTerrain();
// Parse script data into entities
int ParseEntities();
// Parse script data into environment
int ParseEnvironment();
// Parse script data into camera
int ParseCamera();
// size of map
ssize_t m_PatchesPerSide;
@ -95,6 +114,11 @@ private:
// startup script
CStrW m_Script;
// random map data
CStrW m_ScriptFile;
CScriptValRooted m_ScriptSettings;
CScriptValRooted m_MapData;
// state latched by LoadMap and held until DelayedLoadFinished
CFileUnpacker unpacker;
CTerrain* pTerrain;

View File

@ -142,7 +142,14 @@ void CGame::RegisterInit(const CScriptValRooted& attribs)
}
else if (mapType == "random")
{
// TODO: Coming in another patch
// Load random map attributes
std::wstring scriptFile;
CScriptValRooted settings;
m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "script", scriptFile);
m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "settings", settings);
m_World->RegisterInitRMS(scriptFile, settings, m_PlayerID);
}
LDR_EndRegistering();

View File

@ -957,28 +957,62 @@ static bool Autostart(const CmdLineArgs& args)
* -autostart=mapname -- single-player
* -autostart=mapname -autostart-playername=Player -autostart-host -autostart-players=2 -- multiplayer host, wait for 2 players
* -autostart=mapname -autostart-playername=Player -autostart-client -autostart-ip=127.0.0.1 -- multiplayer client, connect to 127.0.0.1
* -autostart=scriptname -autostart-random
*/
CStr autostartMap = args.Get("autostart");
if (autostartMap.empty())
CStr autoStartName = args.Get("autostart");
if (autoStartName.empty())
return false;
g_Game = new CGame();
ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
CScriptValRooted attrs;
scriptInterface.Eval("({})", attrs);
scriptInterface.SetProperty(attrs.get(), "mapType", std::string("scenario"));
scriptInterface.SetProperty(attrs.get(), "map", std::string(autostartMap));
CScriptVal settings;
scriptInterface.Eval("({})", settings);
CScriptVal playerData;
scriptInterface.Eval("([])", playerData);
// Set attrs.settings = { PlayerData: [ { AI: ... }, ... ] }:
// Set different attributes for random or scenario game
if (args.Has("autostart-random"))
{
scriptInterface.SetProperty(attrs.get(), "script", std::string(autoStartName), false); // RMS name
scriptInterface.SetProperty(attrs.get(), "mapType", std::string("random"), false);
// For random map, there are special settings
// TODO: Get these from command line - using defaults for now
scriptInterface.SetProperty(settings.get(), "Size", 16); // Random map size (in patches)
scriptInterface.SetProperty(settings.get(), "Seed", 0); // Random seed
scriptInterface.SetProperty(settings.get(), "BaseTerrain", std::string("grass1_spring")); // Base terrain texture
scriptInterface.SetProperty(settings.get(), "BaseHeight", 0); // Base terrain height
// Define players
// TODO: Get these from command line - using defaults for now
size_t numPlayers = 2;
for (size_t i = 0; i < numPlayers; ++i)
{
CScriptVal player;
scriptInterface.Eval("({})", player);
scriptInterface.SetProperty(player.get(), "Civ", std::string("hele"));
scriptInterface.SetPropertyInt(playerData.get(), i, player);
}
}
else
{
scriptInterface.SetProperty(attrs.get(), "map", std::string(autoStartName), false);
scriptInterface.SetProperty(attrs.get(), "mapType", std::string("scenario"), false);
}
// Set player data for AIs
// attrs.settings = { PlayerData: [ { AI: ... }, ... ] }:
/*
* Handle command-line options for AI:
* -autostart-ai=1:dummybot -autostart-ai=2:dummybot -- adds the dummybot AI to players 1 and 2
*/
CScriptVal playerData;
scriptInterface.Eval("([])", playerData);
if (args.Has("autostart-ai"))
{
std::vector<CStr> aiArgs = args.GetMultiple("autostart-ai");
@ -995,13 +1029,12 @@ static bool Autostart(const CmdLineArgs& args)
}
}
CScriptVal settings;
scriptInterface.Eval("({})", settings);
// Add player data to map settings
scriptInterface.SetProperty(settings.get(), "PlayerData", playerData);
// Add map settings to game attributes
scriptInterface.SetProperty(attrs.get(), "settings", settings);
CScriptVal mpInitData;
g_GUI->GetScriptInterface().Eval("({isNetworked:true, playerAssignments:{}})", mpInitData);
g_GUI->GetScriptInterface().SetProperty(mpInitData.get(), "attribs",

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -94,6 +94,32 @@ void CWorld::RegisterInit(const CStrW& mapFile, int playerID)
}
}
void CWorld::RegisterInitRMS(const CStrW& scriptFile, const CScriptValRooted& settings, int playerID)
{
// If scriptFile is empty, a blank map will be generated using settings (no RMS run)
CMapReader* reader = 0;
try
{
reader = new CMapReader;
CTriggerManager* pTriggerManager = NULL;
reader->LoadRandomMap(scriptFile, settings, m_Terrain,
CRenderer::IsInitialised() ? g_Renderer.GetWaterManager() : NULL,
CRenderer::IsInitialised() ? g_Renderer.GetSkyManager() : NULL,
&g_LightEnv, m_pGame->GetView(),
m_pGame->GetView() ? m_pGame->GetView()->GetCinema() : NULL,
pTriggerManager, m_pGame->GetSimulation2(), playerID);
// fails immediately, or registers for delay loading
}
catch (PSERROR_File& err)
{
delete reader;
LOGERROR(L"Failed to generate random map %ls: %hs", scriptFile.c_str(), err.what());
throw PSERROR_Game_World_MapLoadFailed();
}
}
/**
* Destructor.
*

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -25,6 +25,7 @@
#define INCLUDED_WORLD
#include "ps/Errors.h"
#include "scriptinterface/ScriptInterface.h"
#ifndef ERROR_GROUP_GAME_DEFINED
#define ERROR_GROUP_GAME_DEFINED
@ -74,6 +75,11 @@ public:
*/
void RegisterInit(const CStrW& mapFile, int playerID);
/*
Initialize the World - generate and load the random map
*/
void RegisterInitRMS(const CStrW& scriptFile, const CScriptValRooted& settings, int playerID);
/**
* Get the pointer to the terrain object.
*

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -19,6 +19,9 @@
#include "ScriptInterface.h"
#include "graphics/Color.h"
#include "graphics/Entity.h"
#include "graphics/MapIO.h"
#include "ps/utf16string.h"
#include "ps/CLogger.h"
#include "ps/CStr.h"
@ -84,6 +87,16 @@ template<> bool ScriptInterface::FromJSVal<u32>(JSContext* cx, jsval v, u32& out
return true;
}
template<> bool ScriptInterface::FromJSVal<u16>(JSContext* cx, jsval v, u16& out)
{
uint16 ret;
WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
if (!JS_ValueToUint16(cx, v, &ret))
return false;
out = ret;
return true;
}
// NOTE: we can't define a jsval specialisation, because that conflicts with integer types
template<> bool ScriptInterface::FromJSVal<CScriptVal>(JSContext* UNUSED(cx), jsval v, CScriptVal& out)
{
@ -122,6 +135,69 @@ template<> bool ScriptInterface::FromJSVal<std::string>(JSContext* cx, jsval v,
return true;
}
template<> bool ScriptInterface::FromJSVal<Entity>(JSContext* cx, jsval v, Entity& out)
{
JSObject* obj;
if (!JS_ValueToObject(cx, v, &obj) || obj == NULL)
FAIL("Argument must be an object");
jsval name, id, player, x, z, orient, actor;
if(!JS_GetProperty(cx, obj, "isActor", &actor) || !FromJSVal(cx, actor, out.isActor))
FAIL("Failed to read Entity.isActor property");
if (!out.isActor)
if(!JS_GetProperty(cx, obj, "player", &player) || !FromJSVal(cx, player, out.playerID))
FAIL("Failed to read Entity.player property");
if (!JS_GetProperty(cx, obj, "name", &name) || !FromJSVal(cx, name, out.templateName))
FAIL("Failed to read Entity.name property");
if (!JS_GetProperty(cx, obj, "id", &id) || !FromJSVal(cx, id, out.entityID))
FAIL("Failed to read Entity.id property");
if (!JS_GetProperty(cx, obj, "x", &x) || !FromJSVal(cx, x, out.positionX))
FAIL("Failed to read Entity.x property");
if (!JS_GetProperty(cx, obj, "y", &z) || !FromJSVal(cx, z, out.positionZ))
FAIL("Failed to read Entity.y property");
if (!JS_GetProperty(cx, obj, "orientation", &orient) || !FromJSVal(cx, orient, out.orientationY))
FAIL("Failed to read Entity.orientation property");
return true;
}
template<> bool ScriptInterface::FromJSVal<RGBColor>(JSContext* cx, jsval v, RGBColor& out)
{
JSObject* obj;
if (!JS_ValueToObject(cx, v, &obj) || obj == NULL)
FAIL("Argument must be an object");
jsval r, g, b;
if (!JS_GetProperty(cx, obj, "r", &r) || !FromJSVal(cx, r, out.X))
FAIL("Failed to read RGBColor.r property");
if (!JS_GetProperty(cx, obj, "g", &g) || !FromJSVal(cx, g, out.Y))
FAIL("Failed to read RGBColor.g property");
if (!JS_GetProperty(cx, obj, "b", &b) || !FromJSVal(cx, b, out.Z))
FAIL("Failed to read RGBColor.b property");
return true;
}
template<> bool ScriptInterface::FromJSVal<CMapIO::STileDesc>(JSContext* cx, jsval v, CMapIO::STileDesc& out)
{
JSObject* obj;
if (!JS_ValueToObject(cx, v, &obj) || obj == NULL)
FAIL("Argument must be an object");
jsval texIdx1, texIdx2, priority;
if (!JS_GetProperty(cx, obj, "texIdx1", &texIdx1) || !FromJSVal(cx, texIdx1, out.m_Tex1Index))
FAIL("Failed to read CMapIO::STileDesc.m_Tex1Index property");
if (!JS_GetProperty(cx, obj, "texIdx2", &texIdx2) || !FromJSVal(cx, texIdx2, out.m_Tex2Index))
FAIL("Failed to read CMapIO::STileDesc.m_Tex2Index property");
if (!JS_GetProperty(cx, obj, "priority", &priority) || !FromJSVal(cx, priority, out.m_Priority))
FAIL("Failed to read CMapIO::STileDesc.m_Priority property");
return true;
}
////////////////////////////////////////////////////////////////
// Primitive types:
@ -267,12 +343,24 @@ template<typename T> static bool FromJSVal_vector(JSContext* cx, jsval v, std::v
VECTOR(int)
VECTOR(u32)
VECTOR(u16)
VECTOR(std::string)
VECTOR(std::wstring)
VECTOR(CScriptValRooted)
class IComponent;
template<> jsval ScriptInterface::ToJSVal<std::vector<IComponent*> >(JSContext* cx, const std::vector<IComponent*>& val)
{
return ToJSVal_vector(cx, val);
}
template<> bool ScriptInterface::FromJSVal<std::vector<Entity> >(JSContext* cx, jsval v, std::vector<Entity>& out)
{
return FromJSVal_vector(cx, v, out);
}
template<> bool ScriptInterface::FromJSVal<std::vector<CMapIO::STileDesc> >(JSContext* cx, jsval v, std::vector<CMapIO::STileDesc>& out)
{
return FromJSVal_vector(cx, v, out);
}

View File

@ -43,8 +43,7 @@
#include "valgrind.h"
const int RUNTIME_SIZE = 16 * 1024 * 1024; // TODO: how much memory is needed?
const int STACK_CHUNK_SIZE = 8192;
#define STACK_CHUNK_SIZE 8192
#if ENABLE_SCRIPT_PROFILING
# define signbit std::signbit
@ -57,10 +56,10 @@ const int STACK_CHUNK_SIZE = 8192;
class ScriptRuntime
{
public:
ScriptRuntime() :
ScriptRuntime(int runtimeSize) :
m_rooter(NULL)
{
m_rt = JS_NewRuntime(RUNTIME_SIZE);
m_rt = JS_NewRuntime(runtimeSize);
debug_assert(m_rt); // TODO: error handling
#if ENABLE_SCRIPT_PROFILING
@ -211,9 +210,9 @@ private:
}
};
shared_ptr<ScriptRuntime> ScriptInterface::CreateRuntime()
shared_ptr<ScriptRuntime> ScriptInterface::CreateRuntime(int runtimeSize)
{
return shared_ptr<ScriptRuntime>(new ScriptRuntime);
return shared_ptr<ScriptRuntime>(new ScriptRuntime(runtimeSize));
}
////////////////////////////////////////////////////////////////
@ -438,8 +437,8 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const sh
#endif
// Some other JIT flags to experiment with:
// options |= JSOPTION_JIT;
// options |= JSOPTION_PROFILING;
options |= JSOPTION_JIT;
options |= JSOPTION_PROFILING;
JS_SetOptions(m_cx, options);

View File

@ -40,6 +40,10 @@ namespace boost { class rand48; }
// but as large as necessary for all wrapped functions)
#define SCRIPT_INTERFACE_MAX_ARGS 6
// TODO: what's a good default?
#define DEFAULT_RUNTIME_SIZE 16 * 1024 * 1024
#ifdef NDEBUG
#define ENABLE_SCRIPT_PROFILING 0
#else
@ -67,8 +71,9 @@ public:
* ScriptInterfaces contexts. Values created in one context may be used
* in any other context from the same runtime (but not any other runtime).
* Each runtime should only ever be used on a single thread.
* @param runtimeSize Maximum size in bytes of the new runtime
*/
static shared_ptr<ScriptRuntime> CreateRuntime();
static shared_ptr<ScriptRuntime> CreateRuntime(int runtimeSize = DEFAULT_RUNTIME_SIZE);
/**
* Constructor.