LCOV - code coverage report
Current view: top level - maps/random/rmgen - library.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 19 106 17.9 %
Date: 2023-04-02 12:52:40 Functions: 2 38 5.3 %

          Line data    Source code
       1             : /**
       2             :  * A Centered Placer generates a shape (array of Vector2D points) around a variable center location satisfying a Constraint.
       3             :  * The center can be modified externally using setCenterPosition, typically called by createAreas.
       4             :  */
       5           6 : Engine.LoadLibrary("rmgen/placer/centered");
       6             : 
       7             : /**
       8             :  * A Non-Centered Placer generates a shape (array of Vector2D points) at a fixed location meeting a Constraint and
       9             :  * is typically called by createArea.
      10             :  * Since this type of Placer has no x and z property, its location cannot be randomized using createAreas.
      11             :  */
      12           6 : Engine.LoadLibrary("rmgen/placer/noncentered");
      13             : 
      14             : /**
      15             :  * A Painter modifies an arbitrary feature in a given Area, for instance terrain textures, elevation or calling other painters on that Area.
      16             :  * Typically the area is determined by a Placer called from createArea or createAreas.
      17             :  */
      18           6 : Engine.LoadLibrary("rmgen/painter");
      19             : 
      20           6 : const TERRAIN_SEPARATOR = "|";
      21           6 : const SEA_LEVEL = 20.0;
      22           6 : const HEIGHT_UNITS_PER_METRE = 92;
      23             : 
      24             : /**
      25             :  * Constants needed for heightmap_manipulation.js
      26             :  */
      27           6 : const MAX_HEIGHT_RANGE = 0xFFFF / HEIGHT_UNITS_PER_METRE; // Engine limit, Roughly 700 meters
      28           6 : const MIN_HEIGHT = - SEA_LEVEL;
      29             : 
      30           6 : const MAX_HEIGHT = MAX_HEIGHT_RANGE - SEA_LEVEL;
      31             : 
      32             : /**
      33             :  * Default angle for buildings.
      34             :  */
      35           6 : const BUILDING_ORIENTATION = -1/4 * Math.PI;
      36             : 
      37           6 : const g_CivData = deepfreeze(loadCivFiles(false));
      38             : 
      39           6 : const g_ActorPrefix = "actor|";
      40             : 
      41             : /**
      42             :  * Sets whether setHeight operates on the center of a tile or on the vertices.
      43             :  */
      44           6 : var TILE_CENTERED_HEIGHT_MAP = false;
      45             : 
      46             : function actorTemplate(templateName)
      47             : {
      48           0 :         return g_ActorPrefix + templateName + ".xml";
      49             : }
      50             : 
      51             : function getObstructionSize(templateName, margin = 0)
      52             : {
      53           0 :         let obstruction = Engine.GetTemplate(templateName).Obstruction;
      54             : 
      55             :         let obstructionSize =
      56           0 :                 obstruction.Static ?
      57             :                         new Vector2D(obstruction.Static["@depth"], obstruction.Static["@width"]) :
      58             :                 // Used for gates, should consider the position too
      59             :                 obstruction.Obstructions ?
      60             :                         new Vector2D(
      61           0 :                                 Object.keys(obstruction.Obstructions).reduce((depth, key) => Math.max(depth, +obstruction.Obstructions[key]["@depth"]), 0),
      62           0 :                                 Object.keys(obstruction.Obstructions).reduce((width, key) => width + +obstruction.Obstructions[key]["@width"], 0)) :
      63             :                         new Vector2D(0, 0);
      64             : 
      65           0 :         return obstructionSize.div(TERRAIN_TILE_SIZE).add(new Vector2D(2, 2).mult(margin));
      66             : }
      67             : 
      68             : function fractionToTiles(f)
      69             : {
      70           0 :         return g_MapSettings.Size * f;
      71             : }
      72             : 
      73             : function tilesToFraction(t)
      74             : {
      75           0 :         return t / g_MapSettings.Size;
      76             : }
      77             : 
      78             : function scaleByMapSize(min, max, minMapSize = 128, maxMapSize = 512)
      79             : {
      80           0 :         return min + (max - min) * (g_MapSettings.Size - minMapSize) / (maxMapSize - minMapSize);
      81             : }
      82             : 
      83             : /**
      84             :  * Interpolate quadratic between (min, minMapArea) and (max, maxMapArea) with respect to the mapSize.
      85             :  * Default values set on the area of tiny and giant map sizes according to the map shape (square or circular).
      86             :  */
      87             : function scaleByMapArea(min, max, minMapArea = g_Map.getArea(128), maxMapArea = g_Map.getArea(256))
      88             : {
      89           0 :         return min + (max - min) * (g_Map.getArea() - minMapArea) / (maxMapArea - minMapArea);
      90             : }
      91             : 
      92             : /**
      93             :  * Interpolate quadraticly between (0,0) and (base, baseArea) with respect to the map size.
      94             :  * @param base - Value we should attain at baseArea.
      95             :  * @param disallowedArea - Area deducted from the map area.
      96             :  * @param baseArea - Area at which the base value should be attained. Defaults to the map area of a tiny map of current shape (square or circular).
      97             :  */
      98             : function scaleByMapAreaAbsolute(base, disallowedArea = 0, baseArea = g_Map.getArea(128))
      99             : {
     100           0 :         return scaleByMapArea(0, base, disallowedArea, baseArea + disallowedArea);
     101             : }
     102             : 
     103             : function randomPositionOnTile(tilePosition)
     104             : {
     105           0 :         return Vector2D.add(tilePosition, new Vector2D(randFloat(0, 1), randFloat(0, 1)));
     106             : }
     107             : 
     108             : /**
     109             :  * Retries the given function with those arguments as often as specified.
     110             :  */
     111             : function retryPlacing(placeFunc, retryFactor, amount, behaveDeprecated = false)
     112             : {
     113           0 :         let maxFail = amount * retryFactor;
     114             : 
     115           0 :         let results = [];
     116           0 :         let bad = 0;
     117             : 
     118           0 :         while (results.length < amount && bad <= maxFail)
     119             :         {
     120           0 :                 let result = placeFunc();
     121             : 
     122           0 :                 if (result !== undefined || behaveDeprecated)
     123           0 :                         results.push(result);
     124             :                 else
     125           0 :                         ++bad;
     126             :         }
     127             : 
     128           0 :         return results;
     129             : }
     130             : 
     131             : // TODO this is a hack to simulate the old behaviour of those functions
     132             : // until all old maps are changed to use the correct version of these functions
     133             : function createObjectGroupsDeprecated(group, player, constraints, amount, retryFactor = 10)
     134             : {
     135           0 :         return createObjectGroups(group, player, constraints, amount, retryFactor, true);
     136             : }
     137             : 
     138             : function createObjectGroupsByAreasDeprecated(group, player, constraints, amount, retryFactor, areas)
     139             : {
     140           0 :         return createObjectGroupsByAreas(group, player, constraints, amount, retryFactor, areas, true);
     141             : }
     142             : 
     143             : /**
     144             :  * Attempts to place the given number of areas in random places of the map.
     145             :  * Returns actually placed areas.
     146             :  */
     147             : function createAreas(centeredPlacer, painter, constraints, amount, retryFactor = 10)
     148             : {
     149           0 :         let placeFunc = function() {
     150           0 :                 centeredPlacer.setCenterPosition(g_Map.randomCoordinate(false));
     151           0 :                 return createArea(centeredPlacer, painter, constraints);
     152             :         };
     153             : 
     154           0 :         return retryPlacing(placeFunc, retryFactor, amount, false);
     155             : }
     156             : 
     157             : /**
     158             :  * Attempts to place the given number of areas in random places of the given areas.
     159             :  * Returns actually placed areas.
     160             :  */
     161             : function createAreasInAreas(centeredPlacer, painter, constraints, amount, retryFactor, areas)
     162             : {
     163           0 :         areas = areas.filter(area => area.getPoints().length);
     164           0 :         if (!areas.length) {
     165           0 :                 log("createAreasInAreas: 'areas' was either empty or only contained empty areas thus returning an empty array.\n" + new Error().stack)
     166           0 :                 return [];
     167             :         }
     168             :         
     169           0 :         let placeFunc = function() {
     170           0 :                 centeredPlacer.setCenterPosition(pickRandom(pickRandom(areas).getPoints()));
     171           0 :                 return createArea(centeredPlacer, painter, constraints);
     172             :         };
     173             : 
     174           0 :         return retryPlacing(placeFunc, retryFactor, amount, false);
     175             : }
     176             : 
     177             : /**
     178             :  * Attempts to place the given number of groups in random places of the map.
     179             :  * Returns the number of actually placed groups.
     180             :  */
     181             : function createObjectGroups(group, player, constraints, amount, retryFactor = 10, behaveDeprecated = false)
     182             : {
     183           0 :         let placeFunc = function() {
     184           0 :                 group.setCenterPosition(g_Map.randomCoordinate(true));
     185           0 :                 return createObjectGroup(group, player, constraints);
     186             :         };
     187             : 
     188           0 :         return retryPlacing(placeFunc, retryFactor, amount, behaveDeprecated);
     189             : }
     190             : 
     191             : /**
     192             :  * Attempts to place the given number of groups in random places of the given areas.
     193             :  * Returns the number of actually placed groups.
     194             :  */
     195             : function createObjectGroupsByAreas(group, player, constraints, amount, retryFactor, areas, behaveDeprecated = false)
     196             : {
     197           0 :         areas = areas.filter(area => area.getPoints().length);
     198           0 :         if (!areas.length) {
     199           0 :                 log("createObjectGroupsByAreas: 'areas' was either empty or only contained empty areas.\n" + new Error().stack)
     200           0 :                 return [];
     201             :         }
     202             :         
     203           0 :         let placeFunc = function() {
     204           0 :                 group.setCenterPosition(pickRandom(pickRandom(areas).getPoints()));
     205           0 :                 return createObjectGroup(group, player, constraints);
     206             :         };
     207             : 
     208           0 :         return retryPlacing(placeFunc, retryFactor, amount, behaveDeprecated);
     209             : }
     210             : 
     211             : function createTerrain(terrain)
     212             : {
     213           2 :         return typeof terrain == "string" ?
     214             :                 new SimpleTerrain(...terrain.split(TERRAIN_SEPARATOR)) :
     215           0 :                 new RandomTerrain(terrain.map(t => createTerrain(t)));
     216             : }
     217             : 
     218             : /**
     219             :  * Constructs a new Area shaped by the Placer meeting the Constraints and calls the Painters there.
     220             :  * Supports both Centered and Non-Centered Placers.
     221             :  */
     222             : function createArea(placer, painters, constraints)
     223             : {
     224          10 :         let points = placer.place(new AndConstraint(constraints));
     225          10 :         if (!points)
     226           0 :                 return undefined;
     227             : 
     228          10 :         let area = new Area(points);
     229             : 
     230          10 :         new MultiPainter(painters).paint(area);
     231             : 
     232          10 :         return area;
     233             : }
     234             : 
     235             : /**
     236             :  * @param mode is one of the HeightPlacer constants determining whether to exclude the min/max elevation.
     237             :  */
     238             : function paintTerrainBasedOnHeight(minHeight, maxHeight, mode, terrain)
     239             : {
     240           0 :         return createArea(
     241             :                 new HeightPlacer(mode, minHeight, maxHeight),
     242             :                 new TerrainPainter(terrain));
     243             : }
     244             : 
     245             : function paintTileClassBasedOnHeight(minHeight, maxHeight, mode, tileClass)
     246             : {
     247           0 :         return createArea(
     248             :                 new HeightPlacer(mode, minHeight, maxHeight),
     249             :                 new TileClassPainter(tileClass));
     250             : }
     251             : 
     252             : function unPaintTileClassBasedOnHeight(minHeight, maxHeight, mode, tileClass)
     253             : {
     254           0 :         return createArea(
     255             :                 new HeightPlacer(mode, minHeight, maxHeight),
     256             :                 new TileClassUnPainter(tileClass));
     257             : }
     258             : 
     259             : /**
     260             :  * Places the Entities of the given Group if they meet the Constraints
     261             :  * and sets the given player as the owner.
     262             :  */
     263             : function createObjectGroup(group, player, constraints)
     264             : {
     265           0 :         return group.place(player, new AndConstraint(constraints));
     266             : }
     267             : 
     268             : /**
     269             :  * Create an avoid constraint for the given classes by the given distances
     270             :  */
     271             : function avoidClasses(/*class1, dist1, class2, dist2, etc*/)
     272             : {
     273           0 :         let ar = [];
     274           0 :         for (let i = 0; i < arguments.length/2; ++i)
     275           0 :                 ar.push(new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1]));
     276             : 
     277             :         // Return single constraint
     278           0 :         if (ar.length == 1)
     279           0 :                 return ar[0];
     280             : 
     281           0 :         return new AndConstraint(ar);
     282             : }
     283             : 
     284             : /**
     285             :  * Create a stay constraint for the given classes by the given distances
     286             :  */
     287             : function stayClasses(/*class1, dist1, class2, dist2, etc*/)
     288             : {
     289           0 :         let ar = [];
     290           0 :         for (let i = 0; i < arguments.length/2; ++i)
     291           0 :                 ar.push(new StayInTileClassConstraint(arguments[2*i], arguments[2*i+1]));
     292             : 
     293             :         // Return single constraint
     294           0 :         if (ar.length == 1)
     295           0 :                 return ar[0];
     296             : 
     297           0 :         return new AndConstraint(ar);
     298             : }
     299             : 
     300             : /**
     301             :  * Create a border constraint for the given classes by the given distances
     302             :  */
     303             : function borderClasses(/*class1, idist1, odist1, class2, idist2, odist2, etc*/)
     304             : {
     305           0 :         let ar = [];
     306           0 :         for (let i = 0; i < arguments.length/3; ++i)
     307           0 :                 ar.push(new BorderTileClassConstraint(arguments[3*i], arguments[3*i+1], arguments[3*i+2]));
     308             : 
     309             :         // Return single constraint
     310           0 :         if (ar.length == 1)
     311           0 :                 return ar[0];
     312             : 
     313           0 :         return new AndConstraint(ar);
     314             : }
     315             : 
     316             : /**
     317             :  * Returns a subset of the given heightmap.
     318             :  */
     319             : function extractHeightmap(heightmap, topLeft, size)
     320             : {
     321           0 :         let result = [];
     322           0 :         for (let x = 0; x < size; ++x)
     323             :         {
     324           0 :                 result[x] = new Float32Array(size);
     325           0 :                 for (let y = 0; y < size; ++y)
     326           0 :                         result[x][y] = heightmap[x + topLeft.x][y + topLeft.y];
     327             :         }
     328           0 :         return result;
     329             : }
     330             : 
     331             : function convertHeightmap1Dto2D(heightmap)
     332             : {
     333           0 :         let result = [];
     334           0 :         let hmSize = Math.sqrt(heightmap.length);
     335           0 :         for (let x = 0; x < hmSize; ++x)
     336             :         {
     337           0 :                 result[x] = new Float32Array(hmSize);
     338           0 :                 for (let y = 0; y < hmSize; ++y)
     339           0 :                         result[x][y] = heightmap[y * hmSize + x];
     340             :         }
     341           0 :         return result;
     342             : }
     343             : 
     344             : function getDifficulties()
     345             : {
     346           0 :         return Engine.ReadJSONFile("simulation/data/settings/trigger_difficulties.json").Data;
     347             : }
     348             : 
     349             : /**
     350             :  * Returns the numeric difficulty level the player chose.
     351             :  */
     352             : function getDifficulty()
     353             : {
     354           0 :         let level = g_MapSettings.TriggerDifficulty || 3;
     355           0 :         return getDifficulties().find(difficulty => difficulty.Difficulty == level).Difficulty;
     356             : }

Generated by: LCOV version 1.14