Ticket #3764: realistic_terrain_demo2016-1-31b.diff

File realistic_terrain_demo2016-1-31b.diff, 117.6 KB (added by FeXoR, 8 years ago)
  • binaries/data/mods/public/maps/random/BaseTerrainDiamondSquare.js

     
     1/**
     2*   ToDo:
     3*   Add resources at start locations
     4*   Add stone and metal
     5*   Place start locations of one team close to each other
     6*
     7*   Add parameters for match setup screen (for later use and demonstration)
     8*
     9*   Extract some functions to an RMGEN lib
     10*   Sort out unneeded functions calling other functions
     11*   Clean up
     12*/
     13
     14RMS.LoadLibrary("rmgen");
     15
     16const BUILDING_ANGlE = -PI/4;
     17
     18/**
     19* initialize map
     20*/
     21
     22log("Initializing map...");
     23
     24InitMap();
     25
     26var numPlayers = g_MapSettings.PlayerData.length;
     27var mapSize = getMapSize();
     28
     29
     30/**
     31* Actually do stuff
     32*/
     33
     34/**
     35* Set target min and max height depending on map size to make average steepness about the same on all map sizes
     36*/
     37var heightRange = {"min": MIN_HEIGHT * (mapSize + 512) / 1024 / 8, "max": MAX_HEIGHT * (mapSize + 512) / 1024 / 8};
     38
     39/**
     40* Set average water coverage
     41*/
     42var averageWaterCoverage = 1/5; // NOTE: Since erosion is not predictable actual water coverage might vary much with the same values
     43var waterHeight = -MIN_HEIGHT + heightRange.min + averageWaterCoverage * (heightRange.max - heightRange.min);
     44var waterHeightAdjusted = waterHeight + MIN_HEIGHT;
     45setWaterHeight(waterHeight);
     46
     47/**
     48*  Generate initial reliefmap
     49*/
     50setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, 0.5);
     51
     52
     53/**
     54* Apply simple erosion
     55*/
     56for (var i = 0; i < 5; i++)
     57    globalSmoothHeightmap(0.5);
     58
     59
     60/**
     61* Place start locations and apply terrain texture and decorative props by height
     62*/
     63
     64/**
     65* Set random biome
     66*/
     67randomizeBiome();
     68
     69/**
     70* Define texture textures and decorative props (and density) by height
     71*/
     72var textueByHeight = [];
     73textueByHeight.push({"height": heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), "texture": rbt15, "actor": [rbe9, 0.2]});
     74textueByHeight.push({"height": heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), "texture": rbt15, "actor": [undefined, 0]});
     75textueByHeight.push({"height": heightRange.min + 3/3 * (waterHeightAdjusted - heightRange.min), "texture": rbt15, "actor": [rba3, 0.2]});
     76textueByHeight.push({"height": waterHeightAdjusted + 1/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt14, "actor": [rbe5, 0.1]});
     77textueByHeight.push({"height": waterHeightAdjusted + 2/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt8, "actor": [undefined, 0]});
     78textueByHeight.push({"height": waterHeightAdjusted + 3/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt9, "actor": [undefined, 0]});
     79textueByHeight.push({"height": waterHeightAdjusted + 4/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt1, "actor": [undefined, 0]});
     80textueByHeight.push({"height": waterHeightAdjusted + 5/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt5, "actor": [rbe3, 0.2]});
     81textueByHeight.push({"height": waterHeightAdjusted + 6/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt2, "actor": [rbe1, 0.4]});
     82textueByHeight.push({"height": waterHeightAdjusted + 7/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt3, "actor": [rbe2, 0.4]});
     83textueByHeight.push({"height": waterHeightAdjusted + 8/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt7, "actor": [rbe4, 0.2]});
     84textueByHeight.push({"height": waterHeightAdjusted + 9/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt4, "actor": [undefined, 0]});
     85
     86/**
     87* Get start locations
     88*/
     89rescaleHeightmap(heightRange.min, heightRange.max);
     90var startLocations = getStartLocationsByHeightmap({"min": textueByHeight[4].height, "max": textueByHeight[5].height});
     91
     92/**
     93* Smooth ground at start locations and place starting entities
     94*/
     95var playerHeight = (textueByHeight[4].height + textueByHeight[5].height) / 2;
     96for (var p = 0; p < g_MapSettings.PlayerData.length - 1; p++)
     97{
     98    log("p = " + uneval(p));
     99    log("startLocations[" + p + "] = " + uneval(startLocations[p]));
     100    rectangularSmoothToHeight(startLocations[p], 30, 30, playerHeight);
     101    placeCivDefaultEntities(startLocations[p].x, startLocations[p].y, p + 1, BUILDING_ANGlE, {"iberWall": true});
     102}
     103
     104/**
     105* Normalize and apply reliefmap
     106*/
     107rescaleHeightmap(heightRange.min, heightRange.max);
     108
     109/**
     110* Place terrain texture and decorative props by height
     111*/
     112for(var x = 0; x < mapSize; x++)
     113{
     114    for (var y = 0; y < mapSize; y++)
     115    {
     116        var textureMinHeight = heightRange.min;
     117        for (var i = 0; i < textueByHeight.length; i++)
     118        {
     119            if (g_Map.height[x][y] >= textureMinHeight && g_Map.height[x][y] <= textueByHeight[i].height)
     120            {
     121                if (typeof(textueByHeight[i].texture) == "string")
     122                    g_Map.setTexture(x, y, textueByHeight[i].texture);
     123                else
     124                    g_Map.setTexture(x, y, textueByHeight[i].texture[randInt(textueByHeight[i].texture.length)]);
     125                if (textueByHeight[i].actor[0])
     126                {
     127                    var scaleByDistToPlayer = 1; // ToDo: Make an approximate faster range check!!!
     128                    for (var p = 0; p < g_MapSettings.PlayerData.length; p++)
     129                        scaleByDistToPlayer = min(getDistance(x, y, startLocations[p].x, startLocations[p].y) / 50, scaleByDistToPlayer);
     130                    if (randFloat() < textueByHeight[i].actor[1] * scaleByDistToPlayer)
     131                        placeObject(x + 0.5, y + 0.5, textueByHeight[i].actor[0], 0, randFloat(0, 2*PI));
     132                }
     133                break;
     134            }
     135            else
     136            {
     137                textureMinHeight = textueByHeight[i].height;
     138            }
     139        }
     140    }
     141}
     142
     143/**
     144* Export map data
     145*/
     146ExportMap();
  • binaries/data/mods/public/maps/random/BaseTerrainDiamondSquare.json

     
     1{
     2    "settings" : {
     3        "Name" : "Base Terrain Diamond Square Test",
     4        "Script" : "BaseTerrainDiamondSquare.js",
     5        "Description" : "Testmap for the diamond-square base terrain generation.",
     6        "CircularMap" : false,
     7        "BaseTerrain" : "grass1_spring",
     8        "BaseHeight" : 0,
     9        "Keywords": ["demo"],
     10        "XXXXXX" : "Optionally define other things here, like we would for a scenario"
     11    }
     12}
     13 No newline at end of file
  • binaries/data/mods/public/maps/random/belgian_uplands.js

     
    1 // Prepare progress calculation
     1/**
     2 * Prepare progress calculation
     3 */
    24var timeArray = [];
    35timeArray.push(new Date().getTime());
    46
    5 // Importing rmgen libraries
     7/**
     8 * Importing rmgen libraries
     9 */
    610RMS.LoadLibrary("rmgen");
    711
    812const BUILDING_ANGlE = -PI/4;
    913
    10 // initialize map
    11 
     14/**
     15 * initialize map
     16 */
    1217log("Initializing map...");
    1318
    1419InitMap();
     
    1722var mapSize = getMapSize();
    1823
    1924
    20 //////////
    21 // Heightmap functionality
    22 //////////
     25/**
     26 * Heightmap functionality
     27 */
    2328
    24 // Some general heightmap settings
    25 const MIN_HEIGHT = - SEA_LEVEL; // 20, should be set in the libs!
    26 const MAX_HEIGHT = 0xFFFF/HEIGHT_UNITS_PER_METRE - SEA_LEVEL; // A bit smaler than 90, should be set in the libs!
    27 
    28 // Add random heightmap generation functionality
     29/**
     30 * Add random heightmap generation functionality
     31 */
    2932function getRandomReliefmap(minHeight, maxHeight)
    3033{
    3134    minHeight = (minHeight || MIN_HEIGHT);
     
    3538    if (maxHeight > MAX_HEIGHT)
    3639        warn("getRandomReliefmap: Argument maxHeight is smaler then the supported maximum height of " + MAX_HEIGHT + " (const MAX_HEIGHT): " + maxHeight)
    3740    var reliefmap = [];
    38     for (var x = 0; x <= mapSize; x++)
     41    for (var x = 0; x <= mapSize; ++x)
    3942    {
    4043        reliefmap.push([]);
    41         for (var y = 0; y <= mapSize; y++)
     44        for (var y = 0; y <= mapSize; ++y)
    4245        {
    4346            reliefmap[x].push(randFloat(minHeight, maxHeight));
    4447        }
     
    4649    return reliefmap;
    4750}
    4851
    49 // Apply a heightmap
     52/**
     53 * Apply a heightmap
     54 */
    5055function setReliefmap(reliefmap)
    5156{
    5257    // g_Map.height = reliefmap;
    53     for (var x = 0; x <= mapSize; x++)
     58    for (var x = 0; x <= mapSize; ++x)
    5459    {
    55         for (var y = 0; y <= mapSize; y++)
     60        for (var y = 0; y <= mapSize; ++y)
    5661        {
    5762            setHeight(x, y, reliefmap[x][y]);
    5863        }
    5964    }
    6065}
    6166
    62 // Get minimum and maxumum height used in a heightmap
     67/**
     68 * Get minimum and maxumum height used in a heightmap
     69 */
    6370function getMinAndMaxHeight(reliefmap)
    6471{
    6572    var height = {};
    6673    height.min = Infinity;
    6774    height.max = -Infinity;
    68     for (var x = 0; x <= mapSize; x++)
     75    for (var x = 0; x <= mapSize; ++x)
    6976    {
    70         for (var y = 0; y <= mapSize; y++)
     77        for (var y = 0; y <= mapSize; ++y)
    7178        {
    7279            if (reliefmap[x][y] < height.min)
    7380                height.min = reliefmap[x][y];
     
    7885    return height;
    7986}
    8087
    81 // Rescale a heightmap (Waterlevel is not taken into consideration!)
     88/**
     89 * Rescale a heightmap (Waterlevel is not taken into consideration!)
     90 */
    8291function getRescaledReliefmap(reliefmap, minHeight, maxHeight)
    8392{
    8493    var newReliefmap = deepcopy(reliefmap);
     
    8998    if (maxHeight > MAX_HEIGHT)
    9099        warn("getRescaledReliefmap: Argument maxHeight is smaler then the supported maximum height of " + MAX_HEIGHT + " (const MAX_HEIGHT): " + maxHeight)
    91100    var oldHeightRange = getMinAndMaxHeight(reliefmap);
    92     for (var x = 0; x <= mapSize; x++)
     101    for (var x = 0; x <= mapSize; ++x)
    93102    {
    94         for (var y = 0; y <= mapSize; y++)
     103        for (var y = 0; y <= mapSize; ++y)
    95104        {
    96105            newReliefmap[x][y] = minHeight + (reliefmap[x][y] - oldHeightRange.min) / (oldHeightRange.max - oldHeightRange.min) * (maxHeight - minHeight);
    97106        }
     
    99108    return newReliefmap
    100109}
    101110
    102 // Applying decay errosion (terrain independent)
     111/**
     112 * Applying decay errosion (terrain independent)
     113 */
    103114function getHeightErrosionedReliefmap(reliefmap, strength)
    104115{
    105116    var newReliefmap = deepcopy(reliefmap);
    106117    strength = (strength || 1.0); // Values much higher then 1 (1.32+ for an 8 tile map, 1.45+ for a 12 tile map, 1.62+ @ 20 tile map, 0.99 @ 4 tiles) will result in a resonance disaster/self interference
    107118    var map = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]; // Default
    108     for (var x = 0; x <= mapSize; x++)
     119    for (var x = 0; x <= mapSize; ++x)
    109120    {
    110         for (var y = 0; y <= mapSize; y++)
     121        for (var y = 0; y <= mapSize; ++y)
    111122        {
    112123            var div = 0;
    113             for (var i = 0; i < map.length; i++)
     124            for (var i = 0; i < map.length; ++i)
    114125                newReliefmap[x][y] += strength / map.length * (reliefmap[(x + map[i][0] + mapSize + 1) % (mapSize + 1)][(y + map[i][1] + mapSize + 1) % (mapSize + 1)] - reliefmap[x][y]); // Not entirely sure if scaling with map.length is perfect but tested values seam to indicate it is
    115126        }
    116127    }
     
    118129}
    119130
    120131
    121 //////////
    122 // Prepare for hightmap munipulation
    123 //////////
     132/**
     133 * Prepare for hightmap munipulation
     134 */
    124135
    125 // Set target min and max height depending on map size to make average stepness the same on all map sizes
    126 var heightRange = {"min": MIN_HEIGHT * mapSize / 8192, "max": MAX_HEIGHT * mapSize / 8192};
     136/**
     137 * Set target min and max height depending on map size to make average stepness the same on all map sizes
     138 */
     139var heightRange = { "min": MIN_HEIGHT * (mapSize + 256) / 8192, "max": MAX_HEIGHT * (mapSize + 256) / 8192 };
    127140
    128 // Set average water coverage
     141/**
     142 * Set average water coverage
     143 */
    129144var averageWaterCoverage = 1/3; // NOTE: Since errosion is not predictable actual water coverage might differ much with the same value
    130145if (mapSize < 200) // Sink the waterlevel on tiny maps to ensure enough space
    131146    averageWaterCoverage = 2/3 * averageWaterCoverage;
     
    134149setWaterHeight(waterHeight);
    135150
    136151
    137 //////////
    138 // Prepare terrain texture by height placement
    139 //////////
    140 
     152/**
     153 * Prepare terrain texture by height placement
     154 */
    141155var textueByHeight = [];
    142156
    143 // Deep water
    144 textueByHeight.push({"upperHeightLimit": heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), "terrain": "temp_sea_rocks"});
    145 // Medium deep water (with fish)
    146 var terreins = ["temp_sea_weed"];
    147 terreins = terreins.concat(terreins, terreins, terreins, terreins);
    148 terreins = terreins.concat(terreins, terreins, terreins, terreins);
    149 terreins.push("temp_sea_weed|gaia/fauna_fish");
    150 textueByHeight.push({"upperHeightLimit": heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), "terrain": terreins});
    151 // Flat Water
    152 textueByHeight.push({"upperHeightLimit": heightRange.min + 3/3 * (waterHeightAdjusted - heightRange.min), "terrain": "temp_mud_a"});
    153 // Water surroundings/bog (with stone/metal some rabits and bushes)
    154 var terreins = ["temp_plants_bog", "temp_plants_bog_aut", "temp_dirt_gravel_plants", "temp_grass_d"];
    155 terreins = terreins.concat(terreins, terreins, terreins, terreins, terreins);
    156 terreins = ["temp_plants_bog|gaia/flora_bush_temperate"].concat(terreins, terreins);
    157 terreins = ["temp_dirt_gravel_plants|gaia/geology_metal_temperate", "temp_dirt_gravel_plants|gaia/geology_stone_temperate", "temp_plants_bog|gaia/fauna_rabbit"].concat(terreins, terreins);
    158 terreins = ["temp_plants_bog_aut|gaia/flora_tree_dead"].concat(terreins, terreins);
    159 textueByHeight.push({"upperHeightLimit": waterHeightAdjusted + 1/6 * (heightRange.max - waterHeightAdjusted), "terrain": terreins});
    160 // Juicy grass near bog
    161 textueByHeight.push({"upperHeightLimit": waterHeightAdjusted + 2/6 * (heightRange.max - waterHeightAdjusted),
    162     "terrain": ["temp_grass", "temp_grass_d", "temp_grass_long_b", "temp_grass_plants"]});
    163 // Medium level grass
    164 // var testActor = "actor|geology/decal_stone_medit_a.xml";
    165 textueByHeight.push({"upperHeightLimit": waterHeightAdjusted + 3/6 * (heightRange.max - waterHeightAdjusted),
    166     "terrain": ["temp_grass", "temp_grass_b", "temp_grass_c", "temp_grass_mossy"]});
    167 // Long grass near forest border
    168 textueByHeight.push({"upperHeightLimit": waterHeightAdjusted + 4/6 * (heightRange.max - waterHeightAdjusted),
    169     "terrain": ["temp_grass", "temp_grass_b", "temp_grass_c", "temp_grass_d", "temp_grass_long_b", "temp_grass_clovers_2", "temp_grass_mossy", "temp_grass_plants"]});
    170 // Forest border (With wood/food plants/deer/rabits)
    171 var terreins = ["temp_grass_plants|gaia/flora_tree_euro_beech", "temp_grass_mossy|gaia/flora_tree_poplar", "temp_grass_mossy|gaia/flora_tree_poplar_lombardy",
     157/**
     158 * Deep water
     159 */
     160textueByHeight.push({ "upperHeightLimit": heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), "terrain": "temp_sea_rocks" });
     161
     162/**
     163 * Medium water (with fish)
     164 */
     165var terrains = ["temp_sea_weed"];
     166terrains = terrains.concat(terrains, terrains, terrains, terrains);
     167terrains = terrains.concat(terrains, terrains, terrains, terrains);
     168terrains.push("temp_sea_weed|gaia/fauna_fish");
     169textueByHeight.push({ "upperHeightLimit": heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), "terrain": terrains });
     170
     171/**
     172 * Flat Water
     173 */
     174textueByHeight.push({ "upperHeightLimit": heightRange.min + 3/3 * (waterHeightAdjusted - heightRange.min), "terrain": "temp_mud_a" });
     175
     176/**
     177 * Water surroundings/bog (with stone/metal some rabits and bushes)
     178 */
     179var terrains = ["temp_plants_bog", "temp_plants_bog_aut", "temp_dirt_gravel_plants", "temp_grass_d"];
     180terrains = terrains.concat(terrains, terrains, terrains, terrains, terrains);
     181terrains = ["temp_plants_bog|gaia/flora_bush_temperate"].concat(terrains, terrains);
     182terrains = ["temp_dirt_gravel_plants|gaia/geology_metal_temperate", "temp_dirt_gravel_plants|gaia/geology_stone_temperate", "temp_plants_bog|gaia/fauna_rabbit"].concat(terrains, terrains);
     183terrains = ["temp_plants_bog_aut|gaia/flora_tree_dead"].concat(terrains, terrains);
     184textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 1/6 * (heightRange.max - waterHeightAdjusted), "terrain": terrains });
     185
     186/**
     187 * Juicy grass near bog
     188 */
     189textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 2/6 * (heightRange.max - waterHeightAdjusted),
     190    "terrain": ["temp_grass", "temp_grass_d", "temp_grass_long_b", "temp_grass_plants"] });
     191
     192/**
     193 * Medium level grass
     194 */
     195textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 3/6 * (heightRange.max - waterHeightAdjusted),
     196    "terrain": ["temp_grass", "temp_grass_b", "temp_grass_c", "temp_grass_mossy"] });
     197
     198/**
     199 * Long grass near forest border
     200 */
     201textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 4/6 * (heightRange.max - waterHeightAdjusted),
     202    "terrain": ["temp_grass", "temp_grass_b", "temp_grass_c", "temp_grass_d", "temp_grass_long_b", "temp_grass_clovers_2", "temp_grass_mossy", "temp_grass_plants"] });
     203
     204/**
     205 * Forest border (With wood/food plants/deer/rabits)
     206 */
     207var terrains = ["temp_grass_plants|gaia/flora_tree_euro_beech", "temp_grass_mossy|gaia/flora_tree_poplar", "temp_grass_mossy|gaia/flora_tree_poplar_lombardy",
    172208    "temp_grass_long|gaia/flora_bush_temperate", "temp_mud_plants|gaia/flora_bush_temperate", "temp_mud_plants|gaia/flora_bush_badlands",
    173209    "temp_grass_long|gaia/flora_tree_apple", "temp_grass_clovers|gaia/flora_bush_berry", "temp_grass_clovers_2|gaia/flora_bush_grapes",
    174210    "temp_grass_plants|gaia/fauna_deer", "temp_grass_long_b|gaia/fauna_rabbit"];
    175 var numTerreins = terreins.length;
    176 for (var i = 0; i < numTerreins; i++)
    177     terreins.push("temp_grass_plants");
    178 textueByHeight.push({"upperHeightLimit": waterHeightAdjusted + 5/6 * (heightRange.max - waterHeightAdjusted), "terrain": terreins});
    179 // Unpassable woods
    180 textueByHeight.push({"upperHeightLimit": waterHeightAdjusted + 6/6 * (heightRange.max - waterHeightAdjusted),
     211var numTerreins = terrains.length;
     212for (var i = 0; i < numTerreins; ++i)
     213    terrains.push("temp_grass_plants");
     214textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 5/6 * (heightRange.max - waterHeightAdjusted), "terrain": terrains });
     215
     216/**
     217 * Impassable woods
     218 */
     219textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 6/6 * (heightRange.max - waterHeightAdjusted),
    181220    "terrain": ["temp_grass_mossy|gaia/flora_tree_oak", "temp_forestfloor_pine|gaia/flora_tree_pine",
    182221    "temp_grass_mossy|gaia/flora_tree_oak", "temp_forestfloor_pine|gaia/flora_tree_pine",
    183222    "temp_mud_plants|gaia/flora_tree_dead", "temp_plants_bog|gaia/flora_tree_oak_large",
    184     "temp_dirt_gravel_plants|gaia/flora_tree_aleppo_pine", "temp_forestfloor_autumn|gaia/flora_tree_carob"]});
     223    "temp_dirt_gravel_plants|gaia/flora_tree_aleppo_pine", "temp_forestfloor_autumn|gaia/flora_tree_carob"] });
    185224var minTerrainDistToBorder = 3;
    186225
    187 // Time check 1
     226/**
     227 * Time check 1
     228 */
    188229timeArray.push(new Date().getTime());
    189230RMS.SetProgress(5);
    190231
    191 
    192 // START THE GIANT WHILE LOOP:
    193 // - Generate Heightmap
    194 // - Search valid start position tiles
    195 // - Choose a good start position derivation (largest distance between closest players)
    196 // - Restart the loop if start positions are invalid or two players are to close to each other
     232/**
     233 * START THE GIANT WHILE LOOP:
     234 * - Generate Heightmap
     235 * - Search valid start position tiles
     236 * - Choose a good start position derivation (largest distance between closest players)
     237 * - Restart the loop if start positions are invalid or two players are to close to each other
     238 */
    197239var goodStartPositionsFound = false;
    198240var minDistBetweenPlayers = 16 + mapSize / 16; // Don't set this higher than 25 for tiny maps! It will take forever with 8 players!
    199241var enoughTiles = false;
    200242var tries = 0;
    201243while (!goodStartPositionsFound)
    202244{
    203     tries++;
     245    ++tries;
    204246    log("Starting giant while loop try " + tries);
    205247    // Generate reliefmap
    206248    var myReliefmap = getRandomReliefmap(heightRange.min, heightRange.max);
    207     for (var i = 0; i < 50 + mapSize/4; i++) // Cycles depend on mapsize (more cycles -> bigger structures)
     249    for (var i = 0; i < 50 + mapSize/4; ++i) // Cycles depend on mapsize (more cycles -> bigger structures)
    208250        myReliefmap = getHeightErrosionedReliefmap(myReliefmap, 1);
    209251    myReliefmap = getRescaledReliefmap(myReliefmap, heightRange.min, heightRange.max);
    210252    setReliefmap(myReliefmap);
     
    217259    var lowerHeightLimit = textueByHeight[3].upperHeightLimit;
    218260    var upperHeightLimit = textueByHeight[6].upperHeightLimit;
    219261    // Check for valid points by height
    220     for (var x = distToBorder + minTerrainDistToBorder; x < mapSize - distToBorder - minTerrainDistToBorder; x++)
     262    for (var x = distToBorder + minTerrainDistToBorder; x < mapSize - distToBorder - minTerrainDistToBorder; ++x)
    221263    {
    222         for (var y = distToBorder + minTerrainDistToBorder; y < mapSize - distToBorder - minTerrainDistToBorder; y++)
     264        for (var y = distToBorder + minTerrainDistToBorder; y < mapSize - distToBorder - minTerrainDistToBorder; ++y)
    223265        {
    224266            var actualHeight = getHeight(x, y);
    225267            if (actualHeight > lowerHeightLimit && actualHeight < upperHeightLimit)
    226268            {
    227269                // Check for points within a valid area by height (rectangular since faster)
    228270                var isPossible = true;
    229                 for (var offX = - neededDistance; offX <= neededDistance; offX++)
     271                for (var offX = - neededDistance; offX <= neededDistance; ++offX)
    230272                {
    231                     for (var offY = - neededDistance; offY <= neededDistance; offY++)
     273                    for (var offY = - neededDistance; offY <= neededDistance; ++offY)
    232274                    {
    233275                        var testHeight = getHeight(x + offX, y + offY);
    234276                        if (testHeight <= lowerHeightLimit || testHeight >= upperHeightLimit)
     
    252294    // Reduce to tiles in a circle of mapSize / 2 distance to the center (to avoid players placed in corners)
    253295    var possibleStartPositionsTemp = [];
    254296    var maxDistToCenter = mapSize / 2;
    255     for (var i = 0; i < possibleStartPositions.length; i++)
     297    for (var i = 0; i < possibleStartPositions.length; ++i)
    256298    {
    257299        var deltaX = possibleStartPositions[i][0] - mapSize / 2;
    258300        var deltaY = possibleStartPositions[i][1] - mapSize / 2;
     
    270312    var maxDistToResources = distToBorder; // Has to be <= distToBorder!
    271313    var minNumLowTiles = 10;
    272314    var minNumHighTiles = 10;
    273     for (var i = 0; i < possibleStartPositions.length; i++)
     315    for (var i = 0; i < possibleStartPositions.length; ++i)
    274316    {
    275317        var numLowTiles = 0;
    276318        var numHighTiles = 0;
    277         for (var dx = - maxDistToResources; dx < maxDistToResources; dx++)
     319        for (var dx = - maxDistToResources; dx < maxDistToResources; ++dx)
    278320        {
    279             for (var dy = - maxDistToResources; dy < maxDistToResources; dy++)
     321            for (var dy = - maxDistToResources; dy < maxDistToResources; ++dy)
    280322            {
    281323                var testHeight = getHeight(possibleStartPositions[i][0] + dx, possibleStartPositions[i][1] + dy);
    282324                if (testHeight < lowerHeightLimit)
    283                     numLowTiles++;
     325                    ++numLowTiles;
    284326                if (testHeight > upperHeightLimit)
    285                     numHighTiles++;
     327                    ++numHighTiles;
    286328                if (numLowTiles > minNumLowTiles && numHighTiles > minNumHighTiles)
    287329                    break;
    288330            }
     
    311353        // Get some random start location derivations. NOTE: Itterating over all possible derivations is just to much (valid points ** numPlayers)
    312354        var maxTries = 100000; // floor(800000 / (Math.pow(numPlayers, 2) / 2));
    313355        var possibleDerivations = [];
    314         for (var i = 0; i < maxTries; i++)
     356        for (var i = 0; i < maxTries; ++i)
    315357        {
    316358            var vector = [];
    317             for (var p = 0; p < numPlayers; p++)
     359            for (var p = 0; p < numPlayers; ++p)
    318360                vector.push(randInt(possibleStartPositions.length));
    319361            possibleDerivations.push(vector);
    320362        }
    321363       
    322364        // Choose the start location derivation with the greatest minimum distance between players
    323365        var maxMinDist = 0;
    324         for (var d = 0; d < possibleDerivations.length; d++)
     366        for (var d = 0; d < possibleDerivations.length; ++d)
    325367        {
    326368            var minDist = 2 * mapSize;
    327             for (var p1 = 0; p1 < numPlayers - 1; p1++)
     369            for (var p1 = 0; p1 < numPlayers - 1; ++p1)
    328370            {
    329                 for (var p2 = p1 + 1; p2 < numPlayers; p2++)
     371                for (var p2 = p1 + 1; p2 < numPlayers; ++p2)
    330372                {
    331373                    if (p1 != p2)
    332374                    {
     
    358400    } // End of derivation check
    359401} // END THE GIANT WHILE LOOP
    360402
    361 // Time check 2
     403/**
     404 * Time check 2
     405 */
    362406timeArray.push(new Date().getTime());
    363407RMS.SetProgress(60);
    364408
    365409
    366 ////////
    367 // Paint terrain by height and add props
    368 ////////
     410/**
     411 * Paint terrain by height and add props
     412 */
    369413
    370414var propDensity = 1; // 1 means as determined in the loop, less for large maps as set below
    371415if (mapSize > 500)
    372416    propDensity = 1/4;
    373417else if (mapSize > 400)
    374418    propDensity = 3/4;
    375 for(var x = minTerrainDistToBorder; x < mapSize - minTerrainDistToBorder; x++)
     419for(var x = minTerrainDistToBorder; x < mapSize - minTerrainDistToBorder; ++x)
    376420{
    377     for (var y = minTerrainDistToBorder; y < mapSize - minTerrainDistToBorder; y++)
     421    for (var y = minTerrainDistToBorder; y < mapSize - minTerrainDistToBorder; ++y)
    378422    {
    379423        var textureMinHeight = heightRange.min;
    380         for (var i = 0; i < textueByHeight.length; i++)
     424        for (var i = 0; i < textueByHeight.length; ++i)
    381425        {
    382426            if (getHeight(x, y) >= textureMinHeight && getHeight(x, y) <= textueByHeight[i].upperHeightLimit)
    383427            {
     
    488532    }
    489533}
    490534
    491 // Time check 3
     535/**
     536 * Time check 3
     537 */
    492538timeArray.push(new Date().getTime());
    493539RMS.SetProgress(90);
    494540
    495541
    496 ////////
    497 // Place players and start resources
    498 ////////
     542/**
     543 * Place players and start resources
     544 */
    499545
    500 for (var p = 0; p < numPlayers; p++)
     546for (var p = 0; p < numPlayers; ++p)
    501547{
    502548    var actualX = possibleStartPositions[bestDerivation[p]][0];
    503549    var actualY = possibleStartPositions[bestDerivation[p]][1];
    504     placeCivDefaultEntities(actualX, actualY, p + 1, BUILDING_ANGlE, {"iberWall" : false});
     550    placeCivDefaultEntities(actualX, actualY, p + 1, BUILDING_ANGlE, { "iberWall" : false });
    505551    // Place some start resources
    506552    var uDist = 8;
    507553    var uSpace = 1;
     
    509555    {
    510556        var uAngle = BUILDING_ANGlE - PI * (2-j) / 2;
    511557        var count = 4;
    512         for (var numberofentities = 0; numberofentities < count; numberofentities++)
     558        for (var numberofentities = 0; numberofentities < count; ++numberofentities)
    513559        {
    514560            var ux = actualX + uDist * cos(uAngle) + numberofentities * uSpace * cos(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * cos(uAngle + PI/2));
    515561            var uz = actualY + uDist * sin(uAngle) + numberofentities * uSpace * sin(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * sin(uAngle + PI/2));
     
    521567    }
    522568}
    523569
    524 
    525 // Export map data
     570/**
     571 * Export map data
     572 */
    526573ExportMap();
    527574
    528 // Time check 7
     575/**
     576 * Time check 7
     577 */
    529578timeArray.push(new Date().getTime());
    530579
    531 // Calculate progress percentage with the time checks
     580/**
     581 * Calculate progress percentage with the time checks
     582 */
    532583var generationTime = timeArray[timeArray.length - 1] - timeArray[0];
    533584log("Total generation time (ms): " + generationTime);
    534 for (var i = 0; i < timeArray.length; i++)
     585for (var i = 0; i < timeArray.length; ++i)
    535586{
    536587    var timeSinceStart = timeArray[i] - timeArray[0];
    537588    var progressPercentage = 100 * timeSinceStart / generationTime;
  • binaries/data/mods/public/maps/random/realisticTerrainDemo.js

     
     1/**
     2*   ToDo:
     3*   Add further biomes
     4*   Place start locations of one team close to each other
     5*   Raise the entire map if to much water
     6*
     7*   Add support for circular maps
     8*   Add parameters for match setup screen (for later use and demonstration)
     9*
     10*   Extract some functions to an RMGEN lib
     11*   Sort out unneeded functions calling other functions
     12*   Clean up
     13*/
     14
     15/**
     16* DEBUG Start Timer
     17*/
     18var genStartTime = new Date().getTime();
     19
     20RMS.LoadLibrary("rmgen");
     21
     22const BUILDING_ANGlE = -PI/4;
     23
     24/**
     25* initialize map
     26*/
     27log("Initializing map...");
     28InitMap();
     29
     30
     31/**
     32*  Some general functions
     33*/
     34
     35/**
     36*   Takes an array of 2D points (arrays of length 2)
     37*   Returns the order to go through the points for the shortest closed path (array of indices)
     38*/
     39function getOrderOfPointsForShortestClosePath(points)
     40{
     41    var order = [];
     42    var distances = [];
     43    if (points.length <= 3)
     44    {
     45        for (var i = 0; i < points.length; ++i)
     46            order.push(i);
     47       
     48        return order;
     49    }
     50   
     51    // Just add the first 3 points
     52    var pointsToAdd = deepcopy(points);
     53    for (var i = 0; i < min(points.length, 3); ++i)
     54    {
     55        order.push(i)
     56        pointsToAdd.shift(i);
     57        if (i)
     58            distances.push(getDistance(points[order[i]].x, points[order[i]].y, points[order[i - 1]].x, points[order[i - 1]].y));
     59    }
     60    distances.push(getDistance(points[order[0]].x, points[order[0]].y, points[order[order.length - 1]].x, points[order[order.length - 1]].y))
     61   
     62    // Add remaining points so the path lengthens the least
     63    var numPointsToAdd = pointsToAdd.length;
     64    for (var i = 0; i < numPointsToAdd; ++i)
     65    {
     66        var indexToAddTo = undefined;
     67        var minEnlengthen = Infinity;
     68        var minDist1 = 0;
     69        var minDist2 = 0;
     70        for (var k = 0; k < order.length; ++k)
     71        {
     72            var dist1 = getDistance(pointsToAdd[0].x, pointsToAdd[0].y, points[order[k]].x, points[order[k]].y);
     73            var dist2 = getDistance(pointsToAdd[0].x, pointsToAdd[0].y, points[order[(k + 1) % order.length]].x, points[order[(k + 1) % order.length]].y);
     74            var enlengthen = dist1 + dist2 - distances[k];
     75            if (enlengthen < minEnlengthen)
     76            {
     77                indexToAddTo = k;
     78                minEnlengthen = enlengthen;
     79                minDist1 = dist1;
     80                minDist2 = dist2;
     81            }
     82        }
     83        order.splice(indexToAddTo + 1, 0, i + 3);
     84        distances.splice(indexToAddTo, 1, minDist1, minDist2);
     85        pointsToAdd.shift();
     86    }
     87   
     88    return order;
     89}
     90
     91/*
     92Drags a path to a target height smoothing it at the edges and return some points on the path.
     93
     94TODO:
     95Would be nice to tell the function what to do and how often in the arguments
     96Adding painted tiles to a tile class
     97*/
     98function placeRandomPathToHeight(start, paintPathWithTexture, target, targetHeight, width, occurrence, strength, heightmap)
     99{
     100    if (paintPathWithTexture === false)
     101        paintPathWithTexture = [false, ""];
     102    else if (paintPathWithTexture === true)
     103        paintPathWithTexture = [true, ['temp_road', "temp_road_overgrown", 'temp_grass_b']]
     104    else
     105        paintPathWithTexture = (paintPathWithTexture || [false, ['temp_road', "temp_road_overgrown", 'temp_grass_b']]);
     106    var paintPath = paintPathWithTexture[0];
     107    var pathTexture = paintPathWithTexture[1];
     108    width = (width || 10);
     109    occurrence = (occurrence || 2);
     110    strength = (strength || 0.5);
     111    heightmap = (heightmap || g_Map.height);
     112   
     113    var clTempPath = createTileClass();
     114    var targetReached = false;
     115    var position = deepcopy(start);
     116    while (!targetReached)
     117    {
     118        rectangularSmoothToHeight(position, width, width, targetHeight, strength, heightmap);
     119        if (paintPath)
     120        {
     121            var placer = new ClumpPlacer(0.2 * width * width, 1, 1, 1, round(position.x), round(position.y));
     122            var painter = [new TerrainPainter(pathTexture), paintClass(clTempPath)];
     123            createArea(placer, painter);
     124        }
     125       
     126        // Set vars for next loop
     127        var angleToTarget = getAngle(position.x, position.y, target.x, target.y);
     128        var angleOff = PI * (randFloat() - 0.5);
     129        position.x += occurrence * cos(angleToTarget + angleOff);
     130        position.y += occurrence * sin(angleToTarget + angleOff);
     131        if (getDistance(position.x, position.y, target.x, target.y) < occurrence / 2)
     132            targetReached = true;
     133    }
     134    return clTempPath;
     135}
     136
     137
     138/**
     139*  Actually do stuff
     140*/
     141
     142/**
     143* Set height limits and water level by map size
     144*/
     145
     146/**
     147* Set target min and max height depending on map size to make average steepness about the same on all map sizes
     148*/
     149var heightRange = { "min": MIN_HEIGHT * (g_Map.size + 512) / 8192, "max": MAX_HEIGHT * (g_Map.size + 512) / 8192 };
     150
     151/**
     152* Set average water coverage
     153*/
     154var averageWaterCoverage = 1/5; // NOTE: Since terrain generation is quite unpredictable actual water coverage might vary much with the same value
     155var waterHeight = -MIN_HEIGHT + heightRange.min + averageWaterCoverage * (heightRange.max - heightRange.min); // Water height in environment and the engine
     156var waterHeightAdjusted = waterHeight + MIN_HEIGHT; // Water height in RMGEN
     157setWaterHeight(waterHeight);
     158
     159
     160/**
     161* Generate base terrain
     162*/
     163
     164var initialReliefmap = undefined;
     165var low = heightRange.min;
     166var med = 0.5 * (heightRange.min + heightRange.max);
     167var high = heightRange.max;
     168/**
     169* Island
     170*/
     171// initialReliefmap = [[low, low, low], [low, high, low], [low, low, low]];
     172initialReliefmap = [[low, low, low, low], [low, high, high, low], [low, high, high, low], [low, low, low, low]];
     173/**
     174* Mountain
     175*/
     176// initialReliefmap = [[low, med, low], [med, high, med], [low, med, low]];
     177/**
     178* Lake
     179*/
     180// initialReliefmap = [[high, high, high], [high, low, high], [high, high, high]];
     181// initialReliefmap = [[high, high, high, high], [high, low, low, high], [high, low, low, high], [high, high, high, high]];
     182/**
     183* Extreme Edges
     184*/
     185// initialReliefmap = [[high, med, low], [med, med, med], [low, med, high]];
     186// initialReliefmap = [[high, med, med, low], [med, med, med, med], [med, med, med, med], [med, med, med, med], [low, med, med, high]];
     187/**
     188* Extreme Center
     189*/
     190// initialReliefmap = [[med, med, med, med], [med, high, low, med], [med, low, high, med], [med, med, med, med]];
     191setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, /*0.5*/ 0.8, initialReliefmap, g_Map.height);
     192/**
     193* Apply simple erosion
     194*/
     195for (var i = 0; i < 3; ++i)
     196    globalSmoothHeightmap(0.5);
     197rescaleHeightmap(heightRange.min, heightRange.max);
     198
     199
     200/**
     201* Setup biome and texture height limit
     202*/
     203
     204/**
     205* myBiome presets
     206*/
     207
     208/**
     209* Height presets
     210*/
     211var heighLimits = [
     212    heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), // 0 Deep water
     213    heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), // 1 Medium Water
     214    heightRange.min + (waterHeightAdjusted - heightRange.min), // 2 Shallow water
     215    waterHeightAdjusted + 1/8 * (heightRange.max - waterHeightAdjusted), // 3 Shore
     216    waterHeightAdjusted + 2/8 * (heightRange.max - waterHeightAdjusted), // 4 Low ground
     217    waterHeightAdjusted + 3/8 * (heightRange.max - waterHeightAdjusted), // 5 Player and path height
     218    waterHeightAdjusted + 4/8 * (heightRange.max - waterHeightAdjusted), // 6 High ground
     219    waterHeightAdjusted + 5/8 * (heightRange.max - waterHeightAdjusted), // 7 Lower forest border
     220    waterHeightAdjusted + 6/8 * (heightRange.max - waterHeightAdjusted), // 8 Forest
     221    waterHeightAdjusted + 7/8 * (heightRange.max - waterHeightAdjusted), // 9 Upper forest border
     222    waterHeightAdjusted + (heightRange.max - waterHeightAdjusted)]; // 10 Hilltop
     223
     224/**
     225* Add tile painting presets for each biome
     226*/
     227var dummyActor = "actor|props/special/common/waypoint_flag.xml";
     228var myBiome = [];
     229/**
     230* temperate 1
     231*/
     232myBiome.push([]);
     233/**
     234* snowy 2
     235*/
     236myBiome.push([]);
     237/**
     238* desert 3
     239*/
     240myBiome.push([]);
     241/**
     242* Alpine 4
     243*/
     244myBiome.push([]);
     245myBiome[myBiome.length - 1].push({ // 0 Deep water
     246    "texture": ["shoreline_stoney_a"], "actor": [["gaia/fauna_fish", "actor|geology/stone_granite_boulder.xml"], 0.02],
     247    "textureHS": ["alpine_mountainside"], "actorHS": [["gaia/fauna_fish"], 0.1] });
     248myBiome[myBiome.length - 1].push({ // 1 Medium Water
     249    "texture": ["shoreline_stoney_a", "alpine_shore_rocks"], "actor": [["actor|geology/stone_granite_boulder.xml"], 0.03],
     250    "textureHS": ["alpine_mountainside"], "actorHS": [["actor|geology/stone_granite_boulder.xml"], 0.0] });
     251myBiome[myBiome.length - 1].push({ // 2 Shallow water
     252    "texture": ["alpine_shore_rocks"], "actor": [["actor|props/flora/reeds_pond_dry.xml", "actor|geology/stone_granite_large.xml", "actor|geology/stone_granite_med.xml", "actor|props/flora/reeds_pond_lush_b.xml"], 0.2],
     253    "textureHS": ["alpine_mountainside"], "actorHS": [["actor|props/flora/reeds_pond_dry.xml", "actor|geology/stone_granite_med.xml"], 0.1] });
     254myBiome[myBiome.length - 1].push({ // 3 Shore
     255    "texture": ["alpine_shore_rocks_grass_50", "alpine_grass_rocky"], "actor": [["gaia/flora_tree_pine", "gaia/flora_bush_badlands", "actor|geology/highland1_moss.xml", "actor|props/flora/grass_soft_tuft_a.xml", "actor|props/flora/bush.xml"], 0.3],
     256    "textureHS": ["alpine_mountainside"], "actorHS": [["actor|props/flora/grass_soft_tuft_a.xml"], 0.1] });
     257myBiome[myBiome.length - 1].push({ // 4 Low ground
     258    "texture": ["alpine_dirt_grass_50", "alpine_grass_rocky"], "actor": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_soft_tuft_a.xml", "actor|props/flora/bush.xml", "actor|props/flora/grass_medit_flowering_tall.xml"], 0.2],
     259    "textureHS": ["alpine_grass_rocky"], "actorHS": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_soft_tuft_a.xml"], 0.1] });
     260myBiome[myBiome.length - 1].push({ // 5 Player and path height
     261    "texture": ["new_alpine_grass_c", "new_alpine_grass_b", "new_alpine_grass_d"], "actor": [["actor|geology/stone_granite_small.xml", "actor|props/flora/grass_soft_small.xml", "actor|props/flora/grass_medit_flowering_tall.xml"], 0.2],
     262    "textureHS": ["alpine_grass_rocky"], "actorHS": [["actor|geology/stone_granite_small.xml", "actor|props/flora/grass_soft_small.xml"], 0.1] });
     263myBiome[myBiome.length - 1].push({ // 6 High ground
     264    "texture": ["new_alpine_grass_a", "alpine_grass_rocky"], "actor": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_tufts_a.xml", "actor|props/flora/bush_highlands.xml", "actor|props/flora/grass_medit_flowering_tall.xml"], 0.2],
     265    "textureHS": ["alpine_grass_rocky"], "actorHS": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_tufts_a.xml"], 0.1] });
     266myBiome[myBiome.length - 1].push({ // 7 Lower forest border
     267    "texture": ["new_alpine_grass_mossy", "alpine_grass_rocky"], "actor": [["gaia/flora_tree_pine", "actor|props/flora/grass_tufts_a.xml", "gaia/flora_bush_berry", "gaia/flora_bush_berry", "gaia/flora_bush_berry", "actor|geology/highland2_moss.xml", "gaia/fauna_goat", "actor|props/flora/bush_tempe_underbrush.xml"], 0.3],
     268    "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|props/flora/grass_tufts_a.xml", "actor|geology/highland2_moss.xml"], 0.1] });
     269myBiome[myBiome.length - 1].push({ // 8 Forest
     270    "texture": ["alpine_forrestfloor"], "actor": [["gaia/flora_tree_pine", "gaia/flora_tree_pine", "gaia/flora_tree_pine", "gaia/flora_tree_pine", "actor|geology/highland2_moss.xml", "actor|props/flora/bush_highlands.xml"], 0.5],
     271    "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|geology/highland2_moss.xml", "actor|geology/stone_granite_med.xml"], 0.1] });
     272myBiome[myBiome.length - 1].push({ // 9 Upper forest border
     273    "texture": ["alpine_forrestfloor_snow", "new_alpine_grass_dirt_a"], "actor": [["gaia/flora_tree_pine", "actor|geology/snow1.xml"], 0.3],
     274    "textureHS": ["alpine_cliff_b"], "actorHS": [["actor|geology/stone_granite_med.xml", "actor|geology/snow1.xml"], 0.1] });
     275myBiome[myBiome.length - 1].push({ // 10 Hilltop
     276    "texture": ["alpine_cliff_a"], "actor": [["actor|geology/highland1.xml"], 0.05],
     277    "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|geology/highland1.xml"], 0.0] });
     278/**
     279* medit 5
     280*/
     281myBiome.push([]);
     282/**
     283* savanah 6
     284*/
     285myBiome.push([]);
     286/**
     287* tropic 7
     288*/
     289myBiome.push([]);
     290/**
     291* autumn 8
     292*/
     293myBiome.push([]);
     294
     295/**
     296* Set random biome
     297*/
     298while (biomeID != 4)
     299    randomizeBiome();
     300log("biomeID = " + biomeID); // DEBUG
     301
     302if (biomeID == 4)
     303{
     304    g_Environment.Fog.FogColor = { "r": 0.8, "g": 0.8, "b": 0.8, "a": 0 };
     305    g_Environment.Water.WaterBody.Colour = { "r" : 0.0, "g" : 0.05, "b" : 0.1, "a" : 0 };
     306    g_Environment.Water.WaterBody.Murkiness = 0.5;
     307    rbt7 = rbt8;
     308    rbt8 = rbt1
     309    rbt1 = [rbt9[1]];
     310    rbt9 = [rbt9[0]];
     311}
     312if (biomeID == 6)
     313    rbt9 = [rbt9[1]];
     314if (biomeID == 8)
     315    rbt9 = rbt6;
     316
     317
     318/**
     319* Place start locations and apply terrain texture and decorative props by height
     320*/
     321
     322/**
     323* Get start locations
     324*/
     325var startLocations = getStartLocationsByHeightmap({ "min": heighLimits[4], "max": heighLimits[5] });
     326var playerHeight = (heighLimits[4] + heighLimits[5]) / 2;
     327
     328/**
     329* Place start locations
     330*/
     331for (var p = 0; p < g_MapSettings.PlayerData.length - 1; ++p)
     332{
     333    rectangularSmoothToHeight(startLocations[p], 35, 35, playerHeight, 0.8);
     334    placeCivDefaultEntities(startLocations[p].x, startLocations[p].y, p + 1, BUILDING_ANGlE, { "iberWall": true });
     335    // Place resources at start locations
     336    var uDist = 8;
     337    var uSpace = 1.2;
     338    for (var j = 1; j <= 4; ++j)
     339    {
     340        var uAngle = BUILDING_ANGlE - PI * (2-j) / 2;
     341        var count = 4;
     342        for (var numberofentities = 0; numberofentities < count; ++numberofentities)
     343        {
     344            var ux = startLocations[p].x + uDist * cos(uAngle) + numberofentities * uSpace * cos(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * cos(uAngle + PI/2));
     345            var uz = startLocations[p].y + uDist * sin(uAngle) + numberofentities * uSpace * sin(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * sin(uAngle + PI/2));
     346            if (j % 2 == 0)
     347                placeObject(ux, uz, rbe6, 0, randFloat(0, 2*PI));
     348            else
     349                placeObject(ux, uz, rbe4, 0, randFloat(0, 2*PI));
     350        }
     351    }
     352    ux = startLocations[p].x + 10 * cos(BUILDING_ANGlE + PI / 4);
     353    uz = startLocations[p].y + 10 * sin(BUILDING_ANGlE + PI / 4);
     354    // placeObject(ux, uz, rbe11, 0, randFloat(0, 2*PI));
     355    ux = startLocations[p].x + 10 * cos(BUILDING_ANGlE - 3 * PI / 4);
     356    uz = startLocations[p].y + 10 * sin(BUILDING_ANGlE - 3 * PI / 4);
     357    // placeObject(ux, uz, rbe13, 0, randFloat(0, 2*PI));
     358}
     359
     360/**
     361* Add further stone and metal mines
     362*/
     363distributeEntitiesByHeight({ "min": heighLimits[4], "max": heighLimits[5] }, startLocations);
     364
     365/**
     366* Add paths
     367*/
     368var pathTerrainClasseIDs = [];
     369var pathPointDist = 3;
     370var startLocationOrder = getOrderOfPointsForShortestClosePath(startLocations);
     371// for (var i = 0; i < startLocationOrder.length; ++i)
     372// {
     373    // var start = startLocations[startLocationOrder[i]];
     374    // var target = startLocations[startLocationOrder[(i + 1) % startLocationOrder.length]];
     375    // pathTerrainClasseIDs.push(placeRandomPathToHeight(start, [true, "road_rome_a"], target, playerHeight, 10, pathPointDist, 0.2));
     376// }
     377
     378/**
     379* Calculate slope map (Not entirely sure if correct or slopeMap is the correct term)
     380*/
     381var slopeMap = getSlopeMap();
     382
     383/**
     384* Calculate tileCenteredHeightMap (This has nothing to to with TILE_CENTERED_HEIGHT_MAP which should be false (default) for this map to work properly)
     385*/
     386var tchm = getTileCenteredHeightmap();
     387
     388/**
     389* Divide tiles in areas by height and avoid paths
     390*/
     391var areas = [];
     392var pathAreas = [];
     393for (var i = 0; i < pathTerrainClasseIDs.length; ++i)
     394    pathAreas.push([]);
     395for (var h = 0; h < heighLimits.length; ++h)
     396    areas.push([]);
     397for (var x = 0; x < tchm.length; ++x)
     398{
     399    for (var y = 0; y < tchm[0].length; ++y)
     400    {
     401        var isPath = false;
     402        for (var i = 0; i < pathTerrainClasseIDs.length; ++i)
     403        {
     404            if (getTileClass(pathTerrainClasseIDs[i]).countMembersInRadius(x, y, 0.5))
     405            {
     406                pathAreas[i].push({ "x": x, "y": y });
     407                isPath = true;
     408                break;
     409            }
     410        }
     411        if (!isPath)
     412        {
     413            var minHeight = heightRange.min;
     414            for (var h = 0; h < heighLimits.length; ++h)
     415            {
     416                if (tchm[x][y] >= minHeight && tchm[x][y] <= heighLimits[h])
     417                {
     418                    areas[h].push({ "x": x, "y": y });
     419                    break;
     420                }
     421                else
     422                    minHeight = heighLimits[h];
     423            }
     424        }
     425    }
     426}
     427
     428/**
     429* Get max slope of each area
     430*/
     431var minSlope = new Array(areas.length);
     432var maxSlope = new Array(areas.length);
     433for (var h = 0; h < heighLimits.length; ++h)
     434{
     435    minSlope[h] = Infinity;
     436    maxSlope[h] = 0;
     437    for (var t = 0; t < areas[h].length; ++t)
     438    {
     439        var x = areas[h][t].x;
     440        var y = areas[h][t].y;
     441        var slope = slopeMap[x][y];
     442        if (slope > maxSlope[h])
     443            maxSlope[h] = slope;
     444        if (slope < minSlope[h])
     445            minSlope[h] = slope;
     446    }
     447}
     448
     449/**
     450* Paint areas by height and slope
     451*/
     452for (var h = 0; h < heighLimits.length; ++h)
     453{
     454    for (var t = 0; t < areas[h].length; ++t)
     455    {
     456        var x = areas[h][t].x;
     457        var y = areas[h][t].y;
     458        var actor = undefined;
     459        if (slopeMap[x][y] < 0.5 * (minSlope[h] + maxSlope[h]))
     460        {
     461            var texture = myBiome[biomeID - 1][h].texture[randInt(myBiome[biomeID - 1][h].texture.length)];
     462            if (randFloat() < myBiome[biomeID - 1][h].actor[1])
     463                actor = myBiome[biomeID - 1][h].actor[0][randInt(myBiome[biomeID - 1][h].actor[0].length)];
     464        }
     465        else
     466        {
     467            var texture = myBiome[biomeID - 1][h].textureHS[randInt(myBiome[biomeID - 1][h].textureHS.length)];
     468            if (randFloat() < myBiome[biomeID - 1][h].actorHS[1])
     469                actor = myBiome[biomeID - 1][h].actorHS[0][randInt(myBiome[biomeID - 1][h].actorHS[0].length)];
     470        }
     471        g_Map.setTexture(x, y, texture);
     472        if (actor)
     473            placeObject(x + 0.5, y + 0.5, actor, 0, randFloat() * TWO_PI);
     474    }
     475}
     476
     477
     478/**
     479* DEBUG Collision detection START
     480*/
     481// for (var ox = 0; ox < collision.obstructionMap.length; ++ox)
     482// {
     483    // for (var oy = 0; oy < collision.obstructionMap[ox].length; ++oy)
     484    // {
     485        // if (collision.obstructionMap[ox][oy])
     486        // {
     487            // var x = (ox + 0.5) / CELL_SIZE * collision.obstructionGridSizeInMeters;
     488            // var y = (oy + 0.5) / CELL_SIZE * collision.obstructionGridSizeInMeters;
     489            // // log("ox = " + ox + ", oy = " + oy + ", x = " + x + ", y = " + y);
     490            // if (g_Map.validT(x, y))
     491                // placeObject(x, y, "actor|geology/stone_granite_small.xml", 0, randFloat() * TWO_PI);
     492            // else
     493                // warn("Obstruction cell {x: " + uneval(ox) + ", y: " + uneval(oy) + "} corresponds to a tile outside the map (size = " + uneval(g_Map.height.length) + "): {x: " + uneval(x) + ", y: " + uneval(y) + "}");
     494        // }
     495    // }
     496// }
     497/**
     498* DEBUG Collision detection END
     499*/
     500
     501/**
     502* DEBUG Stop Timer
     503*/
     504log("Map generation time: " + (new Date().getTime() - genStartTime))
     505
     506/**
     507* Export map data
     508*/
     509ExportMap();
     510
     511/**
     512* DEBUG Entity IDs START
     513*/
     514// for (var i = 0; i < g_Map.objects.length; ++i)
     515// {
     516    // var id = g_Map.objects[i].id;
     517    // if (i + 150 != id)
     518        // log("The " + i + "-th object (should be ID " + uneval(i + 150) + ") has ID " + id);
     519// }
     520/**
     521* DEBUG Entity IDs END
     522*/
  • binaries/data/mods/public/maps/random/realisticTerrainDemo.json

     
     1{
     2    "settings" : {
     3        "Name" : "Realistic Terrain Demo",
     4        "Script" : "realisticTerrainDemo.js",
     5        "Description" : "A map to demonstrate playability of more realistic random terrain.",
     6        "CircularMap" : false,
     7        "BaseTerrain" : "whiteness",
     8        "BaseHeight" : 0,
     9        "Keywords": ["demo"],
     10        "XXXXXX" : "Optionally define other things here, like we would for a scenario"
     11    }
     12}
     13 No newline at end of file
  • binaries/data/mods/public/maps/random/rmgen/heightmap_manipulation.js

     
     1/**
     2*   Heightmap manipulation functionality
     3*
     4*   A heightmapt is an array of width arrays of height floats
     5*   Width and height is normally mapSize+1 (Number of vertices is one bigger than number of tiles in each direction)
     6*   The default heightmap is g_Map.height (See the Map object)
     7*
     8*   NOTE (ambiguous naming/potential confusion):
     9*       For using this heightmap functionalities it is VITAL that TILE_CENTERED_HEIGHT_MAP = false (default)!
     10*       Otherwise TILE_CENTERED_HEIGHT_MAP has nothing to do with any tile centered heightmap in this library!
     11*       All tile centered heightmaps in this module are temporary objects only and should never replace g_Map.height!
     12*       (Tile centered heightmaps are one less in width and hight than heightmaps used by the engine)
     13*       (Multiple conversions will lead to strange smoothing effects at best and potentially break the map entirely!)
     14*       In the long run TILE_CENTERED_HEIGHT_MAP should be removed and g_Map.height should never be tile centered...
     15*/
     16
     17/**
     18* getMinAndMaxHeight
     19*   Optionally takes a heightmap, default is g_Map.height
     20*   Returns the minimum and maximum heights present in this heightmap (an associative array of the form {"min": minHeight, "max": maxHeight})
     21*/
     22function getMinAndMaxHeight(heightmap)
     23{
     24    heightmap = (heightmap || g_Map.height)
     25    var height = {};
     26    height.min = Infinity;
     27    height.max = - Infinity;
     28    for (var x = 0; x < heightmap.length; ++x)
     29    {
     30        for (var y = 0; y < heightmap[x].length; ++y)
     31        {
     32            if (heightmap[x][y] < height.min)
     33                height.min = heightmap[x][y];
     34            else if (heightmap[x][y] > height.max)
     35                height.max = heightmap[x][y];
     36        }
     37    }
     38    return height;
     39}
     40
     41/**
     42* rescaleHeightmap
     43*   Takes:
     44*       Optional minHeight(float), minimum height that should be used for the resulting heightmap, default is MIN_HEIGHT
     45*       Optional maxHeight(float), maximum height that should be used for the resulting heightmap, default is MAX_HEIGHT
     46*       Optional heightmap, default is g_Map.height
     47*   Rescales the heightmap so its minimum and maximum height is as the arguments told (preserving it's global shape)
     48*/
     49function rescaleHeightmap(minHeight, maxHeight, heightmap)
     50{
     51    minHeight = (minHeight || MIN_HEIGHT);
     52    maxHeight = (maxHeight || MAX_HEIGHT);
     53    heightmap = (heightmap || g_Map.height);
     54   
     55    var oldHeightRange = getMinAndMaxHeight(heightmap);
     56    var max_x = heightmap.length;
     57    var max_y = heightmap[0].length;
     58    for (var x = 0; x < max_x; ++x)
     59        for (var y = 0; y < max_y; ++y)
     60            heightmap[x][y] = minHeight + (heightmap[x][y] - oldHeightRange.min) / (oldHeightRange.max - oldHeightRange.min) * (maxHeight - minHeight);
     61}
     62
     63/**
     64* getInclineMap
     65*   Optionally takes a heightmap, default is g_Map.height
     66*   Returns an inclination map corresponding to the tiles between the heightmaps vertices:
     67*       array of heightmap width-1 arrays of height-1 vectors (associative arrays) of the from:
     68*           {"x": x_slope, "y": y_slope] so a 2D Vector pointing to the hightest incline (with the length the incline in the vectors direction)
     69* NOTE: The x and y coordinates of a tile in the terrain texture map correspond to those of the inclination map
     70*/
     71function getInclineMap(heightmap)
     72{
     73    heightmap = (heightmap || g_Map.height);
     74    var max_x = heightmap.length - 1;
     75    var max_y = heightmap[0].length - 1;
     76    var inclineMap = [];
     77    for (var x = 0; x < max_x; ++x)
     78    {
     79        inclineMap[x] = [];
     80        for (var y = 0; y < max_y; ++y)
     81        {
     82            var dx = heightmap[x + 1][y] - heightmap[x][y];
     83            var dy = heightmap[x][y + 1] - heightmap[x][y];
     84            var next_dx = heightmap[x + 1][y + 1] - heightmap[x][y + 1];
     85            var next_dy = heightmap[x + 1][y + 1] - heightmap[x + 1][y];
     86            inclineMap[x][y] = {"x": 0.5 * (dx + next_dx), "y": 0.5 * (dy + next_dy)};
     87        }
     88    }
     89    return inclineMap;
     90}
     91
     92/**
     93* getSlopeMap
     94*   Optionally takes an inclineMap, default is getInclineMap(g_Map.height)
     95*   Returns a slope map (same form as the a heightmap with one less width and height)
     96* NOTES:
     97*   Not normalized. Only returns the steepness (float), not the direction of incline.
     98*   The x and y coordinates of a tile in the terrain texture map correspond to those of the slope map
     99*/
     100function getSlopeMap(inclineMap)
     101{
     102    inclineMap = (inclineMap || getInclineMap(g_Map.height));
     103    var max_x = inclineMap.length;
     104    var slopeMap = new Array(max_x);
     105    for (var x = 0; x < max_x; ++x)
     106    {
     107        var max_y = inclineMap[x].length;
     108        slopeMap[x] = new Float32Array(max_y);
     109        for (var y = 0; y < max_y; ++y)
     110            slopeMap[x][y] = Math.pow(inclineMap[x][y].x * inclineMap[x][y].x + inclineMap[x][y].y * inclineMap[x][y].y, 0.5);
     111    }
     112    return slopeMap;
     113}
     114
     115/**
     116* getTileCenteredHeightmap
     117*   Optionally takes a heightmap, default is g_Map.height
     118*   Returns an approximation of the heights of the tiles between the vertices, a tile centered heightmap
     119*   NOTE:
     120*       A tile centered heightmap is one smaller in width and height than an ordinary heightmap
     121*       It is meant to e.g. texture a map by height (x/y coordinates correspond to those of the terrain texture map)
     122*       Don't use this to override g_Map height (Potentially breaks the map)!
     123*/
     124function getTileCenteredHeightmap(heightmap)
     125{
     126    heightmap = (heightmap || g_Map.height);
     127    var max_x = heightmap.length - 1;
     128    var max_y = heightmap[0].length - 1;
     129    var tchm = new Array(max_x);
     130    for (var x = 0; x < max_x; ++x)
     131    {
     132        tchm[x] = new Float32Array(max_y);
     133        for (var y = 0; y < max_y; ++y)
     134        {
     135            tchm[x][y] = 0.25 * (heightmap[x][y] + heightmap[x + 1][y] + heightmap[x][y + 1] + heightmap[x + 1][y + 1]);
     136        }
     137    }
     138    return tchm;
     139}
     140
     141/**
     142*   getStartLocationsByHeightmap
     143*
     144*   Description:
     145*       Get start location with the largest minimum distance between players
     146*   Takes
     147*       hightRange      An associative array with keys "min" and "max" each containing a float (the height range start locations are allowed)
     148*       maxTries        Optional, default is 1000, an integer, how often random player distributions are rolled to be compared
     149*       minDistToBorder Optional, default is 20, an integer, how far start locations have to be away from the map border
     150*       numberOfPlayers Optional, default is g_MapSettings.PlayerData.length, an integer, how many start locations should be placed
     151*       Heightmap       Optional, default is g_Map.height, the heightmap for the start locations to be placed on
     152*   Returns
     153*       An array of 2D points (arrays of length 2)
     154*/
     155function getStartLocationsByHeightmap(hightRange, maxTries, minDistToBorder, numberOfPlayers, heightmap)
     156{
     157    maxTries = (maxTries || 1000);
     158    minDistToBorder = (minDistToBorder || 20);
     159    numberOfPlayers = (numberOfPlayers || g_MapSettings.PlayerData.length);
     160    heightmap = (heightmap || g_Map.height);
     161   
     162    var validStartLoc = [];
     163    for (var x = minDistToBorder; x < heightmap.length - minDistToBorder; ++x)
     164        for (var y = minDistToBorder; y < heightmap[0].length - minDistToBorder; ++y)
     165            if (heightmap[x][y] > hightRange.min && heightmap[x][y] < hightRange.max) // Has the right hight
     166                validStartLoc.push({"x": x, "y": y});
     167   
     168    var maxMinDist = 0;
     169    for (var tries = 0; tries < maxTries; ++tries)
     170    {
     171        var startLoc = [];
     172        var minDist = heightmap.length;
     173        for (var p = 0; p < numberOfPlayers; ++p)
     174            startLoc.push(validStartLoc[randInt(validStartLoc.length)]);
     175        for (var p1 = 0; p1 < numberOfPlayers - 1; ++p1)
     176        {
     177            for (var p2 = p1 + 1; p2 < numberOfPlayers; ++p2)
     178            {
     179                var dist = getDistance(startLoc[p1].x, startLoc[p1].y, startLoc[p2].x, startLoc[p2].y);
     180                if (dist < minDist)
     181                    minDist = dist;
     182            }
     183        }
     184        if (minDist > maxMinDist)
     185        {
     186            maxMinDist = minDist;
     187            var finalStartLoc = startLoc;
     188        }
     189    }
     190   
     191    return finalStartLoc;
     192}
     193
     194/**
     195*   distributeEntitiesByHeight
     196*   Takes
     197*       hightRange      An associative array with keys "min" and "max" each containing a float (the height range start locations are allowed)
     198*       startLoc        An array of 2D points (arrays of length 2)
     199*       heightmap       Optional, default is g_Map.height, an array of (map width) arrays of (map depth) floats
     200*       entityList      Array of entities/actors (strings to be placed with placeObject())
     201*       maxTries        Optional, default is 1000, an integer, how often random player distributions are rolled to be compared
     202*       minDistance     Optional, default is 30, an integer, how far start locations have to be away from start locations and the map border
     203*   Returns
     204*       An array of 2D points (arrays of length 2)
     205*/
     206function distributeEntitiesByHeight(hightRange, startLoc, entityList, maxTries, minDistance, heightmap)
     207{
     208    entityList = (entityList || [rbe11, rbe13]);
     209    maxTries = (maxTries || 1000);
     210    minDistance = (minDistance || 30);
     211    heightmap = (heightmap || g_Map.height);
     212   
     213    var placements = deepcopy(startLoc);
     214    var validTiles = [];
     215    for (var x = minDistance; x < heightmap.length - minDistance; ++x)
     216        for (var y = minDistance; y < heightmap[0].length - minDistance; ++y)
     217            if (heightmap[x][y] > hightRange.min && heightmap[x][y] < hightRange.max) // Has the right hight
     218                validTiles.push({"x": x, "y": y});
     219   
     220    for (var tries = 0; tries < maxTries; ++tries)
     221    {
     222        var tile = validTiles[randInt(validTiles.length)];
     223        var isValid = true;
     224        for (var p = 0; p < placements.length; ++p)
     225        {
     226            if (getDistance(placements[p].x, placements[p].y, tile.x, tile.y) < minDistance)
     227            {
     228                isValid = false;
     229                break;
     230            }
     231        }
     232        if (isValid)
     233        {
     234            placeObject(tile.x, tile.y, entityList[randInt(entityList.length)], 0, randFloat(0, 2*PI));
     235            // placeObject(tile[0], tile[1], "actor|geology/decal_stone_medit_b.xml", 0, randFloat(0, 2*PI));
     236            placements.push(tile);
     237        }
     238    }
     239}
     240
     241/**
     242*   setBaseTerrainDiamondSquare
     243*
     244*   Description:
     245*       Sets the heightmap to a relatively realistic shape.
     246*   Takes:
     247*       minHeight           Optional, Float, Default is the absolute possible minimum height
     248*                           Minimum height that can occur in the resulting heightmap
     249*       maxHeight           Optional, Float, Default is the absolute possible maximum height
     250*                           Maximum height that can occur in the resulting heightmap
     251*       smoothness          Optional, Float between 0 (rough) to 1 (smooth), Default is 0.5
     252*                           Lower values (rough) will generate more local structures (but should be smoothed e.g. with erosion later)
     253*       initialHeightmap    Optional, Array of width Arrays of depth Floats (usually a small square heightmap e.g. 2x2)
     254*                           Sets some initial heights at the edges (square heightmap of size 2) or more more specifically with bigger initial heightmaps
     255*       heightmap           Optional, Array of width Arrays of depth Floats (a heightmap), Default is g_Map.height
     256*                           The heightmap that will be set by this function
     257*   Returns nothing
     258*   NOTES:
     259*       min/maxHeight will not necessarily be present in the heightmap
     260*       This function only supports rectangular heightmaps.
     261*           This means that e.g. the edges (given by initialHeightmap) will not be in the playable map area in circular maps.
     262*       The function doubles the size of the initial heightmap (if given, else a random 2x2 one) until it's big enough.
     263*           Then the extend is cut. So the impact and scale of the initial heightmap depends on its size and the target map size.
     264*/
     265function setBaseTerrainDiamondSquare(minHeight, maxHeight, smoothness, initialHeightmap, heightmap)
     266{
     267    // Make some arguments optional
     268    minHeight = (minHeight || MIN_HEIGHT);
     269    maxHeight = (maxHeight || MAX_HEIGHT);
     270    var heightRange = maxHeight - minHeight;
     271    if (heightRange <= 0)
     272        warn("setBaseTerrainDiamondSquare: heightRange < 0");
     273    smoothness = (smoothness || 0.5);
     274    initialHeightmap = (initialHeightmap || [[randFloat(minHeight / 2, maxHeight / 2), randFloat(minHeight / 2, maxHeight / 2)], [randFloat(minHeight / 2, maxHeight / 2), randFloat(minHeight / 2, maxHeight / 2)]]);
     275    heightmap = (heightmap || g_Map.height);
     276   
     277    var offset = heightRange / 2;
     278   
     279    // Double initialHeightmap width until target width is reached (diamond square method)
     280    while (initialHeightmap.length < heightmap.length)
     281    {
     282        var newHeightmap = [];
     283        var oldWidth = initialHeightmap.length;
     284        // Square
     285        for (var x = 0; x < 2 * oldWidth - 1; ++x)
     286        {
     287            newHeightmap.push([]);
     288            for (var y = 0; y < 2 * oldWidth - 1; ++y)
     289            {
     290                if (x % 2 == 0 && y % 2 == 0) // Old tile
     291                    newHeightmap[x].push(initialHeightmap[x/2][y/2]);
     292                else if (x % 2 == 1 && y % 2 == 1) // New tile with diagonal old tile neighbors
     293                {
     294                    newHeightmap[x].push((initialHeightmap[(x-1)/2][(y-1)/2] + initialHeightmap[(x+1)/2][(y-1)/2] + initialHeightmap[(x-1)/2][(y+1)/2] + initialHeightmap[(x+1)/2][(y+1)/2]) / 4);
     295                    newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset)
     296                }
     297                else // New tile with straight old tile neighbors
     298                    newHeightmap[x].push(undefined); // Define later
     299            }
     300        }
     301        // Diamond
     302        for (var x = 0; x < 2 * oldWidth - 1; ++x)
     303        {
     304            for (var y = 0; y < 2 * oldWidth - 1; ++y)
     305            {
     306                if (newHeightmap[x][y] === undefined)
     307                {
     308                    if (x > 0 && x + 1 < newHeightmap.length - 1 && y > 0 && y + 1 < newHeightmap.length - 1) // Not a border tile
     309                    {
     310                        newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 4;
     311                        newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset)
     312                    }
     313                    else if (x < newHeightmap.length - 1 && y > 0 && y < newHeightmap.length - 1) // Left border
     314                    {
     315                        newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x][y-1]) / 3;
     316                        newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset)
     317                    }
     318                    else if (x > 0 && y > 0 && y < newHeightmap.length - 1) // Right border
     319                    {
     320                        newHeightmap[x][y] = (newHeightmap[x][y+1] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 3;
     321                        newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset)
     322                    }
     323                    else if (x > 0 && x < newHeightmap.length - 1 && y < newHeightmap.length - 1) // Bottom border
     324                    {
     325                        newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x-1][y]) / 3;
     326                        newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset)
     327                    }
     328                    else if (x > 0 && x < newHeightmap.length - 1 && y > 0) // Top border
     329                    {
     330                        newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 3;
     331                        newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset)
     332                    }
     333                }
     334            }
     335        }
     336        initialHeightmap = deepcopy(newHeightmap);
     337        offset /= Math.pow(2, smoothness);
     338    }
     339   
     340    // Cut initialHeightmap to fit target width
     341    var shift = [floor((newHeightmap.length - heightmap.length) / 2), floor((newHeightmap[0].length - heightmap[0].length) / 2)];
     342    for (var x = 0; x < heightmap.length; ++x)
     343        for (var y = 0; y < heightmap[0].length; ++y)
     344            heightmap[x][y] = newHeightmap[x][y];
     345}
     346
     347/**
     348*   globalSmoothHeightmap
     349*
     350*   Takes:
     351*       strength    Optional, Float between 0 (no effect) and 1 (2 appending tiles will be set to roughly the same height), default is 0.8
     352*                   How strong the smooth effect should be
     353*       heightmap   Optional, Array of width Arrays of depth Floats (a heightmap), Default is g_Map.height
     354*                   The heightmap to be smoothed
     355*   Returns nothing
     356*   NOTES:
     357*       Strength values >0.9 might result in interference.
     358*       To strengthen the smooth effect it's better to apply it multiple times than using high strength values.
     359*/
     360function globalSmoothHeightmap(strength, heightmap)
     361{
     362    strength = (strength || 0.8); // 0 to 1
     363    heightmap = (heightmap || g_Map.height);
     364   
     365    var referenceHeightmap = deepcopy(heightmap);
     366    // var map = [[1, 0], [0, 1], [-1, 0], [0, -1]]; // faster
     367    var map = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]; // smoother
     368    var max_x = heightmap.length;
     369    var max_y = heightmap[0].length;
     370    for (var x = 0; x < max_x; ++x)
     371    {
     372        for (var y = 0; y < max_y; ++y)
     373        {
     374            for (var i = 0; i < map.length; ++i)
     375            {
     376                var mapX = x + map[i][0];
     377                var mapY = y + map[i][1];
     378                if (mapX >= 0 && mapX < max_x && mapY >= 0 && mapY < max_y)
     379                    heightmap[x][y] += strength / map.length * (referenceHeightmap[mapX][mapY] - referenceHeightmap[x][y]);
     380            }
     381        }
     382    }
     383}
     384
     385/**
     386*   rectangularSmoothToHeight
     387*
     388*   Description:
     389*       Pushes a rectangular area towards a given height smoothing it into the original terrain
     390*   Takes:
     391*       center          Associative Array with keys x and y containing Floats (the x/y coordinates of the center)
     392*       dx/dy           Float, Distance from the center in x/y direction the rectangle ends (half width/depth)
     393*       targetHeight    Float, Height the center of the rectangle will be pushed to
     394*       strength        Float between 0 (no change) to 1 (center set to target height), how strong the height is pushed
     395*       heightmap       Optional, Array of width Arrays of depth Floats (a heightmap), Default is g_Map.height
     396*                       The heightmap to be manipulated
     397*   Returns nothing
     398*   NOTES:
     399*       The window function to determine the smooth is not exactly a gaussian to ensure smooth edges.
     400*       The optimal function is not clear so several working ones are left in the function (documented out)
     401*/
     402function rectangularSmoothToHeight(center, dx, dy, targetHeight, strength, heightmap)
     403{
     404    var x = round(center.x);
     405    var y = round(center.y);
     406    dx = round(dx);
     407    dy = round(dy);
     408    strength = (strength || 1);
     409    heightmap = (heightmap || g_Map.height);
     410   
     411    var heightmapWin = [];
     412    for (var wx = 0; wx < 2 * dx + 1; ++wx)
     413    {
     414        heightmapWin.push([]);
     415        for (var wy = 0; wy < 2 * dy + 1; ++wy)
     416        {
     417            var actualX = x - dx + wx;
     418            var actualY = y - dy + wy;
     419            if (actualX >= 0 && actualX < heightmap.length - 1 && actualY >= 0 && actualY < heightmap[0].length - 1) // Is in map
     420                heightmapWin[wx].push(heightmap[actualX][actualY]);
     421            else
     422                heightmapWin[wx].push(targetHeight);
     423        }
     424    }
     425    for (var wx = 0; wx < 2 * dx + 1; ++wx)
     426    {
     427        for (var wy = 0; wy < 2 * dy + 1; ++wy)
     428        {
     429            var actualX = x - dx + wx;
     430            var actualY = y - dy + wy;
     431            if (actualX >= 0 && actualX < heightmap.length - 1 && actualY >= 0 && actualY < heightmap[0].length - 1) // Is in map
     432            {
     433                // // Window function rectangle
     434                // var scaleX = 1;
     435                // var scaleY = 1;
     436                // // Window function triangle
     437                // var scaleX = min(min(wx / dx, (2 * dx + 1 - wx) / dx), 1);
     438                // var scaleY = min(min(wy / dy, (2 * dy + 1 - wy) / dy), 1);
     439                // // Window function trapeze
     440                // var scaleX = min(min(1.3 * wx / dx, 1.3 * (2 * dx + 1 - wx) / dx), 1);
     441                // var scaleY = min(min(1.3 * wy / dy, 1.3 * (2 * dy + 1 - wy) / dy), 1);
     442                // // Window function simple sine (range PI)
     443                // var scaleX = sin(wx * PI / (2 * dx + 1));
     444                // var scaleY = sin(wy * PI / (2 * dy + 1));
     445                // // Window function cosine (range 2*PI)
     446                // var scaleX = (cos((wx - (2 * dx + 1) / 2) * 2 * PI / (2 * dx + 1)) + 1) / 2;
     447                // var scaleY = (cos((wy - (2 * dy + 1) / 2) * 2 * PI / (2 * dy + 1)) + 1) / 2;
     448                // // Window function polynomial 2nd degree (BEST SINGLE SO FAR)
     449                var scaleX = 1 - (wx / dx - 1) * (wx / dx - 1);
     450                var scaleY = 1 - (wy / dy - 1) * (wy / dy - 1);
     451                // // Window function polynomial 4th degree
     452                // var scaleX = (wx / dx - 1) * (wx / dx - 1) * (wx / dx - 1) * (wx / dx - 1) - 2 * (wx / dx - 1) * (wx / dx - 1) + 1;
     453                // var scaleY = (wy / dy - 1) * (wy / dy - 1) * (wy / dy - 1) * (wy / dy - 1) - 2 * (wy / dy - 1) * (wy / dy - 1) + 1;
     454                // COMBINATIONS
     455                // // Window function arithmetic mean of simple sine (range PI) and trapeze
     456                // var scaleX = (sin(wx * PI / (2 * dx + 1)) + min(min(2.1 * wx / dx, 2.1 * (2 * dx + 1 - wx) / dx), 1)) / 2;
     457                // var scaleY = (sin(wy * PI / (2 * dy + 1)) + min(min(2.1 * wy / dy, 2.1 * (2 * dy + 1 - wy) / dy), 1)) / 2;
     458                // // Window function arithmetic mean of cosine (range 2*PI) and trapeze
     459                // var scaleX = ((cos((wx - (2 * dx + 1) / 2) * 2 * PI / (2 * dx + 1)) + 1) / 2 + min(min(5 * wx / dx, 5 * (2 * dx + 1 - wx) / dx), 1)) / 2;
     460                // var scaleY = ((cos((wy - (2 * dy + 1) / 2) * 2 * PI / (2 * dy + 1)) + 1) / 2 + min(min(5 * wy / dy, 5 * (2 * dy + 1 - wy) / dy), 1)) / 2;
     461                // // Window function arithmetic mean of polynomial 2nd degree and trapeze (BEST COMBINATION SO FAR)
     462                // var scaleX = (1 - (wx / dx - 1) * (wx / dx - 1) + min(min(3 * wx / dx, 3 * (2 * dx + 1 - wx) / dx), 1)) / 2;
     463                // var scaleY = (1 - (wy / dy - 1) * (wy / dy - 1) + min(min(3 * wy / dy, 3 * (2 * dy + 1 - wy) / dy), 1)) / 2;
     464               
     465                if (scaleX < 0 || scaleY < 0 || scaleX > 1 || scaleY > 1) // DEBUG
     466                    warn("rectangularSmoothToHeight: Scale < 0 or > 1: scaleX = " + scaleX + ", scaleY = " + scaleY); // DEBUG
     467                heightmap[actualX][actualY] = heightmapWin[wx][wy] + strength * scaleX * scaleY * (targetHeight - heightmapWin[wx][wy]);
     468                // g_Map.setTexture(actualX, actualY, "red"); // DEBUG
     469            }
     470        }
     471    }
     472}
  • binaries/data/mods/public/maps/random/rmgen/library.js

     
    1 
    2 /////////////////////////////////////////////////////////////////////////////////////////////
    3 //  Constant definitions
    4 /////////////////////////////////////////////////////////////////////////////////////////////
    5 
     1/**
     2 * Constant definitions
     3 */
    64const PI = Math.PI;
    7 const TWO_PI = 2 * Math.PI;
     5const TWO_PI = 2 * Math.PI; // mathematically known as 'tau'
    86const TERRAIN_SEPARATOR = "|";
    9 const SEA_LEVEL = 20.0;
     7const SEA_LEVEL = 160.0;
    108const CELL_SIZE = 4;
    119const HEIGHT_UNITS_PER_METRE = 92;
    1210const MIN_MAP_SIZE = 128;
    1311const MAX_MAP_SIZE = 512;
    1412const FALLBACK_CIV = "athen";
    1513
    16 /////////////////////////////////////////////////////////////////////////////////////////////
    17 //  Utility functions
    18 /////////////////////////////////////////////////////////////////////////////////////////////
     14/**
     15 * Constants needed for heightmap_manipulation.js
     16 */
     17const MAX_HEIGHT_RANGE = 0xFFFF / HEIGHT_UNITS_PER_METRE // Engine limit, Roughly 700 meters
     18const MIN_HEIGHT = - SEA_LEVEL;
     19const MAX_HEIGHT = MAX_HEIGHT_RANGE - SEA_LEVEL;
     20/**
     21 * Entity template structure keys that might change, for easier mod support
     22 */
     23const CIV_PLACEHOLDER_STRING = "{civ}";
     24const START_ENTITY_KEYS = ["StartEntities"];
     25const START_ENTITY_TEMPLATE_KEYS = ["Template"];
     26const BUILDER_TEMPLATEPATH_KEYS = ["Builder", "Entities", "_string"];
     27const PRODUCTION_TEMPLATEPATH_KEYS = ["ProductionQueue", "Entities", "_string"];
     28const WALLSET_KEYS = ["WallSet"];
     29const BUILDING_KEYS = ["Obstruction", "Static"];
    1930
     31
     32/**
     33 * Utility functions
     34 */
     35
    2036function fractionToTiles(f)
    2137{
    2238    return getMapSize() * f;
     
    103119    if (numArgs != 1)
    104120    {
    105121        var ret = new Array(numArgs);
    106         for (var i=0; i < numArgs; i++)
     122        for (var i=0; i < numArgs; ++i)
    107123        {
    108124            ret[i] = x[i];
    109125        }
     
    125141    return ar[randInt(ar.length)];
    126142}
    127143
    128 // "Inside-out" implementation of Fisher-Yates shuffle
     144/**
     145 * "Inside-out" implementation of Fisher-Yates shuffle
     146 */
    129147function shuffleArray(source)
    130148{
    131149    if (!source.length)
    132150        return [];
    133151
    134152    var result = [source[0]];
    135     for (var i = 1; i < source.length; i++)
     153    for (var i = 1; i < source.length; ++i)
    136154    {
    137155        var j = randInt(0, i);
    138156        result[i] = result[j];
     
    172190        var area = g_Map.createArea(centeredPlacer, painter, constraint);
    173191        if (area !== undefined)
    174192        {
    175             good++;
     193            ++good;
    176194            result.push(area);
    177195        }
    178196        else
    179197        {
    180             bad++;
     198            ++bad;
    181199        }
    182200    }
    183201    return result;
     
    208226        var area = g_Map.createArea(centeredPlacer, painter, constraint);
    209227        if (area !== undefined)
    210228        {
    211             good++;
     229            ++good;
    212230            result.push(area);
    213231        }
    214232        else
    215233        {
    216             bad++;
     234            ++bad;
    217235        }
    218236    }
    219237    return result;
     
    248266        var result = createObjectGroup(placer, player, constraint);
    249267        if (result !== undefined)
    250268        {
    251             good++;
     269            ++good;
    252270        }
    253271        else
    254272        {
    255             bad++;
     273            ++bad;
    256274        }
    257275    }
    258276    return good;
     
    282300        var result = createObjectGroup(placer, player, constraint);
    283301        if (result !== undefined)
    284302        {
    285             good++;
     303            ++good;
    286304        }
    287305        else
    288306        {
    289             bad++;
     307            ++bad;
    290308        }
    291309    }
    292310    return good;
     
    349367    return (g_MapSettings.CircularMap ? true : false);
    350368}
    351369
    352 /////////////////////////////////////////////////////////////////////////////////////////////
    353 //  Access global map variable
    354 /////////////////////////////////////////////////////////////////////////////////////////////
     370/**
     371 * Access global map variable
     372 */
    355373
    356374function createTileClass()
    357375{
     
    394412    return g_MapSettings.PlayerData.length - 1;
    395413}
    396414
     415/**
     416 * Takes nothing, returns an array of strings representing all available civilizations
     417 */
     418function getCivList()
     419{
     420    var raw_civData = RMS.GetCivData();
     421    var civList = [];
     422    for (var i = 0; i < raw_civData.length; ++i)
     423        civList.push(JSON.parse(raw_civData[i]).Code);
     424   
     425    return civList;
     426}
     427
     428/**
     429 * Takes nothing, returns an associative array with civ strings as keys containing all unpacked civ data (Templates need to be unpacked with RMS.GetTemplate() if needed)
     430 */
     431function getFullCivData()
     432{
     433    var rawCivData = RMS.GetCivData();
     434    var unpackedCivData = {};
     435    for (var i = 0; i < rawCivData.length; ++i)
     436    {
     437        var singleCivData = JSON.parse(rawCivData[i]);
     438        unpackedCivData[singleCivData.Code] = singleCivData;
     439    }
     440   
     441    return unpackedCivData;
     442}
     443
     444/**
     445 * Takes a player number (0-7, so Gaia excluded). Returns this players civ string
     446 * ToDo: If the player number is to high an error will occur (and the fallback won't be reached)!
     447 */
    397448function getCivCode(player)
    398449{
    399450    if (g_MapSettings.PlayerData[player+1].Civ)
    400451        return g_MapSettings.PlayerData[player+1].Civ;
    401452
    402     warn("undefined civ specified for player " + (player + 1) + ", falling back to '" + FALLBACK_CIV + "'");
     453    warn("Undefined civ specified for player " + (player + 1) + ", falling back to '" + FALLBACK_CIV + "'");
    403454    return FALLBACK_CIV;
    404455}
    405456
     457/**
     458 * Takes an entity path and a key list to get the templates value
     459 */
     460function getTemplateValue(entPath, key_list)
     461{
     462    var subdata = RMS.GetTemplate(entPath);
     463    for (var i = 0; i < key_list.length; ++i)
     464    {
     465        if (!subdata[key_list[i]])
     466        {
     467            return false
     468        }
     469        subdata = subdata[key_list[i]];
     470    }
     471    return subdata;
     472}
     473
     474/**
     475 * Returns a list of all templates paths available to the given civ
     476 */
     477function getTempatePathList(civ)
     478{
     479    var templatePaths = getFullCivData();
     480    if (!templatePaths[civ])
     481    {
     482        warn("getTempatePathList: Unknown civ '" + civ + "' not in " + Object.keys(templatePaths));
     483        return false;
     484    }
     485    templatePaths = templatePaths[civ];
     486   
     487    if (!templatePaths[START_ENTITY_KEYS])
     488    {
     489        warn("getTempatePathList: Civ has no starting entities as defined in START_ENTITY_KEYS (" + START_ENTITY_KEYS + "): " + Object.keys(templatePaths));
     490        return false;
     491    }
     492    templatePaths = templatePaths[START_ENTITY_KEYS];
     493   
     494    for (var i = 0; i < templatePaths.length; ++i)
     495    {
     496        if (!templatePaths[i][START_ENTITY_TEMPLATE_KEYS])
     497        {
     498            warn("getTempatePathList: Starting entity list item has no template as defined in START_ENTITY_TEMPLATE_KEYS (" + START_ENTITY_TEMPLATE_KEYS + "): " + Object.keys(templatePaths));
     499            return false;
     500        }
     501        templatePaths[i] = templatePaths[i][START_ENTITY_TEMPLATE_KEYS];
     502    }
     503    var foundNew = 1;
     504    while (foundNew > 0)
     505    {
     506        foundNew = 0;
     507        var methods = [BUILDER_TEMPLATEPATH_KEYS, PRODUCTION_TEMPLATEPATH_KEYS];
     508        for (var m = 0; m < methods.length; ++m)
     509        {
     510            for (var t = 0; t < templatePaths.length; ++t)
     511            {
     512                var pathsToCheck = getTemplateValue(templatePaths[t], methods[m]);
     513                if (typeof(pathsToCheck) === typeof(""))
     514                {
     515                    pathsToCheck = pathsToCheck.split(/\s+/);
     516                    for (var c = 0; c < pathsToCheck.length; ++c)
     517                    {
     518                        var actualPath = pathsToCheck[c].replace(CIV_PLACEHOLDER_STRING, civ);
     519                        if (templatePaths.indexOf(actualPath) == -1 && RMS.TemplateExists(actualPath))
     520                        {
     521                            templatePaths.push(actualPath);
     522                            ++foundNew;
     523                        }
     524                    }
     525                }
     526            }
     527        }
     528    }
     529    return templatePaths;
     530}
     531
     532/**
     533 * Takes a list of entity path strings and returns a list with only those remaining that have the specified keys
     534 */
     535function filterByKeys(entPaths, keys)
     536{
     537    let filteredEntPaths = [];
     538    for (let i = 0; i < entPaths.length; ++i)
     539    {
     540        if (getTemplateValue(entPaths[i], keys))
     541            filteredEntPaths.push(entPaths[i]);
     542    }
     543    return filteredEntPaths;
     544}
     545
    406546function areAllies(player1, player2)
    407547{
    408548    if ((g_MapSettings.PlayerData[player1+1].Team === undefined) || (g_MapSettings.PlayerData[player2+1].Team === undefined) || (g_MapSettings.PlayerData[player2+1].Team == -1) || (g_MapSettings.PlayerData[player1+1].Team == -1))
     
    434574
    435575    var result = new Array(0);
    436576    var team = new Array(5);
    437     for (var q = 0; q < 5; q++)
     577    for (var q = 0; q < 5; ++q)
    438578    {
    439579        team[q] = new Array(1);
    440580    }
    441581
    442     for (var i = -1; i < 4; i++)
     582    for (var i = -1; i < 4; ++i)
    443583    {
    444         for (var j = 0; j < source.length; j++)
     584        for (var j = 0; j < source.length; ++j)
    445585        {
    446586            if (getPlayerTeam(j) == i)
    447             {
    448587                team[i+1].unshift(j+1);
    449             }
    450588        }
    451589        team[i+1].pop();
    452590        result=result.concat(shuffleArray(team[i+1]))
     
    461599
    462600    var prime = new Array(source.length);
    463601
    464     for (var i = 0; i < round(source.length/2); i++)
     602    for (var i = 0; i < round(source.length/2); ++i)
    465603    {
    466604        prime[2*i]=source[i];
    467605        prime[2*i+1]=source[source.length-1-i];
     
    492630    g_Map.setHeight(x, z, height);
    493631}
    494632
    495 /////////////////////////////////////////////////////////////////////////////////////////////
    496 //  Utility functions for classes
    497 /////////////////////////////////////////////////////////////////////////////////////////////
    498633
     634/**
     635 *  Utility functions for classes
     636 */
    499637
    500 // Add point to given class by id
     638/**
     639 * Add point to given class by id
     640 */
    501641function addToClass(x, z, id)
    502642{
    503643    var tileClass = getTileClass(id);
    504644   
    505645    if (tileClass !== null)
    506     {
    507646        tileClass.add(x, z);
    508     }
    509647}
    510648
    511 // Remove point from the given class by id
     649/**
     650 * Remove point from the given class by id
     651 */
    512652function removeFromClass(x, z, id)
    513653{
    514654    var tileClass = getTileClass(id);
    515655   
    516656    if (tileClass !== null)
    517     {
    518657        tileClass.remove(x, z);
    519     }
    520658}
    521659
    522 // Create a painter for the given class
     660/**
     661 * Create a painter for the given class
     662 */
    523663function paintClass(id)
    524664{
    525665    return new TileClassPainter(getTileClass(id));
    526666}
    527667
    528 // Create a painter for the given class
     668/**
     669 * Create a painter for the given class
     670 */
    529671function unPaintClass(id)
    530672{
    531673    return new TileClassUnPainter(getTileClass(id));
    532674}
    533675
    534 // Create an avoid constraint for the given classes by the given distances
     676/**
     677 * Create an avoid constraint for the given classes by the given distances
     678 */
    535679function avoidClasses(/*class1, dist1, class2, dist2, etc*/)
    536680{
    537681    var ar = new Array(arguments.length/2);
    538     for (var i = 0; i < arguments.length/2; i++)
     682    for (var i = 0; i < arguments.length/2; ++i)
    539683    {
    540684        ar[i] = new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1]);
    541685    }
    542686   
    543687    // Return single constraint
    544688    if (ar.length == 1)
    545     {
    546689        return ar[0];
    547     }
    548690    else
    549691    {
    550692        return new AndConstraint(ar);
    551693    }
    552694}
    553695
    554 // Create a stay constraint for the given classes by the given distances
     696/**
     697 * Create a stay constraint for the given classes by the given distances
     698 */
    555699function stayClasses(/*class1, dist1, class2, dist2, etc*/)
    556700{
    557701    var ar = new Array(arguments.length/2);
    558     for (var i = 0; i < arguments.length/2; i++)
     702    for (var i = 0; i < arguments.length/2; ++i)
    559703    {
    560704        ar[i] = new StayInTileClassConstraint(arguments[2*i], arguments[2*i+1]);
    561705    }
     
    571715    }
    572716}
    573717
    574 // Create a border constraint for the given classes by the given distances
     718/**
     719 * Create a border constraint for the given classes by the given distances
     720 */
    575721function borderClasses(/*class1, idist1, odist1, class2, idist2, odist2, etc*/)
    576722{
    577723    var ar = new Array(arguments.length/3);
    578     for (var i = 0; i < arguments.length/3; i++)
     724    for (var i = 0; i < arguments.length/3; ++i)
    579725    {
    580726        ar[i] = new BorderTileClassConstraint(arguments[3*i], arguments[3*i+1], arguments[3*i+2]);
    581727    }
     
    591737    }
    592738}
    593739
    594 // Checks if the given tile is in class "id"
     740/**
     741 * Checks if the given tile is in class "id"
     742 */
    595743function checkIfInClass(x, z, id)
    596744{
    597745    var tileClass = getTileClass(id);
     
    612760    }
    613761}
    614762
    615 
    616 // Returns the distance between 2 points
     763/**
     764 * Returns the distance between 2 points
     765 */
    617766function getDistance(x1, z1, x2, z2)
    618767{
    619768    return Math.pow(Math.pow(x1 - x2, 2) + Math.pow(z1 - z2, 2), 1/2);
    620769}
    621770
    622 // Returns the angle of the vector between point 1 and point 2.  The angle is anticlockwise from the positive x axis.
     771/**
     772 * Returns the angle of the vector between point 1 and point 2.  The angle is anticlockwise from the positive x axis.
     773 */
    623774function getAngle(x1, z1, x2, z2)
    624775{
    625776    return Math.atan2(z2 - z1, x2 - x1);
    626777}
    627778
    628 // Returns the gradient of the line between point 1 and 2 in the form dz/dx
     779/**
     780 * Returns the gradient of the line between point 1 and 2 in the form dz/dx
     781 */
    629782function getGradient(x1, z1, x2, z2)
    630783{
    631784    if (x1 == x2 && z1 == z2)
     
    642795{
    643796    return g_Map.getTexture(x, y);
    644797}
    645 
  • binaries/data/mods/public/maps/random/rmgen/misc.js

     
    1 /////////////////////////////////////////////////////////////////////////////////////////
    2 //  passageMaker
    3 //
    4 //  Function for creating shallow water between two given points by changing the heiight of all tiles in
    5 //  the path with height less than or equal to "maxheight" to "height"
    6 //
    7 //  x1,z1:  Starting point of path
    8 //  x2,z2:  Ending point of path
    9 //  width:  Width of the shallow
    10 //  maxheight:      Maximum height that it changes
    11 //  height:     Height of the shallow
    12 //  smooth:     smooth elevation in borders
    13 //  tileclass:      (Optianal) - Adds those tiles to the class given
    14 //  terrain:        (Optional) - Changes the texture of the elevated land
    15 //
    16 /////////////////////////////////////////////////////////////////////////////////////////
    17 
     1/**
     2 *  passageMaker
     3 *
     4 *  Function for creating shallow water between two given points by changing the heiight of all tiles in
     5 *  the path with height less than or equal to "maxheight" to "height"
     6 *
     7 *  x1,z1:  Starting point of path
     8 *  x2,z2:  Ending point of path
     9 *  width:  Width of the shallow
     10 *  maxheight:      Maximum height that it changes
     11 *  height:     Height of the shallow
     12 *  smooth:     smooth elevation in borders
     13 *  tileclass:      (Optianal) - Adds those tiles to the class given
     14 *  terrain:        (Optional) - Changes the texture of the elevated land
     15 */
    1816function passageMaker(x1, z1, x2, z2, width, maxheight, height, smooth, tileclass, terrain, riverheight)
    1917{
    2018    var tchm = TILE_CENTERED_HEIGHT_MAP;
    2119    TILE_CENTERED_HEIGHT_MAP = true;
    2220    var mapSize = g_Map.size;
    23     for (var ix = 0; ix < mapSize; ix++)
     21    for (var ix = 0; ix < mapSize; ++ix)
    2422    {
    25         for (var iz = 0; iz < mapSize; iz++)
     23        for (var iz = 0; iz < mapSize; ++iz)
    2624        {
    2725            var a = z1-z2;
    2826            var b = x2-x1;
     
    7371    TILE_CENTERED_HEIGHT_MAP = tchm;
    7472}
    7573
    76 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    77 //rndRiver is a fuction that creates random values useful for making a jagged river.
    78 //
    79 //it works the same as sin or cos function. the only difference is that it's period is 1 instead of 2*pi
    80 //it needs the "seed" parameter to use it to make random curves that don't get broken.
    81 //seed must be created using randFloat(). or else it won't work
    82 //
    83 //  f:  Input: Same as angle in a sine function
    84 //  seed:   Random Seed: Best to implement is to use randFloat()
    85 //
    86 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    87 
     74/**
     75 *rndRiver is a fuction that creates random values useful for making a jagged river.
     76 *
     77 *it works the same as sin or cos function. the only difference is that it's period is 1 instead of 2*pi
     78 *it needs the "seed" parameter to use it to make random curves that don't get broken.
     79 *seed must be created using randFloat(). or else it won't work
     80 *
     81 *  f:  Input: Same as angle in a sine function
     82 *  seed:   Random Seed: Best to implement is to use randFloat()
     83 */
    8884function rndRiver(f, seed)
    8985{
    9086    var rndRq = seed;
     
    9288    var rndRe = 0;
    9389    var rndRr = f-floor(f);
    9490    var rndRa = 0;
    95     for (var rndRx=0; rndRx<=floor(f); rndRx++)
     91    for (var rndRx=0; rndRx<=floor(f); ++rndRx)
    9692    {
    9793        rndRw = 10*(rndRw-floor(rndRw));
    9894    }
     
    129125}
    130126
    131127
    132 /////////////////////////////////////////////////////////////////////////////////////////
    133 // createStartingPlayerEntities
    134 //
    135 //  Creates the starting player entities
    136 //  fx&fz: position of player base
    137 //  playerid: id of player
    138 //  civEntities: use getStartingEntities(id-1) fo this one
    139 //  BUILDING_ANGlE: angle of main base building
    140 //
    141 ///////////////////////////////////////////////////////////////////////////////////////////
     128/**
     129 * createStartingPlayerEntities
     130 *
     131 *  Creates the starting player entities
     132 *  fx&fz: position of player base
     133 *  playerid: id of player
     134 *  civEntities: use getStartingEntities(id-1) fo this one
     135 *  BUILDING_ANGlE: angle of main base building
     136 */
    142137function createStartingPlayerEntities(fx, fz, playerid, civEntities, BUILDING_ANGlE)
    143138{
    144139    var uDist = 6;
     
    148143    {
    149144        var uAngle = BUILDING_ANGlE - PI * (2-j) / 2;
    150145        var count = (civEntities[j].Count !== undefined ? civEntities[j].Count : 1);
    151         for (var numberofentities = 0; numberofentities < count; numberofentities++)
     146        for (var numberofentities = 0; numberofentities < count; ++numberofentities)
    152147        {
    153148            var ux = fx + uDist * cos(uAngle) + numberofentities * uSpace * cos(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * cos(uAngle + PI/2));
    154149            var uz = fz + uDist * sin(uAngle) + numberofentities * uSpace * sin(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * sin(uAngle + PI/2));
     
    157152    }
    158153}
    159154
    160 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    161 // placeCivDefaultEntities
    162 //
    163 //  Creates the default starting player entities depending on the players civ
    164 //  fx&fy: position of player base
    165 //  playerid: id of player
    166 //  angle: angle of main base building, optional, default is BUILDING_ANGlE
    167 //  kwargs: Takes some optional keyword arguments to tweek things
    168 //      'iberWall': may be false, 'walls' (default) or 'towers'. Determines the defensive structures Iberians get as civ bonus
    169 //
    170 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     155/**
     156 * placeCivDefaultEntities
     157 *
     158 *  Creates the default starting player entities depending on the players civ
     159 *  fx&fy: position of player base
     160 *  playerid: id of player
     161 *  angle: angle of main base building, optional, default is BUILDING_ANGlE
     162 *  kwargs: Optional. Takes an associative array with keyword arguments to tweak things:
     163 *      Known keys: 'iberWall': may be false, 'walls' (default) or 'towers'. Determines the defensive structures Iberians get as civ bonus
     164 */
    171165function placeCivDefaultEntities(fx, fz, playerid, angle, kwargs)
    172166{
    173167    // Unpack kwargs
     
    187181    {
    188182        var uAngle = angle - PI * (2-j) / 2;
    189183        var count = (civEntities[j].Count !== undefined ? civEntities[j].Count : 1);
    190         for (var numberofentities = 0; numberofentities < count; numberofentities++)
     184        for (var numberofentities = 0; numberofentities < count; ++numberofentities)
    191185        {
    192186            var ux = fx + uDist * cos(uAngle) + numberofentities * uSpace * cos(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * cos(uAngle + PI/2));
    193187            var uz = fz + uDist * sin(uAngle) + numberofentities * uSpace * sin(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * sin(uAngle + PI/2));
     
    205199}
    206200
    207201
    208 /////////////////////////////////////////////////////////////////////////////////////////
    209 // paintTerrainBasedOnHeight
    210 //
    211 //  paints the tiles which have a height between minheight and maxheight with the given terrain
    212 //  minheight: minimum height of the tile
    213 //  maxheight: maximum height of the tile
    214 //  mode: accepts 4 values. 0 means the it will select tiles with height more than minheight and less than maxheight.
    215 //  1 means it selects tiles with height more than or equal to minheight and less than max height. 2 means more than
    216 //  minheight and less than or equal to maxheight. 3 means more than or equal to minheight and less than or equal to maxheight
    217 //  terrain: intended terrain texture
    218 //
    219 ///////////////////////////////////////////////////////////////////////////////////////////
     202/**
     203 * paintTerrainBasedOnHeight
     204 *
     205 *  paints the tiles which have a height between minheight and maxheight with the given terrain
     206 *  minheight: minimum height of the tile
     207 *  maxheight: maximum height of the tile
     208 *  mode: accepts 4 values. 0 means the it will select tiles with height more than minheight and less than maxheight.
     209 *  1 means it selects tiles with height more than or equal to minheight and less than max height. 2 means more than
     210 *  minheight and less than or equal to maxheight. 3 means more than or equal to minheight and less than or equal to maxheight
     211 *  terrain: intended terrain texture
     212 */
    220213
    221214function paintTerrainBasedOnHeight(minheight, maxheight, mode, terrain)
    222215{
    223216    var mSize = g_Map.size;
    224     for (var qx = 0; qx < mSize; qx++)
     217    for (var qx = 0; qx < mSize; ++qx)
    225218    {
    226         for (var qz = 0; qz < mSize; qz++)
     219        for (var qz = 0; qz < mSize; ++qz)
    227220        {
    228221            if (mode == 0)
    229222            {
     
    257250    }
    258251}
    259252
    260 /////////////////////////////////////////////////////////////////////////////////////////
    261 // paintTileClassBasedOnHeight and unPaintTileClassBasedOnHeight
    262 //
    263 //  paints or unpaints the tiles which have a height between minheight and maxheight with the given tile class
    264 //  minheight: minimum height of the tile
    265 //  maxheight: maximum height of the tile
    266 //  mode: accepts 4 values. 0 means the it will select tiles with height more than minheight and less than maxheight.
    267 //  1 means it selects tiles with height more than or equal to minheight and less than max height. 2 means more than
    268 //  minheight and less than or equal to maxheight. 3 means more than or equal to minheight and less than or equal to maxheight
    269 //  tileclass: intended tile class
    270 //
    271 ///////////////////////////////////////////////////////////////////////////////////////////
    272 
     253/**
     254 * paintTileClassBasedOnHeight and unPaintTileClassBasedOnHeight
     255 *
     256 *  paints or unpaints the tiles which have a height between minheight and maxheight with the given tile class
     257 *  minheight: minimum height of the tile
     258 *  maxheight: maximum height of the tile
     259 *  mode: accepts 4 values. 0 means the it will select tiles with height more than minheight and less than maxheight.
     260 *  1 means it selects tiles with height more than or equal to minheight and less than max height. 2 means more than
     261 *  minheight and less than or equal to maxheight. 3 means more than or equal to minheight and less than or equal to maxheight
     262 *  tileclass: intended tile class
     263 */
    273264function paintTileClassBasedOnHeight(minheight, maxheight, mode, tileclass)
    274265{
    275266    var mSize = g_Map.size;
    276     for (var qx = 0; qx < mSize; qx++)
     267    for (var qx = 0; qx < mSize; ++qx)
    277268    {
    278         for (var qz = 0; qz < mSize; qz++)
     269        for (var qz = 0; qz < mSize; ++qz)
    279270        {
    280271            if (mode == 0)
    281272            {
     
    313304function unPaintTileClassBasedOnHeight(minheight, maxheight, mode, tileclass)
    314305{
    315306    var mSize = g_Map.size;
    316     for (var qx = 0; qx < mSize; qx++)
     307    for (var qx = 0; qx < mSize; ++qx)
    317308    {
    318         for (var qz = 0; qz < mSize; qz++)
     309        for (var qz = 0; qz < mSize; ++qz)
    319310        {
    320311            if (mode == 0)
    321312            {
     
    349340    }
    350341}
    351342
    352 /////////////////////////////////////////////////////////////////////////////////////////
    353 // getTIPIADBON
    354 //
    355 //  "get The Intended Point In A Direction Based On Height"
    356 //  gets the N'th point with a specific height in a line and returns it as a [x, y] array
    357 //  startPoint: [x, y] array defining the start point
    358 //  endPoint: [x, y] array defining the ending point
    359 //  heightRange: [min, max] array defining the range which the height of the intended point can be. includes both "min" and "max"
    360 //  step: how much tile units per turn should the search go. more value means faster but less accurate
    361 //  n: how many points to skip before ending the search. skips """n-1 points""".
    362 //
    363 ///////////////////////////////////////////////////////////////////////////////////////////
    364 
     343/**
     344 * getTIPIADBON
     345 *
     346 *  "get The Intended Point In A Direction Based On Height"
     347 *  gets the N'th point with a specific height in a line and returns it as a [x, y] array
     348 *  startPoint: [x, y] array defining the start point
     349 *  endPoint: [x, y] array defining the ending point
     350 *  heightRange: [min, max] array defining the range which the height of the intended point can be. includes both "min" and "max"
     351 *  step: how much tile units per turn should the search go. more value means faster but less accurate
     352 *  n: how many points to skip before ending the search. skips """n-1 points""".
     353 */
    365354function getTIPIADBON(startPoint, endPoint, heightRange, step, n)
    366355{
    367356    var stepX = step*(endPoint[0]-startPoint[0])/(sqrt((endPoint[0]-startPoint[0])*(endPoint[0]-startPoint[0]) + step*(endPoint[1]-startPoint[1])*(endPoint[1]-startPoint[1])));
     
    394383    return undefined;
    395384}
    396385
    397 /////////////////////////////////////////////////////////////////////////////////////////
    398 // doIntersect
    399 //
    400 //  determines if two lines with the width "width" intersect or collide with each other
    401 //  x1, y1, x2, y2: determine the position of the first line
    402 //  x3, y3, x4, y4: determine the position of the second line
    403 //  width: determines the width of the lines
    404 //
    405 ///////////////////////////////////////////////////////////////////////////////////////////
    406 
     386/**
     387 * doIntersect
     388 *
     389 *  determines if two lines with the width "width" intersect or collide with each other
     390 *  x1, y1, x2, y2: determine the position of the first line
     391 *  x3, y3, x4, y4: determine the position of the second line
     392 *  width: determines the width of the lines
     393 */
    407394function checkIfIntersect (x1, y1, x2, y2, x3, y3, x4, y4, width)
    408395{
    409396    if (x1 == x2)
     
    445432    return false;
    446433}
    447434
    448 /////////////////////////////////////////////////////////////////////////////////////////
    449 // distanceOfPointFromLine
    450 //
    451 //  returns the distance of a point from a line
    452 //  x1, y1, x2, y2: determine the position of the line
    453 //  x3, y3: determine the position of the point
    454 //
    455 ///////////////////////////////////////////////////////////////////////////////////////////
    456 
     435/**
     436 * distanceOfPointFromLine
     437 *
     438 *  returns the distance of a point from a line
     439 *  x1, y1, x2, y2: determine the position of the line
     440 *  x3, y3: determine the position of the point
     441 */
    457442function distanceOfPointFromLine (x1, y1, x2, y2, x3, y3)
    458443{
    459444    if (x1 == x2)
     
    473458    }
    474459}
    475460
    476 /////////////////////////////////////////////////////////////////////////////////////////
    477 // createRamp
    478 //
    479 //  creates a ramp from point (x1, y1) to (x2, y2).
    480 //  x1, y1, x2, y2: determine the position of the start and end of the ramp
    481 //  minHeight, maxHeight: determine the height levels of the start and end point
    482 //  width: determines the width of the ramp
    483 //  smoothLevel: determines the smooth level around the edges of the ramp
    484 //  mainTerrain: (Optional) determines the terrain texture for the ramp
    485 //  edgeTerrain: (Optional) determines the terrain texture for the edges
    486 //  tileclass: (Optional) adds the ramp to this tile class
    487 //
    488 ///////////////////////////////////////////////////////////////////////////////////////////
    489 
     461/**
     462 * createRamp
     463 *
     464 *  creates a ramp from point (x1, y1) to (x2, y2).
     465 *  x1, y1, x2, y2: determine the position of the start and end of the ramp
     466 *  minHeight, maxHeight: determine the height levels of the start and end point
     467 *  width: determines the width of the ramp
     468 *  smoothLevel: determines the smooth level around the edges of the ramp
     469 *  mainTerrain: (Optional) determines the terrain texture for the ramp
     470 *  edgeTerrain: (Optional) determines the terrain texture for the edges
     471 *  tileclass: (Optional) adds the ramp to this tile class
     472 */
    490473function createRamp (x1, y1, x2, y2, minHeight, maxHeight, width, smoothLevel, mainTerrain, edgeTerrain, tileclass)
    491474{
    492475    var halfWidth = width / 2;
     
    540523    }
    541524}
    542525
    543 /////////////////////////////////////////////////////////////////////////////////////////
    544 // createMountain
    545 //
    546 //  creates a mountain using a tecnique very similar to chain placer.
    547 //
    548 ///////////////////////////////////////////////////////////////////////////////////////////
     526/**
     527 * createMountain
     528 *
     529 *  creates a mountain using a tecnique very similar to chain placer.
     530 *
     531 */
    549532
    550533function createMountain(maxHeight, minRadius, maxRadius, numCircles, constraint, x, z, terrain, tileclass, fcc, q)
    551534{
     
    617600       
    618601        for (var ix = sx; ix <= lx; ++ix)
    619602        {
    620             for (var iz = sz; iz <= lz; ++ iz)
     603            for (var iz = sz; iz <= lz; ++iz)
    621604            {
    622605                dx = ix - cx;
    623606                dz = iz - cz;
     
    666649       
    667650        for (var ix = sx; ix <= lx; ++ix)
    668651        {
    669             for (var iz = sz; iz <= lz; ++ iz)
     652            for (var iz = sz; iz <= lz; ++iz)
    670653            {
    671654                if (fcc)
    672655                    if ((x - ix) > fcc || (ix - x) > fcc || (z - iz) > fcc || (iz - z) > fcc)
     
    737720       
    738721        for (var ix = sx; ix <= lx; ++ix)
    739722        {
    740             for (var iz = sz; iz <= lz; ++ iz)
     723            for (var iz = sz; iz <= lz; ++iz)
    741724            {
    742725                dx = ix - cx;
    743726                dz = iz - cz;
  • binaries/data/mods/public/maps/random/schwarzwald.js

     
    1 // Created by Niek ten Brinke (aka niektb)
    2 // Based on FeXoR's Daimond Square Algorithm for heightmap generation and several official random maps
     1/**
     2 * Created by Niek ten Brinke (aka niektb)
     3 * Based on FeXoR's Daimond Square Algorithm for heightmap generation and several official random maps
     4 */
    35
    46'use strict';
    57
    68RMS.LoadLibrary('rmgen');
    79
    8 // initialize map
     10/**
     11 * initialize map
     12 */
    913
    1014log('Initializing map...');
    1115
    1216InitMap();
    1317
    1418
    15 ////////////////
    16 //
    17 //  Initializing
    18 //
    19 ////////////////
     19/**
     20 *  Initializing
     21 */
    2022
    21 //sky
     23/**
     24 *sky
     25 */
    2226setSkySet("fog");
    2327setFogFactor(0.35);
    2428setFogThickness(0.19);
    25 
    26 // water
     29/**
     30 * water
     31 */
    2732setWaterColor(0.501961, 0.501961, 0.501961);
    2833setWaterTint(0.25098, 0.501961, 0.501961);
    2934setWaterWaviness(0.5);
    3035setWaterType("clap");
    3136setWaterMurkiness(0.75);
    32 
    33 // post processing
     37/**
     38 * post processing
     39 */
    3440setPPSaturation(0.37);
    3541setPPContrast(0.4);
    3642setPPBrightness(0.4);
    3743setPPEffect("hdr");
    3844setPPBloom(0.4);
    39 
    40 // Setup tile classes
     45/**
     46 * Setup tile classes
     47 */
    4148var clPlayer = createTileClass();
    4249var clPath = createTileClass();
    4350var clHill = createTileClass();
     
    4754var clFood = createTileClass();
    4855var clBaseResource = createTileClass();
    4956var clOpen = createTileClass();
    50 
    51 // Setup Templates
     57/**
     58 * Setup Templates
     59 */
    5260var templateStone = 'gaia/geology_stone_alpine_a';
    5361var templateStoneMine = 'gaia/geology_stonemine_alpine_quarry';
    5462var templateMetal = 'actor|geology/stone_granite_med.xml';
     
    6472var aReeds = 'actor|props/flora/reeds_pond_lush_b.xml';
    6573var oFish = "gaia/fauna_fish";
    6674
    67 
    68 // Setup terrain
     75/**
     76 * Setup terrain
     77 */
    6978var terrainWood = ['alpine_forrestfloor|gaia/flora_tree_oak', 'alpine_forrestfloor|gaia/flora_tree_pine'];
    7079var terrainWoodBorder = ['new_alpine_grass_mossy|gaia/flora_tree_oak', 'alpine_forrestfloor|gaia/flora_tree_pine',
    7180    'temp_grass_long|gaia/flora_bush_temperate', 'temp_grass_clovers|gaia/flora_bush_berry', 'temp_grass_clovers_2|gaia/flora_bush_grapes',
     
    108117
    109118const BUILDING_ANGlE = -PI/4;
    110119
    111 // Setup map
     120/**
     121 * Setup map
     122 */
    112123var mapSize = getMapSize();
    113124var mapRadius = mapSize/2;
    114125var playableMapRadius = mapRadius - 5;
    115126var mapCenterX = mapRadius;
    116127var mapCenterZ = mapRadius;
    117128
    118 // Setup players and bases
     129/**
     130 * Setup players and bases
     131 */
    119132var numPlayers = getNumPlayers();
    120133var baseRadius = 15;
    121134var minPlayerRadius = min(mapRadius-1.5*baseRadius, 5*mapRadius/8);
     
    128141var playerAngleAddAvrg = 2*PI / numPlayers;
    129142var playerAngleMaxOff = playerAngleAddAvrg/4;
    130143
    131 // Setup paths
     144/**
     145 * Setup paths
     146 */
    132147var pathSucsessRadius = baseRadius/2;
    133148var pathAngleOff = PI/2;
    134 var pathWidth = 10; // This is not really the path's thickness in tiles but the number of tiles in the clumbs of the path
     149var pathWidth = 10; // This is not really the path's thickness in tiles but the number of tiles in the clumps of the path
    135150
    136 // Setup additional resources
     151/**
     152 * Setup additional resources
     153 */
    137154var resourceRadius = 2*mapRadius/3; // 3*mapRadius/8;
    138155//var resourcePerPlayer = [templateStone, templateMetalMine];
    139156
    140 // Setup woods
    141 // For large maps there are memory errors with too many trees.  A density of 256*192/mapArea works with 0 players.
    142 // Around each player there is an area without trees so with more players the max density can increase a bit.
     157/**
     158 * Setup woods
     159 * For large maps there are memory errors with too many trees.  A density of 256*192/mapArea works with 0 players.
     160 * Around each player there is an area without trees so with more players the max density can increase a bit.
     161 */
    143162var maxTreeDensity = min(256 * (192 + 8 * numPlayers) / (mapSize * mapSize), 1); // Has to be tweeked but works ok
    144163var bushChance = 1/3; // 1 means 50% chance in deepest wood, 0.5 means 25% chance in deepest wood
    145164
    146165
    147 ////////////////
    148 //
    149 //  Some general functions
    150 //
    151 ////////////////
    152 
     166/**
     167 * Some general functions
     168 */
    153169function HeightPlacer(lowerBound, upperBound) {
    154170    this.lowerBound = lowerBound;
    155171    this.upperBound = upperBound;
     
    159175    constraint = (constraint || new NullConstraint());
    160176   
    161177    var ret = [];
    162     for (var x = 0; x < g_Map.size; x++) {
    163         for (var y = 0; y < g_Map.size; y++) {
     178    for (var x = 0; x < g_Map.size; ++x) {
     179        for (var y = 0; y < g_Map.size; ++y) {
    164180            if (g_Map.height[x][y] >= this.lowerBound && g_Map.height[x][y] <= this.upperBound && constraint.allows(x, y)) {
    165181                ret.push(new PointXZ(x, y));
    166182            }
     
    169185    return ret;
    170186};
    171187
    172 /*
    173 Takes an array of 2D points (arrays of length 2)
    174 Returns the order to go through the points for the shortest closed path (array of indices)
    175 */
     188/**
     189 * Takes an array of 2D points (arrays of length 2)
     190 * Returns the order to go through the points for the shortest closed path (array of indices)
     191 */
    176192function getOrderOfPointsForShortestClosePath(points)
    177193{
    178194    var order = [];
    179195    var distances = [];
    180196    if (points.length <= 3)
    181197    {
    182         for (var i = 0; i < points.length; i++)
     198        for (var i = 0; i < points.length; ++i)
    183199            order.push(i);
    184200       
    185201        return order;
     
    187203   
    188204    // Just add the first 3 points
    189205    var pointsToAdd = deepcopy(points);
    190     for (var i = 0; i < min(points.length, 3); i++)
     206    for (var i = 0; i < min(points.length, 3); i)
    191207    {
    192208        order.push(i);
    193209        pointsToAdd.shift(i);
     
    198214   
    199215    // Add remaining points so the path lengthens the least
    200216    var numPointsToAdd = pointsToAdd.length;
    201     for (var i = 0; i < numPointsToAdd; i++)
     217    for (var i = 0; i < numPointsToAdd; ++i)
    202218    {
    203219        var indexToAddTo = undefined;
    204220        var minEnlengthen = Infinity;
    205221        var minDist1 = 0;
    206222        var minDist2 = 0;
    207         for (var k = 0; k < order.length; k++)
     223        for (var k = 0; k < order.length; ++k)
    208224        {
    209225            var dist1 = getDistance(pointsToAdd[0][0], pointsToAdd[0][1], points[order[k]][0], points[order[k]][1]);
    210226            var dist2 = getDistance(pointsToAdd[0][0], pointsToAdd[0][1], points[order[(k + 1) % order.length]][0], points[order[(k + 1) % order.length]][1]);
     
    226242}
    227243
    228244
    229 ////////////////
    230 //
    231 //  Heightmap functionality
    232 //
    233 ////////////////
     245/**
     246 *  Heightmap functionality
     247 */
    234248
    235 // Some heightmap constants
    236 const MIN_HEIGHT = - SEA_LEVEL; // -20
    237 const MAX_HEIGHT = 0xFFFF/HEIGHT_UNITS_PER_METRE - SEA_LEVEL; // A bit smaller than 90
    238 
    239 // Get the diferrence between minimum and maxumum height
     249/**
     250 * Get the diferrence between minimum and maxumum height
     251 */
    240252function getMinAndMaxHeight(reliefmap)
    241253{
    242254    var height = {};
    243255    height.min = Infinity;
    244256    height.max = - Infinity;
    245     for (var x = 0; x < reliefmap.length; x++)
     257    for (var x = 0; x < reliefmap.length; ++x)
    246258    {
    247         for (var y = 0; y < reliefmap[x].length; y++)
     259        for (var y = 0; y < reliefmap[x].length; ++y)
    248260        {
    249261            if (reliefmap[x][y] < height.min)
    250262                height.min = reliefmap[x][y];
     
    264276    var oldHeightRange = getMinAndMaxHeight(heightmap);
    265277    var max_x = heightmap.length;
    266278    var max_y = heightmap[0].length;
    267     for (var x = 0; x < max_x; x++)
    268         for (var y = 0; y < max_y; y++)
     279    for (var x = 0; x < max_x; ++x)
     280        for (var y = 0; y < max_y; ++y)
    269281            heightmap[x][y] = minHeight + (heightmap[x][y] - oldHeightRange.min) / (oldHeightRange.max - oldHeightRange.min) * (maxHeight - minHeight);
    270282}
    271283
    272 /*
    273 getStartLocationsByHeightmap
    274 Takes
    275     hightRange      An associative array with keys 'min' and 'max' each containing a float (the height range start locations are allowed)
    276     heightmap       Optional, default is g_Map.height, an array of (map width) arrays of (map depth) floats
    277     maxTries        Optional, default is 1000, an integer, how often random player distributions are rolled to be compared
    278     minDistToBorder Optional, default is 20, an integer, how far start locations have to be
    279     numberOfPlayers Optional, default is getNumPlayers, an integer, how many start locations should be placed
    280 Returns
    281     An array of 2D points (arrays of length 2)
    282 */
     284/**
     285 * getStartLocationsByHeightmap
     286 * Takes
     287 *  hightRange      An associative array with keys 'min' and 'max' each containing a float (the height range start locations are allowed)
     288 *  heightmap       Optional, default is g_Map.height, an array of (map width) arrays of (map depth) floats
     289 *  maxTries        Optional, default is 1000, an integer, how often random player distributions are rolled to be compared
     290 *  minDistToBorder Optional, default is 20, an integer, how far start locations have to be
     291 *  numberOfPlayers Optional, default is getNumPlayers, an integer, how many start locations should be placed
     292 * Returns
     293 *  An array of 2D points (arrays of length 2)
     294 */
    283295function getStartLocationsByHeightmap(hightRange, maxTries, minDistToBorder, numberOfPlayers, heightmap)
    284296{
    285297    maxTries = (maxTries || 1000);
     
    288300    heightmap = (heightmap || g_Map.height);
    289301   
    290302    var validStartLocTiles = [];
    291     for (var x = minDistToBorder; x < heightmap.length - minDistToBorder; x++)
    292         for (var y = minDistToBorder; y < heightmap[0].length - minDistToBorder; y++)
     303    for (var x = minDistToBorder; x < heightmap.length - minDistToBorder; ++x)
     304        for (var y = minDistToBorder; y < heightmap[0].length - minDistToBorder; ++y)
    293305            if (heightmap[x][y] > hightRange.min && heightmap[x][y] < hightRange.max) // Has the right hight
    294306                validStartLocTiles.push([x, y]);
    295307   
    296308    var maxMinDist = 0;
    297     for (var tries = 0; tries < maxTries; tries++)
     309    for (var tries = 0; tries < maxTries; ++tries)
    298310    {
    299311        var startLoc = [];
    300312        var minDist = heightmap.length;
    301         for (var p = 0; p < numberOfPlayers; p++)
     313        for (var p = 0; p < numberOfPlayers; ++p)
    302314            startLoc.push(validStartLocTiles[randInt(validStartLocTiles.length)]);
    303         for (var p1 = 0; p1 < numberOfPlayers - 1; p1++)
     315        for (var p1 = 0; p1 < numberOfPlayers - 1; ++p1)
    304316        {
    305             for (var p2 = p1 + 1; p2 < numberOfPlayers; p2++)
     317            for (var p2 = p1 + 1; p2 < numberOfPlayers; ++p2)
    306318            {
    307319                var dist = getDistance(startLoc[p1][0], startLoc[p1][1], startLoc[p2][0], startLoc[p2][1]);
    308320                if (dist < minDist)
     
    319331    return finalStartLoc;
    320332}
    321333
    322 /*
    323 derivateEntitiesByHeight
    324 Takes
    325     hightRange      An associative array with keys 'min' and 'max' each containing a float (the height range start locations are allowed)
    326     startLoc        An array of 2D points (arrays of length 2)
    327     heightmap       Optional, default is g_Map.height, an array of (map width) arrays of (map depth) floats
    328     entityList      Array of entities/actors (strings to be placed with placeObject())
    329     maxTries        Optional, default is 1000, an integer, how often random player distributions are rolled to be compared
    330     minDistance     Optional, default is 30, an integer, how far start locations have to be away from start locations and the map border
    331 Returns
    332     An array of 2D points (arrays of length 2)
    333 */
     334/**
     335 *derivateEntitiesByHeight
     336 *Takes
     337 *  hightRange      An associative array with keys 'min' and 'max' each containing a float (the height range start locations are allowed)
     338 *  startLoc        An array of 2D points (arrays of length 2)
     339 *  heightmap       Optional, default is g_Map.height, an array of (map width) arrays of (map depth) floats
     340 *  entityList      Array of entities/actors (strings to be placed with placeObject())
     341 *  maxTries        Optional, default is 1000, an integer, how often random player distributions are rolled to be compared
     342 *  minDistance     Optional, default is 30, an integer, how far start locations have to be away from start locations and the map border
     343 *Returns
     344 *  An array of 2D points (arrays of length 2)
     345 */
    334346function derivateEntitiesByHeight(hightRange, startLoc, entityList, maxTries, minDistance, heightmap)
    335347{
    336348    entityList = (entityList || [templateMetalMine, templateStoneMine]);
     
    340352   
    341353    var placements = deepcopy(startLoc);
    342354    var validTiles = [];
    343     for (var x = minDistance; x < heightmap.length - minDistance; x++)
    344         for (var y = minDistance; y < heightmap[0].length - minDistance; y++)
     355    for (var x = minDistance; x < heightmap.length - minDistance; ++x)
     356        for (var y = minDistance; y < heightmap[0].length - minDistance; ++y)
    345357            if (heightmap[x][y] > hightRange.min && heightmap[x][y] < hightRange.max) // Has the right hight
    346358                validTiles.push([x, y]);
    347359   
    348360    if (!validTiles.length)
    349361        return;
    350362   
    351     for (var tries = 0; tries < maxTries; tries++)
     363    for (var tries = 0; tries < maxTries; ++tries)
    352364    {
    353365        var tile = validTiles[randInt(validTiles.length)];
    354366        var isValid = true;
    355         for (var p = 0; p < placements.length; p++)
     367        for (var p = 0; p < placements.length; ++p)
    356368        {
    357369            if (getDistance(placements[p][0], placements[p][1], tile[0], tile[1]) < minDistance)
    358370            {
     
    370382}
    371383
    372384
    373 ////////////////
    374 //
    375 //  Base terrain generation functionality
    376 //
    377 ////////////////
     385/**
     386 *  Base terrain generation functionality
     387 */
    378388
    379389function setBaseTerrainDiamondSquare(minHeight, maxHeight, smoothness, initialHeightmap, heightmap)
    380390{
     
    395405        var newHeightmap = [];
    396406        var oldWidth = initialHeightmap.length;
    397407        // Square
    398         for (var x = 0; x < 2 * oldWidth - 1; x++)
     408        for (var x = 0; x < 2 * oldWidth - 1; ++x)
    399409        {
    400410            newHeightmap.push([]);
    401             for (var y = 0; y < 2 * oldWidth - 1; y++)
     411            for (var y = 0; y < 2 * oldWidth - 1; ++y)
    402412            {
    403413                if (x % 2 === 0 && y % 2 === 0) // Old tile
    404414                    newHeightmap[x].push(initialHeightmap[x/2][y/2]);
     
    412422            }
    413423        }
    414424        // Diamond
    415         for (var x = 0; x < 2 * oldWidth - 1; x++)
     425        for (var x = 0; x < 2 * oldWidth - 1; ++x)
    416426        {
    417             for (var y = 0; y < 2 * oldWidth - 1; y++)
     427            for (var y = 0; y < 2 * oldWidth - 1; ++y)
    418428            {
    419429                if (newHeightmap[x][y] === undefined)
    420430                {
     
    452462   
    453463    // Cut initialHeightmap to fit target width
    454464    var shift = [floor((newHeightmap.length - heightmap.length) / 2), floor((newHeightmap[0].length - heightmap[0].length) / 2)];
    455     for (var x = 0; x < heightmap.length; x++)
    456         for (var y = 0; y < heightmap[0].length; y++)
     465    for (var x = 0; x < heightmap.length; ++x)
     466        for (var y = 0; y < heightmap[0].length; ++y)
    457467            heightmap[x][y] = newHeightmap[x][y];
    458468}
    459469
    460470
    461 ////////////////
    462 //
    463 //  Terrain erosion functionality
    464 //
    465 ////////////////
    466 
     471/**
     472 *  Terrain erosion functionality
     473 */
    467474function decayErrodeHeightmap(strength, heightmap)
    468475{
    469476    strength = (strength || 0.9); // 0 to 1
     
    474481    var map = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]; // smoother
    475482    var max_x = heightmap.length;
    476483    var max_y = heightmap[0].length;
    477     for (var x = 0; x < max_x; x++)
    478         for (var y = 0; y < max_y; y++)
    479             for (var i = 0; i < map.length; i++)
     484    for (var x = 0; x < max_x; ++x)
     485        for (var y = 0; y < max_y; ++y)
     486            for (var i = 0; i < map.length; ++i)
    480487                heightmap[x][y] += strength / map.length * (referenceHeightmap[(x + map[i][0] + max_x) % max_x][(y + map[i][1] + max_y) % max_y] - referenceHeightmap[x][y]); // Not entirely sure if scaling with map.length is perfect but tested values seam to indicate it is
    481488}
    482489
     
    490497    heightmap = (heightmap || g_Map.height);
    491498   
    492499    var heightmapWin = [];
    493     for (var wx = 0; wx < 2 * dx + 1; wx++)
     500    for (var wx = 0; wx < 2 * dx + 1; ++wx)
    494501    {
    495502        heightmapWin.push([]);
    496         for (var wy = 0; wy < 2 * dy + 1; wy++)
     503        for (var wy = 0; wy < 2 * dy + 1; ++wy)
    497504        {
    498505            var actualX = x - dx + wx;
    499506            var actualY = y - dy + wy;
     
    503510                heightmapWin[wx].push(targetHeight);
    504511        }
    505512    }
    506     for (var wx = 0; wx < 2 * dx + 1; wx++)
     513    for (var wx = 0; wx < 2 * dx + 1; ++wx)
    507514    {
    508         for (var wy = 0; wy < 2 * dy + 1; wy++)
     515        for (var wy = 0; wy < 2 * dy + 1; ++wy)
    509516        {
    510517            var actualX = x - dx + wx;
    511518            var actualY = y - dy + wy;
     
    522529}
    523530
    524531
    525 ////////////////
    526 //
    527 //  Actually do stuff
    528 //
    529 ////////////////
     532/**
     533 *  Actually do stuff
     534 */
    530535
    531 ////////////////
    532 // Set height limits and water level by map size
    533 ////////////////
     536/**
     537 * Set height limits and water level by map size
     538 */
    534539
    535 // Set target min and max height depending on map size to make average steepness about the same on all map sizes
     540/**
     541 * Set target min and max height depending on map size to make average steepness about the same on all map sizes
     542 */
    536543var heightRange = {'min': MIN_HEIGHT * (g_Map.size + 512) / 8192, 'max': MAX_HEIGHT * (g_Map.size + 512) / 8192, 'avg': (MIN_HEIGHT * (g_Map.size + 512) +MAX_HEIGHT * (g_Map.size + 512))/16384};
    537544
    538 // Set average water coverage
     545/**
     546 * Set average water coverage
     547 */
    539548var averageWaterCoverage = 1/5; // NOTE: Since erosion is not predictable actual water coverage might vary much with the same values
    540549var waterHeight = -MIN_HEIGHT + heightRange.min + averageWaterCoverage * (heightRange.max - heightRange.min);
    541550var waterHeightAdjusted = waterHeight + MIN_HEIGHT;
    542551setWaterHeight(waterHeight);
    543552
    544 ////////////////
    545 // Generate base terrain
    546 ////////////////
     553/**
     554 * Generate base terrain
     555 */
    547556
    548 // Setting a 3x3 Grid as initial heightmap
     557/**
     558 * Setting a 3x3 Grid as initial heightmap
     559 */
    549560var initialReliefmap = [[heightRange.max, heightRange.max, heightRange.max], [heightRange.max, heightRange.min, heightRange.max], [heightRange.max, heightRange.max, heightRange.max]];
    550561
    551562setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, 0.5, initialReliefmap, g_Map.height);
    552 // Apply simple erosion
    553 for (var i = 0; i < 5; i++)
     563/**
     564 * Apply simple erosion
     565 */
     566for (var i = 0; i < 5; ++i)
    554567    decayErrodeHeightmap(0.5);
    555568rescaleHeightmap(heightRange.min, heightRange.max);
    556569
    557570RMS.SetProgress(50);
    558571
    559 //////////
    560 // Setup height limit
    561 //////////
     572/**
     573 * Setup height limit
     574 */
    562575
    563 // Height presets
     576/**
     577 * Height presets
     578 */
    564579var heighLimits = [
    565580    heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), // 0 Deep water
    566581    heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), // 1 Medium Water
     
    574589    waterHeightAdjusted + 7/8 * (heightRange.max - waterHeightAdjusted), // 9 Upper forest border
    575590    waterHeightAdjusted + (heightRange.max - waterHeightAdjusted)]; // 10 Hilltop
    576591
    577 //////////
    578 // Place start locations and apply terrain texture and decorative props
    579 //////////
     592/**
     593 * Place start locations and apply terrain texture and decorative props
     594 */
    580595
    581 // Get start locations
     596/**
     597 * Get start locations
     598 */
    582599var startLocations = getStartLocationsByHeightmap({'min': heighLimits[4], 'max': heighLimits[5]});
    583600var playerHeight = (heighLimits[4] + heighLimits[5]) / 2;
    584601
    585 for (var i=0; i < numPlayers; i++)
     602for (var i=0; i < numPlayers; ++i)
    586603{
    587604    playerAngle[i] = (playerAngleStart + i*playerAngleAddAvrg + randFloat(0, playerAngleMaxOff))%(2*PI);
    588605    var x = round(mapCenterX + randFloat(minPlayerRadius, maxPlayerRadius)*cos(playerAngle[i]));
     
    604621    var distToSL = 15;
    605622    var resStartAngle = playerAngle[i] + PI;
    606623    var resAddAngle = 2*PI / startingResources.length;
    607     for (var rIndex = 0; rIndex < startingResources.length; rIndex++)
     624    for (var rIndex = 0; rIndex < startingResources.length; ++rIndex)
    608625    {
    609626        var angleOff = randFloat(-resAddAngle/2, resAddAngle/2);
    610627        var placeX = x + distToSL*cos(resStartAngle + rIndex*resAddAngle + angleOff);
     
    614631    }
    615632}
    616633
    617 // Add further stone and metal mines
     634/**
     635 * Add further stone and metal mines
     636 */
    618637derivateEntitiesByHeight({'min': heighLimits[3], 'max': ((heighLimits[4]+heighLimits[3])/2)}, startLocations);
    619638derivateEntitiesByHeight({'min': ((heighLimits[5]+heighLimits[6])/2), 'max': heighLimits[7]}, startLocations);
    620639
    621640RMS.SetProgress(50);
    622641
    623 //place water & open terrain textures and assign TileClasses
     642/**
     643 * place water & open terrain textures and assign TileClasses
     644 */
    624645log("Painting textures...");
    625646var placer = new HeightPlacer(heighLimits[2], (heighLimits[3]+heighLimits[2])/2);
    626647var painter = new LayeredPainter([terrainBase, terrainBaseBorder], [5]);
     
    634655
    635656RMS.SetProgress(60);
    636657
    637 // Place paths
     658/**
     659 * Place paths
     660 */
    638661log("Placing paths...");
    639662var doublePaths = true;
    640663if (numPlayers > 4)
     
    644667    var maxI = numPlayers+1;
    645668else
    646669    var maxI = numPlayers;
    647 for (var i = 0; i < maxI; i++)
     670for (var i = 0; i < maxI; ++i)
    648671{
    649672    if (doublePaths === true)
    650673        var minJ = 0;
    651674    else
    652675        var minJ = i+1;
    653     for (var j = minJ; j < numPlayers+1; j++)
     676    for (var j = minJ; j < numPlayers+1; ++j)
    654677    {
    655678        // Setup start and target coordinates
    656679        if (i < numPlayers)
     
    700723            }
    701724            if (getDistance(x, z, targetX, targetZ) < pathSucsessRadius)
    702725                targetReached = true;
    703             tries++;
     726            ++tries;
    704727
    705728        }
    706729    }
     
    708731
    709732RMS.SetProgress(75);
    710733
    711 //create general decoration
     734/**
     735 *create general decoration
     736 */
    712737log("Creating decoration...");
    713738createDecoration
    714739(
     
    730755
    731756RMS.SetProgress(80);
    732757
    733 //create fish
     758/**
     759 *create fish
     760 */
    734761log("Growing fish...");
    735762createFood
    736763(
     
    745772
    746773RMS.SetProgress(85);
    747774
    748 // create reeds
     775/**
     776 * create reeds
     777 */
    749778log("Planting reeds...");
    750779var types = [aReeds];   // some variation
    751780for (var i = 0; i < types.length; ++i)
     
    759788
    760789RMS.SetProgress(90);
    761790
    762 // place trees
     791/**
     792 * place trees
     793 */
    763794log("Planting trees...");
    764 for (var x = 0; x < mapSize; x++)
     795for (var x = 0; x < mapSize; ++x)
    765796{
    766     for (var z = 0;z < mapSize;z++)
     797    for (var z = 0; z < mapSize; ++z)
    767798    {
    768799        // Some variables
    769800        var radius = Math.pow(Math.pow(mapCenterX - x - 0.5, 2) + Math.pow(mapCenterZ - z - 0.5, 2), 1/2); // The 0.5 is a correction for the entities placed on the center of tiles
    770801        var minDistToSL = mapSize;
    771         for (var i=0; i < numPlayers; i++)
     802        for (var i=0; i < numPlayers; ++i)
    772803            minDistToSL = min(minDistToSL, getDistance(playerStartLocX[i], playerStartLocZ[i], x, z));
    773804        // Woods tile based
    774805        var tDensFactSL = max(min((minDistToSL - baseRadius) / baseRadius, 1), 0);
     
    793824
    794825RMS.SetProgress(100);
    795826
    796 // Export map data
     827/**
     828 * Export map data
     829 */
    797830ExportMap();