LCOV - code coverage report
Current view: top level - maps/random/rmgen-common - gaia_entities.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 74 0.0 %
Date: 2023-04-02 12:52:40 Functions: 0 13 0.0 %

          Line data    Source code
       1             : /**
       2             :  * @file These functions are often used to place gaia entities, like forests, mines, animals or decorative bushes.
       3             :  */
       4             : 
       5             : /**
       6             :  * Returns the number of trees in forests and straggler trees.
       7             :  */
       8             : function getTreeCounts(minTrees, maxTrees, forestRatio)
       9             : {
      10           0 :         return [forestRatio, 1 - forestRatio].map(p => p * scaleByMapSize(minTrees, maxTrees));
      11             : }
      12             : 
      13             : /**
      14             :  * Places uniformly sized forests at random locations.
      15             :  * Unless you want a custom number of forest, prefer createDefaultForests.
      16             :  * Generates two variants of forests from the given terrain textures and tree templates.
      17             :  * The forest border has less trees than the inside.
      18             :  * @param terrainsSet - a list of 5 terrains to use. The first 3 are border terrains, the later 2 interior.
      19             :  * @param constraint - constraints to respect
      20             :  * @param tileClass - the tileclass to print
      21             :  * @param treeCount - Either { "nbForests": X, "treesPerForest": X } or (legacy) a number of trees.
      22             :  * @param retryFactor - @see createAreas
      23             :  */
      24             : function createForests(terrainSet, constraint, tileClass, treeCount, retryFactor)
      25             : {
      26           0 :         if (!treeCount)
      27           0 :                 return;
      28             : 
      29             :         // Construct different forest types from the terrain textures and template names.
      30           0 :         const [mainTerrain, terrainForestFloor1, terrainForestFloor2, terrainForestTree1, terrainForestTree2] = terrainSet;
      31             : 
      32             :         // The painter will pick a random Terrain for each part of the forest.
      33           0 :         const forestVariants = [
      34             :                 {
      35             :                         "borderTerrains": [terrainForestFloor2, mainTerrain, terrainForestTree1],
      36             :                         "interiorTerrains": [terrainForestFloor2, terrainForestTree1]
      37             :                 },
      38             :                 {
      39             :                         "borderTerrains": [terrainForestFloor1, mainTerrain, terrainForestTree2],
      40             :                         "interiorTerrains": [terrainForestFloor1, terrainForestTree2]
      41             :                 }
      42             :         ];
      43             : 
      44             :         let numberOfTrees;
      45             :         let numberOfForests;
      46           0 :         if (typeof treeCount === "number")
      47             :         {
      48           0 :                 numberOfTrees = treeCount;
      49           0 :                 numberOfForests = Math.floor(numberOfTrees / (scaleByMapSize(3, 6) * getNumPlayers() * forestVariants.length));
      50             :         }
      51             :         else
      52             :         {
      53           0 :                 numberOfForests = treeCount.nbForests;
      54           0 :                 numberOfTrees = numberOfForests * treeCount.treesPerForest;
      55             :         }
      56             : 
      57           0 :         if (!numberOfForests)
      58           0 :                 return;
      59             : 
      60           0 :         g_Map.log("Creating forests");
      61           0 :         for (const forestVariant of forestVariants)
      62           0 :                 createAreas(
      63             :                         new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), numberOfTrees / numberOfForests, 0.5),
      64             :                         [
      65             :                                 new LayeredPainter([forestVariant.borderTerrains, forestVariant.interiorTerrains], [2]),
      66             :                                 new TileClassPainter(tileClass)
      67             :                         ],
      68             :                         constraint,
      69             :                         numberOfForests,
      70             :                         retryFactor);
      71             : }
      72             : 
      73           0 : var g_DefaultNumberOfForests = scaleByMapSize(8, 36);
      74             : 
      75             : /**
      76             :  * Passes some sane defaults to createForests.
      77             :  */
      78             : function createDefaultForests(terrainSet, constraints, tileClass, totalNumberOfTrees)
      79             : {
      80           0 :         return createForests(
      81             :                 terrainSet,
      82             :                 constraints,
      83             :                 tileClass,
      84             :                 {
      85             :                         "nbForests": g_DefaultNumberOfForests,
      86             :                         "treesPerForest": totalNumberOfTrees / g_DefaultNumberOfForests,
      87             :                 }
      88             :         );
      89             : }
      90             : 
      91             : 
      92             : /**
      93             :  * Places the given amount of Entities at random places meeting the given Constraint, chosing a different template for each.
      94             :  */
      95             : function createStragglerTrees(templateNames, constraint, tileClass, treeCount, retryFactor)
      96             : {
      97           0 :         g_Map.log("Creating straggler trees");
      98           0 :         for (let templateName of templateNames)
      99           0 :                 createObjectGroupsDeprecated(
     100             :                         new SimpleGroup([new SimpleObject(templateName, 1, 1, 0, 3)], true, tileClass),
     101             :                         0,
     102             :                         constraint,
     103             :                         Math.floor(treeCount / templateNames.length),
     104             :                         retryFactor);
     105             : }
     106             : 
     107             : /**
     108             :  * Places a SimpleGroup consisting of the given number of the given Objects
     109             :  * at random locations that meet the given Constraint.
     110             :  */
     111             : function createMines(objects, constraint, tileClass, count)
     112             : {
     113           0 :         for (let object of objects)
     114           0 :                 createObjectGroupsDeprecated(
     115             :                         new SimpleGroup(object, true, tileClass),
     116             :                         0,
     117             :                         constraint,
     118             :                         count || scaleByMapSize(4, 16),
     119             :                         70);
     120             : }
     121             : 
     122             : /**
     123             :  * Place large/small mines on the map in such a way that it should be relatively fair.
     124             :  * @param oSmall - the small mine object
     125             :  * @param oLarge - the large mine object
     126             :  * @param clMine - the 'mine' class to paint.
     127             :  * @param constraints - Custom constraints. Note that the function automatically avoids clMine as well.
     128             :  * @param counts - a dict of numbers { "largeCount": 10, "smallCount": 100, "randomSmallCount": 2 }
     129             :  * @param randomness - randomize counts by a random multiplier between [1 - randomness, 1 + randomness]
     130             :  */
     131             : function createBalancedMines(oSmall, oLarge, clMine, constraints, counts, randomness)
     132             : {
     133           0 :         let largeCount = counts.largeCount;
     134           0 :         let smallCount = counts.smallCount;
     135           0 :         let randomSmallCount = counts.randomSmallCount;
     136             : 
     137           0 :         if (randomness > 0 && randomness < 1)
     138             :         {
     139           0 :                 largeCount = Math.round(largeCount * randFloat(1 - randomness, 1 + randomness));
     140           0 :                 smallCount = Math.round(smallCount * randFloat(1 - randomness, 1 + randomness));
     141           0 :                 randomSmallCount = Math.round(randomSmallCount * randFloat(1 - randomness, 1 + randomness));
     142             :         }
     143             : 
     144           0 :         const arrayConstraints = Array.isArray(constraints) ? constraints : [constraints];
     145             : 
     146             :         // Plop large mines far away from each other.
     147           0 :         createObjectGroups(
     148             :                 new SimpleGroup([new SimpleObject(oLarge, 1, 1, 0, 1)], true, clMine),
     149             :                 0,
     150             :                 new AndConstraint([avoidClasses(clMine, scaleByMapSize(25, 50)), ...arrayConstraints]),
     151             :                 largeCount,
     152             :                 100);
     153             : 
     154             :         // Plop smaller clusters of small mines, also somewhat farther away.
     155           0 :         createObjectGroups(
     156             :                 new SimpleGroup([new SimpleObject(oSmall, 2, 3, 0, 2)], true, clMine),
     157             :                 0,
     158             :                 new AndConstraint([avoidClasses(clMine, scaleByMapSize(18, 35)), ...arrayConstraints]),
     159             :                 smallCount,
     160             :                 50);
     161             : 
     162             :         // Plop a few smaller clusters in a random fashion, occasionally making very good dropsites spots.
     163           0 :         createObjectGroups(
     164             :                 new SimpleGroup([new SimpleObject(oSmall, 1, 2, 0, 2)], true, clMine),
     165             :                 0,
     166             :                 new AndConstraint([avoidClasses(clMine, 5), ...arrayConstraints]),
     167             :                 randomSmallCount,
     168             :                 50);
     169             : }
     170             : 
     171             : /**
     172             :  * Helper for createBalancedMines with default metal counts.
     173             :  * The current settings are so that a Small 1v1 has about 40K metal,
     174             :  * and a Normal 4v4 has about 140K.
     175             :  * The setup is biaised so that with fewer players, there are more small mines,
     176             :  * and with more players there are proportionally more big mines, to maintain
     177             :  * some randomness to the distribution but keep it somewhat fair in 1v1.
     178             :  */
     179             : function createBalancedMetalMines(oSmall, oLarge, clMine, constraints, counts = 1.0, randomness = 0.05)
     180             : {
     181           0 :         return createBalancedMines(
     182             :                 oSmall,
     183             :                 oLarge,
     184             :                 clMine,
     185             :                 constraints,
     186             :                 {
     187             :                         "largeCount": (Math.max(scaleByMapSize(1, 9), getNumPlayers() * 1.8 - 0.8)) * counts,
     188             :                         "smallCount": (scaleByMapSize(4, 12)) * counts,
     189             :                         "randomSmallCount": (scaleByMapSize(1, 8)) * counts,
     190             :                 },
     191             :                 randomness
     192             :         );
     193             : }
     194             : 
     195             : /**
     196             :  * Helper for createBalancedMines with default stone counts.
     197             :  * There is a little less stone than metal overall.
     198             :  */
     199             : function createBalancedStoneMines(oSmall, oLarge, clMine, constraints, counts = 1.0, randomness = 0.05)
     200             : {
     201           0 :         return createBalancedMines(
     202             :                 oSmall,
     203             :                 oLarge,
     204             :                 clMine,
     205             :                 constraints,
     206             :                 {
     207             :                         "largeCount": (Math.max(scaleByMapSize(1, 9), getNumPlayers() * 1.25)) * counts,
     208             :                         "smallCount": (scaleByMapSize(1, 8)) * counts,
     209             :                         "randomSmallCount": (scaleByMapSize(1, 8)) * counts,
     210             :                 },
     211             :                 randomness
     212             :         );
     213             : }
     214             : 
     215             : 
     216             : /**
     217             :  * Places Entities of the given templateName in a circular pattern (leaving out a quarter of the circle).
     218             :  */
     219             : function createStoneMineFormation(position, templateName, terrain, radius = 2.5, count = 8, startAngle = undefined, maxOffset = 1)
     220             : {
     221           0 :         createArea(
     222             :                 new ChainPlacer(radius / 2, radius, 2, Infinity, position, undefined, [5]),
     223             :                 new TerrainPainter(terrain));
     224             : 
     225           0 :         let angle = startAngle !== undefined ? startAngle : randomAngle();
     226             : 
     227           0 :         for (let i = 0; i < count; ++i)
     228             :         {
     229           0 :                 let pos = Vector2D.add(position, new Vector2D(radius + randFloat(0, maxOffset), 0).rotate(-angle)).round();
     230           0 :                 g_Map.placeEntityPassable(templateName, 0, pos, randomAngle());
     231           0 :                 angle += 3/2 * Math.PI / count;
     232             :         }
     233             : }
     234             : 
     235             : /**
     236             :  * Places the given amounts of the given Objects at random locations meeting the given Constraint.
     237             :  */
     238             : function createFood(objects, counts, constraint, tileClass)
     239             : {
     240           0 :         g_Map.log("Creating food");
     241           0 :         for (let i = 0; i < objects.length; ++i)
     242           0 :                 createObjectGroupsDeprecated(
     243             :                         new SimpleGroup(objects[i], true, tileClass),
     244             :                         0,
     245             :                         constraint,
     246             :                         counts[i],
     247             :                         50);
     248             : }
     249             : 
     250             : /**
     251             :  * Same as createFood, but doesn't mark the terrain with a TileClass.
     252             :  */
     253             : function createDecoration(objects, counts, constraint)
     254             : {
     255           0 :         g_Map.log("Creating decoration");
     256           0 :         for (let i = 0; i < objects.length; ++i)
     257           0 :                 createObjectGroupsDeprecated(
     258             :                         new SimpleGroup(objects[i], true),
     259             :                         0,
     260             :                         constraint,
     261             :                         counts[i],
     262             :                         5);
     263             : }
     264             : 
     265             : /**
     266             :  * Places docks in situations where the location of land and water is not known in advance.
     267             :  * Do determine the position, it picks a random point on the land, find the closest, significantly large body of water,
     268             :  * then places the dock at the first point close to that body of water within the given heightrange.
     269             :  *
     270             :  * @param {string} template - The template name of the dock to be placed.
     271             :  * @param {number} playerID - The owner of the dock.
     272             :  * @param {number} count - The number of docks to be placed.
     273             :  * @param {Object} tileClassWater - The tileclass the water area is marked with.
     274             :  * @param {Object} tileClassDock - The dock position is marked with this class.
     275             :  * @param {number} heightMin - The lowest height a dock could be placed.
     276             :  * @param {number} heightMax - The greatest height a dock could be placed.
     277             :  * @param {Array|Constraint} constraints - Only consider dock positions valid that meet this Constraint.
     278             :  * @param {number} offset - How many tiles to move the dock towards the direction of the water after having found a location.
     279             :  * @param {number} retryFactor- How many different locations should be tested.
     280             :  */
     281             : function placeDocks(template, playerID, count, tileClassWater, tileClassDock, heightMin, heightMax, constraints, offset, retryFactor)
     282             : {
     283           0 :         let mapCenter = g_Map.getCenter();
     284             : 
     285           0 :         g_Map.log("Marking dock search start area");
     286           0 :         let areaSearchStart = createArea(
     287             :                 new DiskPlacer(fractionToTiles(0.5) - 10, mapCenter),
     288             :                 undefined,
     289             :                 avoidClasses(tileClassWater, 6));
     290             : 
     291           0 :         g_Map.log("Marking dock search end area");
     292           0 :         let areaSearchEnd = createArea(
     293             :                 new DiskPlacer(fractionToTiles(0.5) - 10, mapCenter),
     294             :                 undefined,
     295             :                 stayClasses(tileClassWater, 20));
     296             : 
     297           0 :         g_Map.log("Marking land area");
     298           0 :         let areaLand = createArea(
     299             :                 new MapBoundsPlacer(),
     300             :                 undefined,
     301             :                 avoidClasses(tileClassWater, 0));
     302             : 
     303           0 :         g_Map.log("Marking water area");
     304           0 :         let areaWater = createArea(
     305             :                 new MapBoundsPlacer(),
     306             :                 undefined,
     307             :                 stayClasses(tileClassWater, 0));
     308             : 
     309           0 :         if (!areaSearchEnd || !areaSearchEnd.getPoints().length)
     310           0 :                 return;
     311             : 
     312             :         // TODO: computing the exact intersection with the waterplane would both not require us to pass reasonable heights and be more precise
     313           0 :         let constraint = new AndConstraint(constraints);
     314           0 :         g_Map.log("Placing docks");
     315           0 :         for (let i = 0; i < count; ++i)
     316           0 :                 for (let tries = 0; tries < retryFactor; ++tries)
     317             :                 {
     318           0 :                         let positionLand = pickRandom(areaSearchStart.getPoints());
     319           0 :                         let positionWaterLarge = areaSearchEnd.getClosestPointTo(positionLand);
     320           0 :                         let positionDock = findLocationInDirectionBasedOnHeight(positionWaterLarge, positionLand, heightMin, heightMax, offset);
     321           0 :                         if (!positionDock)
     322           0 :                                 continue;
     323             : 
     324           0 :                         positionDock.round();
     325             : 
     326           0 :                         if (!g_Map.inMapBounds(positionDock) || !constraint.allows(positionDock))
     327           0 :                                 continue;
     328             : 
     329           0 :                         let angle = positionDock.angleTo(Vector2D.average(new DiskPlacer(8, positionDock).place(stayClasses(tileClassWater, 0))));
     330             : 
     331           0 :                         g_Map.placeEntityPassable(template, playerID, positionDock, -angle + Math.PI / 2);
     332           0 :                         tileClassDock.add(positionDock);
     333           0 :                         break;
     334             :                 }
     335             : }

Generated by: LCOV version 1.14