forked from 0ad/0ad
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:
parent
23ebe32b4c
commit
0e0ed94926
@ -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
|
||||
|
@ -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>
|
||||
|
||||
|
@ -1,4 +0,0 @@
|
||||
const SIZE = 64;
|
||||
|
||||
init(SIZE, "grass dirt 50", 0);
|
||||
|
8
binaries/data/mods/public/maps/random/blank.js
Normal file
8
binaries/data/mods/public/maps/random/blank.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Blank map, possibly useful for Atlas or as fallback
|
||||
|
||||
RMS.LoadLibrary("rmgen");
|
||||
|
||||
InitMap();
|
||||
|
||||
// Export map data
|
||||
ExportMap();
|
@ -1 +0,0 @@
|
||||
init(256, "grass_mediterranean_green_50", 0);
|
@ -1 +0,0 @@
|
||||
init(400, "grass_mediterranean_green_50", 0);
|
@ -1 +0,0 @@
|
||||
init(512, "grass_mediterranean_green_50", 0);
|
@ -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();
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
10
binaries/data/mods/public/maps/random/latium.json
Normal file
10
binaries/data/mods/public/maps/random/latium.json
Normal 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"
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
60
binaries/data/mods/public/maps/random/new_rms_test.js
Normal file
60
binaries/data/mods/public/maps/random/new_rms_test.js
Normal 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();
|
10
binaries/data/mods/public/maps/random/new_rms_test.json
Normal file
10
binaries/data/mods/public/maps/random/new_rms_test.json
Normal 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"
|
||||
}
|
||||
}
|
4
binaries/data/mods/public/maps/random/rmgen/area.js
Normal file
4
binaries/data/mods/public/maps/random/rmgen/area.js
Normal file
@ -0,0 +1,4 @@
|
||||
function Area(points)
|
||||
{
|
||||
this.points = (points !== undefined ? points : []);
|
||||
}
|
99
binaries/data/mods/public/maps/random/rmgen/constraint.js
Normal file
99
binaries/data/mods/public/maps/random/rmgen/constraint.js
Normal 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);
|
||||
};
|
22
binaries/data/mods/public/maps/random/rmgen/entity.js
Normal file
22
binaries/data/mods/public/maps/random/rmgen/entity.js
Normal 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);
|
||||
}
|
319
binaries/data/mods/public/maps/random/rmgen/library.js
Normal file
319
binaries/data/mods/public/maps/random/rmgen/library.js
Normal 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);
|
||||
}
|
325
binaries/data/mods/public/maps/random/rmgen/map.js
Normal file
325
binaries/data/mods/public/maps/random/rmgen/map.js
Normal 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;
|
||||
};
|
67
binaries/data/mods/public/maps/random/rmgen/mapgen.js
Normal file
67
binaries/data/mods/public/maps/random/rmgen/mapgen.js
Normal 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);
|
||||
}
|
141
binaries/data/mods/public/maps/random/rmgen/noise.js
Normal file
141
binaries/data/mods/public/maps/random/rmgen/noise.js
Normal 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;
|
||||
};
|
379
binaries/data/mods/public/maps/random/rmgen/painter.js
Normal file
379
binaries/data/mods/public/maps/random/rmgen/painter.js
Normal 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);
|
||||
}
|
||||
};
|
291
binaries/data/mods/public/maps/random/rmgen/placer.js
Normal file
291
binaries/data/mods/public/maps/random/rmgen/placer.js
Normal 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;
|
||||
};
|
||||
|
5
binaries/data/mods/public/maps/random/rmgen/point.js
Normal file
5
binaries/data/mods/public/maps/random/rmgen/point.js
Normal file
@ -0,0 +1,5 @@
|
||||
function Point(x, y)
|
||||
{
|
||||
this.x = (x !== undefined ? x : 0);
|
||||
this.y = (y !== undefined ? y : 0);
|
||||
}
|
53
binaries/data/mods/public/maps/random/rmgen/random.js
Normal file
53
binaries/data/mods/public/maps/random/rmgen/random.js
Normal 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;
|
||||
}
|
||||
}
|
57
binaries/data/mods/public/maps/random/rmgen/terrain.js
Normal file
57
binaries/data/mods/public/maps/random/rmgen/terrain.js
Normal 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);
|
||||
};
|
141
binaries/data/mods/public/maps/random/rmgen/tileclass.js
Normal file
141
binaries/data/mods/public/maps/random/rmgen/tileclass.js
Normal 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);
|
||||
};
|
135
binaries/data/mods/public/maps/random/rmgen/vector.js
Normal file
135
binaries/data/mods/public/maps/random/rmgen/vector.js
Normal 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;
|
||||
};
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -1,5 +0,0 @@
|
||||
//TODO: Move to some library file?
|
||||
|
||||
|
||||
initFromScenario("mediterannean", LOAD_ALL);
|
||||
|
@ -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;
|
||||
}
|
@ -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
34
source/graphics/Entity.h
Normal 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
|
135
source/graphics/MapGenerator.cpp
Normal file
135
source/graphics/MapGenerator.cpp
Normal 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;
|
||||
}
|
58
source/graphics/MapGenerator.h
Normal file
58
source/graphics/MapGenerator.h
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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",
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user