Ticket #3764: realistic_terrain_demo2016-1-31c-noStyle.diff

File realistic_terrain_demo2016-1-31c-noStyle.diff, 61.7 KB (added by FeXoR, 8 years ago)

Stile-only sections removed

  • 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

     
    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);
     
    118128}
    119129
    120130
    121 //////////
    122 // Prepare for hightmap munipulation
    123 //////////
     131/**
     132 * Prepare for hightmap munipulation
     133 */
    124134
    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};
     135/**
     136 * Set target min and max height depending on map size to make average stepness the same on all map sizes
     137 */
     138var heightRange = { "min": MIN_HEIGHT * (mapSize + 256) / 8192, "max": MAX_HEIGHT * (mapSize + 256) / 8192 };
    127139
    128 // Set average water coverage
     140/**
     141 * Set average water coverage
     142 */
    129143var averageWaterCoverage = 1/3; // NOTE: Since errosion is not predictable actual water coverage might differ much with the same value
    130144if (mapSize < 200) // Sink the waterlevel on tiny maps to ensure enough space
    131145    averageWaterCoverage = 2/3 * averageWaterCoverage;
     
    134148setWaterHeight(waterHeight);
    135149
    136150
    137 //////////
    138 // Prepare terrain texture by height placement
    139 //////////
    140 
     151/**
     152 * Prepare terrain texture by height placement
     153 */
    141154var textueByHeight = [];
    142155
    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",
     156/**
     157 * Deep water
     158 */
     159textueByHeight.push({ "upperHeightLimit": heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), "terrain": "temp_sea_rocks" });
     160
     161/**
     162 * Medium water (with fish)
     163 */
     164var terrains = ["temp_sea_weed"];
     165terrains = terrains.concat(terrains, terrains, terrains, terrains);
     166terrains = terrains.concat(terrains, terrains, terrains, terrains);
     167terrains.push("temp_sea_weed|gaia/fauna_fish");
     168textueByHeight.push({ "upperHeightLimit": heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), "terrain": terrains });
     169
     170/**
     171 * Flat Water
     172 */
     173textueByHeight.push({ "upperHeightLimit": heightRange.min + 3/3 * (waterHeightAdjusted - heightRange.min), "terrain": "temp_mud_a" });
     174
     175/**
     176 * Water surroundings/bog (with stone/metal some rabits and bushes)
     177 */
     178var terrains = ["temp_plants_bog", "temp_plants_bog_aut", "temp_dirt_gravel_plants", "temp_grass_d"];
     179terrains = terrains.concat(terrains, terrains, terrains, terrains, terrains);
     180terrains = ["temp_plants_bog|gaia/flora_bush_temperate"].concat(terrains, terrains);
     181terrains = ["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);
     182terrains = ["temp_plants_bog_aut|gaia/flora_tree_dead"].concat(terrains, terrains);
     183textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 1/6 * (heightRange.max - waterHeightAdjusted), "terrain": terrains });
     184
     185/**
     186 * Juicy grass near bog
     187 */
     188textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 2/6 * (heightRange.max - waterHeightAdjusted),
     189    "terrain": ["temp_grass", "temp_grass_d", "temp_grass_long_b", "temp_grass_plants"] });
     190
     191/**
     192 * Medium level grass
     193 */
     194textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 3/6 * (heightRange.max - waterHeightAdjusted),
     195    "terrain": ["temp_grass", "temp_grass_b", "temp_grass_c", "temp_grass_mossy"] });
     196
     197/**
     198 * Long grass near forest border
     199 */
     200textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 4/6 * (heightRange.max - waterHeightAdjusted),
     201    "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"] });
     202
     203/**
     204 * Forest border (With wood/food plants/deer/rabits)
     205 */
     206var terrains = ["temp_grass_plants|gaia/flora_tree_euro_beech", "temp_grass_mossy|gaia/flora_tree_poplar", "temp_grass_mossy|gaia/flora_tree_poplar_lombardy",
    172207    "temp_grass_long|gaia/flora_bush_temperate", "temp_mud_plants|gaia/flora_bush_temperate", "temp_mud_plants|gaia/flora_bush_badlands",
    173208    "temp_grass_long|gaia/flora_tree_apple", "temp_grass_clovers|gaia/flora_bush_berry", "temp_grass_clovers_2|gaia/flora_bush_grapes",
    174209    "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),
     210var numTerreins = terrains.length;
     211for (var i = 0; i < numTerreins; ++i)
     212    terrains.push("temp_grass_plants");
     213textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 5/6 * (heightRange.max - waterHeightAdjusted), "terrain": terrains });
     214
     215/**
     216 * Impassable woods
     217 */
     218textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 6/6 * (heightRange.max - waterHeightAdjusted),
    181219    "terrain": ["temp_grass_mossy|gaia/flora_tree_oak", "temp_forestfloor_pine|gaia/flora_tree_pine",
    182220    "temp_grass_mossy|gaia/flora_tree_oak", "temp_forestfloor_pine|gaia/flora_tree_pine",
    183221    "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"]});
     222    "temp_dirt_gravel_plants|gaia/flora_tree_aleppo_pine", "temp_forestfloor_autumn|gaia/flora_tree_carob"] });
    185223var minTerrainDistToBorder = 3;
    186224
    187 // Time check 1
     225/**
     226 * Time check 1
     227 */
    188228timeArray.push(new Date().getTime());
    189229RMS.SetProgress(5);
    190230
    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
     231/**
     232 * START THE GIANT WHILE LOOP:
     233 * - Generate Heightmap
     234 * - Search valid start position tiles
     235 * - Choose a good start position derivation (largest distance between closest players)
     236 * - Restart the loop if start positions are invalid or two players are to close to each other
     237 */
    197238var goodStartPositionsFound = false;
    198239var minDistBetweenPlayers = 16 + mapSize / 16; // Don't set this higher than 25 for tiny maps! It will take forever with 8 players!
    199240var enoughTiles = false;
    200241var tries = 0;
    201242while (!goodStartPositionsFound)
    202243{
    203     tries++;
     244    ++tries;
    204245    log("Starting giant while loop try " + tries);
    205246    // Generate reliefmap
    206247    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)
     248    for (var i = 0; i < 50 + mapSize/4; ++i) // Cycles depend on mapsize (more cycles -> bigger structures)
    208249        myReliefmap = getHeightErrosionedReliefmap(myReliefmap, 1);
    209250    myReliefmap = getRescaledReliefmap(myReliefmap, heightRange.min, heightRange.max);
    210251    setReliefmap(myReliefmap);
     
    241282                if (isPossible)
    242283                {
    243284                    possibleStartPositions.push([x, y]);
    244                     // placeTerrain(x, y, "blue"); // For debug reasons. Plz don't remove. // Only works properly for 1 loop
    245285                }
    246286            }
    247287        }
     
    260300        if (distToCenter < maxDistToCenter)
    261301        {
    262302            possibleStartPositionsTemp.push(possibleStartPositions[i]);
    263             // placeTerrain(possibleStartPositions[i][0], possibleStartPositions[i][1], "purple"); // Only works properly for 1 loop
    264303        }
    265304    }
    266305    possibleStartPositions = deepcopy(possibleStartPositionsTemp);
     
    292331        if (numLowTiles > minNumLowTiles && numHighTiles > minNumHighTiles)
    293332        {
    294333            possibleStartPositionsTemp.push(possibleStartPositions[i]);
    295             // placeTerrain(possibleStartPositions[i][0], possibleStartPositions[i][1], "red"); // Only works properly for 1 loop
    296334        }
    297335    }
    298336    possibleStartPositions = deepcopy(possibleStartPositionsTemp);
  • 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* 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            continue;
     413       
     414        var minHeight = heightRange.min;
     415        for (var h = 0; h < heighLimits.length; ++h)
     416        {
     417            if (tchm[x][y] >= minHeight && tchm[x][y] <= heighLimits[h])
     418            {
     419                areas[h].push({ "x": x, "y": y });
     420                break;
     421            }
     422            else
     423                minHeight = heighLimits[h];
     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* Stop Timer
     479*/
     480log("Map generation time: " + (new Date().getTime() - genStartTime))
     481
     482/**
     483* Export map data
     484*/
     485ExportMap();
  • 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            placements.push(tile);
     236        }
     237    }
     238}
     239
     240/**
     241*   setBaseTerrainDiamondSquare
     242*
     243*   Description:
     244*       Sets the heightmap to a relatively realistic shape.
     245*   Takes:
     246*       minHeight           Optional, Float, Default is the absolute possible minimum height
     247*                           Minimum height that can occur in the resulting heightmap
     248*       maxHeight           Optional, Float, Default is the absolute possible maximum height
     249*                           Maximum height that can occur in the resulting heightmap
     250*       smoothness          Optional, Float between 0 (rough) to 1 (smooth), Default is 0.5
     251*                           Lower values (rough) will generate more local structures (but should be smoothed e.g. with erosion later)
     252*       initialHeightmap    Optional, Array of width Arrays of depth Floats (usually a small square heightmap e.g. 2x2)
     253*                           Sets some initial heights at the edges (square heightmap of size 2) or more more specifically with bigger initial heightmaps
     254*       heightmap           Optional, Array of width Arrays of depth Floats (a heightmap), Default is g_Map.height
     255*                           The heightmap that will be set by this function
     256*   Returns nothing
     257*   NOTES:
     258*       min/maxHeight will not necessarily be present in the heightmap
     259*       This function only supports rectangular heightmaps.
     260*           This means that e.g. the edges (given by initialHeightmap) will not be in the playable map area in circular maps.
     261*       The function doubles the size of the initial heightmap (if given, else a random 2x2 one) until it's big enough.
     262*           Then the extend is cut. So the impact and scale of the initial heightmap depends on its size and the target map size.
     263*/
     264function setBaseTerrainDiamondSquare(minHeight, maxHeight, smoothness, initialHeightmap, heightmap)
     265{
     266    // Make some arguments optional
     267    minHeight = (minHeight || MIN_HEIGHT);
     268    maxHeight = (maxHeight || MAX_HEIGHT);
     269    var heightRange = maxHeight - minHeight;
     270    if (heightRange <= 0)
     271        warn("setBaseTerrainDiamondSquare: heightRange < 0");
     272    smoothness = (smoothness || 0.5);
     273    initialHeightmap = (initialHeightmap || [[randFloat(minHeight / 2, maxHeight / 2), randFloat(minHeight / 2, maxHeight / 2)], [randFloat(minHeight / 2, maxHeight / 2), randFloat(minHeight / 2, maxHeight / 2)]]);
     274    heightmap = (heightmap || g_Map.height);
     275   
     276    var offset = heightRange / 2;
     277   
     278    // Double initialHeightmap width until target width is reached (diamond square method)
     279    while (initialHeightmap.length < heightmap.length)
     280    {
     281        var newHeightmap = [];
     282        var oldWidth = initialHeightmap.length;
     283        // Square
     284        for (var x = 0; x < 2 * oldWidth - 1; ++x)
     285        {
     286            newHeightmap.push([]);
     287            for (var y = 0; y < 2 * oldWidth - 1; ++y)
     288            {
     289                if (x % 2 == 0 && y % 2 == 0) // Old tile
     290                    newHeightmap[x].push(initialHeightmap[x/2][y/2]);
     291                else if (x % 2 == 1 && y % 2 == 1) // New tile with diagonal old tile neighbors
     292                {
     293                    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);
     294                    newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset)
     295                }
     296                else // New tile with straight old tile neighbors
     297                    newHeightmap[x].push(undefined); // Define later
     298            }
     299        }
     300        // Diamond
     301        for (var x = 0; x < 2 * oldWidth - 1; ++x)
     302        {
     303            for (var y = 0; y < 2 * oldWidth - 1; ++y)
     304            {
     305                if (newHeightmap[x][y] === undefined)
     306                {
     307                    if (x > 0 && x + 1 < newHeightmap.length - 1 && y > 0 && y + 1 < newHeightmap.length - 1) // Not a border tile
     308                    {
     309                        newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 4;
     310                        newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset)
     311                    }
     312                    else if (x < newHeightmap.length - 1 && y > 0 && y < newHeightmap.length - 1) // Left border
     313                    {
     314                        newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x][y-1]) / 3;
     315                        newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset)
     316                    }
     317                    else if (x > 0 && y > 0 && y < newHeightmap.length - 1) // Right border
     318                    {
     319                        newHeightmap[x][y] = (newHeightmap[x][y+1] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 3;
     320                        newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset)
     321                    }
     322                    else if (x > 0 && x < newHeightmap.length - 1 && y < newHeightmap.length - 1) // Bottom border
     323                    {
     324                        newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x-1][y]) / 3;
     325                        newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset)
     326                    }
     327                    else if (x > 0 && x < newHeightmap.length - 1 && y > 0) // Top border
     328                    {
     329                        newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 3;
     330                        newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset)
     331                    }
     332                }
     333            }
     334        }
     335        initialHeightmap = deepcopy(newHeightmap);
     336        offset /= Math.pow(2, smoothness);
     337    }
     338   
     339    // Cut initialHeightmap to fit target width
     340    var shift = [floor((newHeightmap.length - heightmap.length) / 2), floor((newHeightmap[0].length - heightmap[0].length) / 2)];
     341    for (var x = 0; x < heightmap.length; ++x)
     342        for (var y = 0; y < heightmap[0].length; ++y)
     343            heightmap[x][y] = newHeightmap[x][y];
     344}
     345
     346/**
     347*   globalSmoothHeightmap
     348*
     349*   Takes:
     350*       strength    Optional, Float between 0 (no effect) and 1 (2 appending tiles will be set to roughly the same height), default is 0.8
     351*                   How strong the smooth effect should be
     352*       heightmap   Optional, Array of width Arrays of depth Floats (a heightmap), Default is g_Map.height
     353*                   The heightmap to be smoothed
     354*   Returns nothing
     355*   NOTES:
     356*       Strength values >0.9 might result in interference.
     357*       To strengthen the smooth effect it's better to apply it multiple times than using high strength values.
     358*/
     359function globalSmoothHeightmap(strength, heightmap)
     360{
     361    strength = (strength || 0.8); // 0 to 1
     362    heightmap = (heightmap || g_Map.height);
     363   
     364    var referenceHeightmap = deepcopy(heightmap);
     365    // var map = [[1, 0], [0, 1], [-1, 0], [0, -1]]; // faster
     366    var map = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]; // smoother
     367    var max_x = heightmap.length;
     368    var max_y = heightmap[0].length;
     369    for (var x = 0; x < max_x; ++x)
     370    {
     371        for (var y = 0; y < max_y; ++y)
     372        {
     373            for (var i = 0; i < map.length; ++i)
     374            {
     375                var mapX = x + map[i][0];
     376                var mapY = y + map[i][1];
     377                if (mapX >= 0 && mapX < max_x && mapY >= 0 && mapY < max_y)
     378                    heightmap[x][y] += strength / map.length * (referenceHeightmap[mapX][mapY] - referenceHeightmap[x][y]);
     379            }
     380        }
     381    }
     382}
     383
     384/**
     385*   rectangularSmoothToHeight
     386*
     387*   Description:
     388*       Pushes a rectangular area towards a given height smoothing it into the original terrain
     389*   Takes:
     390*       center          Associative Array with keys x and y containing Floats (the x/y coordinates of the center)
     391*       dx/dy           Float, Distance from the center in x/y direction the rectangle ends (half width/depth)
     392*       targetHeight    Float, Height the center of the rectangle will be pushed to
     393*       strength        Float between 0 (no change) to 1 (center set to target height), how strong the height is pushed
     394*       heightmap       Optional, Array of width Arrays of depth Floats (a heightmap), Default is g_Map.height
     395*                       The heightmap to be manipulated
     396*   Returns nothing
     397*   NOTES:
     398*       The window function to determine the smooth is not exactly a gaussian to ensure smooth edges.
     399*       The optimal function is not clear so several working ones are left in the function (documented out)
     400*/
     401function rectangularSmoothToHeight(center, dx, dy, targetHeight, strength, heightmap)
     402{
     403    var x = round(center.x);
     404    var y = round(center.y);
     405    dx = round(dx);
     406    dy = round(dy);
     407    strength = (strength || 1);
     408    heightmap = (heightmap || g_Map.height);
     409   
     410    var heightmapWin = [];
     411    for (var wx = 0; wx < 2 * dx + 1; ++wx)
     412    {
     413        heightmapWin.push([]);
     414        for (var wy = 0; wy < 2 * dy + 1; ++wy)
     415        {
     416            var actualX = x - dx + wx;
     417            var actualY = y - dy + wy;
     418            if (actualX >= 0 && actualX < heightmap.length - 1 && actualY >= 0 && actualY < heightmap[0].length - 1) // Is in map
     419                heightmapWin[wx].push(heightmap[actualX][actualY]);
     420            else
     421                heightmapWin[wx].push(targetHeight);
     422        }
     423    }
     424    for (var wx = 0; wx < 2 * dx + 1; ++wx)
     425    {
     426        for (var wy = 0; wy < 2 * dy + 1; ++wy)
     427        {
     428            var actualX = x - dx + wx;
     429            var actualY = y - dy + wy;
     430            if (actualX >= 0 && actualX < heightmap.length - 1 && actualY >= 0 && actualY < heightmap[0].length - 1) // Is in map
     431            {
     432                // Window function polynomial 2nd degree
     433                var scaleX = 1 - (wx / dx - 1) * (wx / dx - 1);
     434                var scaleY = 1 - (wy / dy - 1) * (wy / dy - 1);
     435               
     436                heightmap[actualX][actualY] = heightmapWin[wx][wy] + strength * scaleX * scaleY * (targetHeight - heightmapWin[wx][wy]);
     437            }
     438        }
     439    }
     440}
  • 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;
     
    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))
     
    642795{
    643796    return g_Map.getTexture(x, y);
    644797}
    645 
  • binaries/data/mods/public/maps/random/schwarzwald.js

     
    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];