2 "barrels": "actor|props/special/eyecandy/barrels_buried.xml",
3 "crate": "actor|props/special/eyecandy/crate_a.xml",
4 "cart": "actor|props/special/eyecandy/handcart_1_broken.xml",
5 "well": "actor|props/special/eyecandy/well_1_c.xml",
6 "skeleton": "actor|props/special/eyecandy/skeleton.xml",
10 * Create bluffs, i.e. a slope hill reachable from ground level.
11 * Fill it with wood, mines, animals and decoratives.
13 * @param {Array} constraint - where to place them
14 * @param {number} size - size of the bluffs (1.2 would be 120% of normal)
15 * @param {number} deviation - degree of deviation from the defined size (0.2 would be 20% plus/minus)
16 * @param {number} fill - size of map to fill (1.5 would be 150% of normal)
17 * @param {number} baseHeight - elevation of the floor, making the bluff reachable
19 function addBluffs(constraint, size, deviation, fill, baseHeight)
21 var constrastTerrain = g_Terrains.tier2Terrain;
23 if (currentBiome() == "tropic")
24 constrastTerrain = g_Terrains.dirt;
26 if (currentBiome() == "autumn")
27 constrastTerrain = g_Terrains.tier3Terrain;
29 var count = fill * 15;
35 for (var i = 0; i < count; ++i)
37 var offset = getRandomDeviation(size, deviation);
39 var pMinSize = Math.floor(minSize * offset);
40 var pMaxSize = Math.floor(maxSize * offset);
41 var pSpread = Math.floor(spread * offset);
42 var pElevation = Math.floor(elevation * offset);
44 var placer = new ChainPlacer(pMinSize, pMaxSize, pSpread, 0.5);
45 var terrainPainter = new LayeredPainter([g_Terrains.cliff, g_Terrains.mainTerrain, constrastTerrain], [2, 3]);
46 var elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, pElevation, 2);
47 var rendered = createAreas(placer, [terrainPainter, elevationPainter, paintClass(g_TileClasses.bluff)], constraint, 1);
49 // Find the bounding box of the bluff
50 if (rendered[0] === undefined)
53 var points = rendered[0].points;
55 var corners = findCorners(points);
57 // Seed an array the size of the bounding box
58 var bb = createBoundingBox(points, corners);
60 // Get a random starting position for the baseline and the endline
61 var angle = randIntInclusive(0, 3);
62 var opAngle = angle - 2;
66 // Find the edges of the bluff
70 // If we can't access the bluff, try different angles
73 while (bluffCat != 0 && retries < 5)
75 baseLine = findClearLine(bb, corners, angle, baseHeight);
76 endLine = findClearLine(bb, corners, opAngle, baseHeight);
78 bluffCat = unreachableBluff(bb, corners, baseLine, endLine);
90 // Inaccessible, turn it into a plateau
97 // Create an entrance area by using a small margin
99 var ground = createTerrain(g_Terrains.mainTerrain);
100 var slopeLength = (1 - margin) * getDistance(baseLine.midX, baseLine.midZ, endLine.midX, endLine.midZ);
102 // Adjust the height of each point in the bluff
103 for (var p = 0; p < points.length; ++p)
106 var dist = distanceOfPointFromLine(baseLine.x1, baseLine.z1, baseLine.x2, baseLine.z2, pt.x, pt.z);
108 var curHeight = g_Map.getHeight(pt.x, pt.z);
109 var newHeight = curHeight - curHeight * (dist / slopeLength) - 2;
111 newHeight = Math.max(newHeight, endLine.height);
113 if (newHeight <= endLine.height + 2 && g_Map.validT(pt.x, pt.z) && g_Map.getTexture(pt.x, pt.z).indexOf('cliff') > -1)
114 ground.place(pt.x, pt.z);
116 g_Map.setHeight(pt.x, pt.z, newHeight);
119 // Smooth out the ground around the bluff
120 fadeToGround(bb, corners.minX, corners.minZ, endLine.height);
127 g_TileClasses.hill, 3,
128 g_TileClasses.player, 20,
129 g_TileClasses.valley, 2,
130 g_TileClasses.water, 2
132 "stay": [g_TileClasses.bluff, 3],
135 "amounts": g_AllAmounts
141 "func": addLayeredPatches,
143 g_TileClasses.dirt, 5,
144 g_TileClasses.forest, 2,
145 g_TileClasses.mountain, 2,
146 g_TileClasses.player, 12,
147 g_TileClasses.water, 3
149 "stay": [g_TileClasses.bluff, 5],
152 "amounts": ["normal"]
158 "func": addDecoration,
160 g_TileClasses.forest, 2,
161 g_TileClasses.player, 12,
162 g_TileClasses.water, 3
164 "stay": [g_TileClasses.bluff, 5],
167 "amounts": ["normal"]
175 g_TileClasses.forest, 2,
176 g_TileClasses.player, 12,
177 g_TileClasses.prop, 40,
178 g_TileClasses.water, 3
181 g_TileClasses.bluff, 7,
182 g_TileClasses.mountain, 7
186 "amounts": ["scarce"]
190 addElements(shuffleArray([
194 g_TileClasses.berries, 5,
195 g_TileClasses.forest, 18,
196 g_TileClasses.metal, 5,
197 g_TileClasses.mountain, 5,
198 g_TileClasses.player, 20,
199 g_TileClasses.rock, 5,
200 g_TileClasses.water, 2
202 "stay": [g_TileClasses.bluff, 6],
205 "amounts": ["normal", "many", "tons"]
210 g_TileClasses.berries, 5,
211 g_TileClasses.forest, 5,
212 g_TileClasses.mountain, 2,
213 g_TileClasses.player, 50,
214 g_TileClasses.rock, 15,
215 g_TileClasses.metal, 40,
216 g_TileClasses.water, 3
218 "stay": [g_TileClasses.bluff, 6],
221 "amounts": ["normal"]
226 g_TileClasses.berries, 5,
227 g_TileClasses.forest, 5,
228 g_TileClasses.mountain, 2,
229 g_TileClasses.player, 50,
230 g_TileClasses.rock, 40,
231 g_TileClasses.metal, 15,
232 g_TileClasses.water, 3
234 "stay": [g_TileClasses.bluff, 6],
237 "amounts": ["normal"]
241 let savanna = currentBiome() == "savanna";
242 addElements(shuffleArray([
244 "func": addStragglerTrees,
246 g_TileClasses.berries, 5,
247 g_TileClasses.forest, 10,
248 g_TileClasses.metal, 5,
249 g_TileClasses.mountain, 1,
250 g_TileClasses.player, 12,
251 g_TileClasses.rock, 5,
252 g_TileClasses.water, 5
254 "stay": [g_TileClasses.bluff, 6],
255 "sizes": savanna ? ["big"] : g_AllSizes,
256 "mixes": savanna ? ["varied"] : g_AllMixes,
257 "amounts": savanna ? ["tons"] : ["normal", "many", "tons"]
262 g_TileClasses.animals, 20,
263 g_TileClasses.forest, 5,
264 g_TileClasses.mountain, 1,
265 g_TileClasses.player, 20,
266 g_TileClasses.rock, 5,
267 g_TileClasses.metal, 5,
268 g_TileClasses.water, 3
270 "stay": [g_TileClasses.bluff, 6],
273 "amounts": ["normal", "many", "tons"]
278 g_TileClasses.berries, 50,
279 g_TileClasses.forest, 5,
280 g_TileClasses.metal, 10,
281 g_TileClasses.mountain, 2,
282 g_TileClasses.player, 20,
283 g_TileClasses.rock, 10,
284 g_TileClasses.water, 3
286 "stay": [g_TileClasses.bluff, 6],
289 "amounts": ["normal", "many", "tons"]
295 * Add grass, rocks and bushes.
297 function addDecoration(constraint, size, deviation, fill)
299 var offset = getRandomDeviation(size, deviation);
302 new SimpleObject(g_Decoratives.rockMedium, offset, 3 * offset, 0, offset)
305 new SimpleObject(g_Decoratives.rockLarge, offset, 2 * offset, 0, offset),
306 new SimpleObject(g_Decoratives.rockMedium, offset, 3 * offset, 0, 2 * offset)
309 new SimpleObject(g_Decoratives.grassShort, offset, 2 * offset, 0, offset)
312 new SimpleObject(g_Decoratives.grass, 2 * offset, 4 * offset, 0, 1.8 * offset),
313 new SimpleObject(g_Decoratives.grassShort, 3 * offset, 6 * offset, 1.2 * offset, 2.5 * offset)
316 new SimpleObject(g_Decoratives.bushMedium, offset, 2 * offset, 0, 2 * offset),
317 new SimpleObject(g_Decoratives.bushSmall, 2 * offset, 4 * offset, 0, 2 * offset)
322 if (currentBiome() == "tropic")
326 scaleByMapSize(16, 262),
327 scaleByMapSize(8, 131),
328 baseCount * scaleByMapSize(13, 200),
329 baseCount * scaleByMapSize(13, 200),
330 baseCount * scaleByMapSize(13, 200)
333 for (var i = 0; i < decorations.length; ++i)
335 var decorCount = Math.floor(counts[i] * fill);
336 var group = new SimpleGroup(decorations[i], true);
337 createObjectGroupsDeprecated(group, 0, constraint, decorCount, 5);
342 * Create varying elevations.
344 * @param {Array} constraint - avoid/stay-classes
346 * @param {Object} el - the element to be rendered, for example:
347 * "class": g_TileClasses.hill,
348 * "painter": [g_Terrains.mainTerrain, g_Terrains.mainTerrain],
352 * "count": scaleByMapSize(4, 8),
353 * "minSize": Math.floor(scaleByMapSize(3, 8)),
354 * "maxSize": Math.floor(scaleByMapSize(5, 10)),
355 * "spread": Math.floor(scaleByMapSize(10, 20)),
357 * "maxElevation": 12,
361 function addElevation(constraint, el)
363 var count = el.fill * el.count;
364 var minSize = el.minSize;
365 var maxSize = el.maxSize;
366 var spread = el.spread;
368 var elType = ELEVATION_MODIFY;
369 if (el.class == g_TileClasses.water)
370 elType = ELEVATION_SET;
374 // Allow for shore and cliff rendering
375 for (var s = el.painter.length; s > 2; --s)
378 for (var i = 0; i < count; ++i)
380 var elevation = randIntExclusive(el.minElevation, el.maxElevation);
381 var smooth = Math.floor(elevation / el.steepness);
383 var offset = getRandomDeviation(el.size, el.deviation);
384 var pMinSize = Math.floor(minSize * offset);
385 var pMaxSize = Math.floor(maxSize * offset);
386 var pSpread = Math.floor(spread * offset);
387 var pSmooth = Math.abs(Math.floor(smooth * offset));
388 var pElevation = Math.floor(elevation * offset);
390 pElevation = Math.max(el.minElevation, Math.min(pElevation, el.maxElevation));
392 pMinSize = Math.min(pMinSize, pMaxSize);
393 pMaxSize = Math.min(pMaxSize, el.maxSize);
394 pMinSize = Math.max(pMaxSize, el.minSize);
396 pSmooth = Math.max(pSmooth, 1);
398 var pWidths = widths.concat(pSmooth);
400 var placer = new ChainPlacer(pMinSize, pMaxSize, pSpread, 0.5);
401 var terrainPainter = new LayeredPainter(el.painter, [pWidths]);
402 var elevationPainter = new SmoothElevationPainter(elType, pElevation, pSmooth);
403 createAreas(placer, [terrainPainter, elevationPainter, paintClass(el.class)], constraint, 1);
408 * Create rolling hills.
410 function addHills(constraint, size, deviation, fill)
412 addElevation(constraint, {
413 "class": g_TileClasses.hill,
414 "painter": [g_Terrains.mainTerrain, g_Terrains.mainTerrain],
416 "deviation": deviation,
429 * Create random lakes with fish in it.
431 function addLakes(constraint, size, deviation, fill)
433 var lakeTile = g_Terrains.water;
435 if (currentBiome() == "temperate" || currentBiome() == "tropic")
436 lakeTile = g_Terrains.dirt;
438 if (currentBiome() == "mediterranean")
439 lakeTile = g_Terrains.tier2Terrain;
441 if (currentBiome() == "autumn")
442 lakeTile = g_Terrains.shore;
444 addElevation(constraint, {
445 "class": g_TileClasses.water,
446 "painter": [lakeTile, lakeTile],
448 "deviation": deviation,
463 g_TileClasses.fish, 12,
464 g_TileClasses.hill, 8,
465 g_TileClasses.mountain, 8,
466 g_TileClasses.player, 8
468 "stay": [g_TileClasses.water, 7],
471 "amounts": ["normal", "many", "tons"]
475 var group = new SimpleGroup([new SimpleObject(g_Decoratives.rockMedium, 1, 3, 1, 3)], true, g_TileClasses.dirt);
476 createObjectGroupsDeprecated(group, 0, [stayClasses(g_TileClasses.water, 1), borderClasses(g_TileClasses.water, 4, 3)], 1000, 100);
478 group = new SimpleGroup([new SimpleObject(g_Decoratives.reeds, 10, 15, 1, 3), new SimpleObject(g_Decoratives.rockMedium, 1, 3, 1, 3)], true, g_TileClasses.dirt);
479 createObjectGroupsDeprecated(group, 0, [stayClasses(g_TileClasses.water, 2), borderClasses(g_TileClasses.water, 4, 3)], 1000, 100);
483 * Universal function to create layered patches.
485 function addLayeredPatches(constraint, size, deviation, fill)
488 var maxRadius = Math.floor(scaleByMapSize(3, 5));
489 var count = fill * scaleByMapSize(15, 45);
492 scaleByMapSize(3, 6),
493 scaleByMapSize(5, 10),
494 scaleByMapSize(8, 21)
497 for (var i = 0; i < sizes.length; ++i)
499 var offset = getRandomDeviation(size, deviation);
500 var patchMinRadius = Math.floor(minRadius * offset);
501 var patchMaxRadius = Math.floor(maxRadius * offset);
502 var patchSize = Math.floor(sizes[i] * offset);
503 var patchCount = count * offset;
505 if (patchMinRadius > patchMaxRadius)
506 patchMinRadius = patchMaxRadius;
508 var placer = new ChainPlacer(patchMinRadius, patchMaxRadius, patchSize, 0.5);
509 var painter = new LayeredPainter(
511 [g_Terrains.mainTerrain, g_Terrains.tier1Terrain],
512 [g_Terrains.tier1Terrain, g_Terrains.tier2Terrain],
513 [g_Terrains.tier2Terrain, g_Terrains.tier3Terrain],
514 [g_Terrains.tier4Terrain]
518 createAreas(placer, [painter, paintClass(g_TileClasses.dirt)], constraint, patchCount);
523 * Create steep mountains.
525 function addMountains(constraint, size, deviation, fill)
527 addElevation(constraint, {
528 "class": g_TileClasses.mountain,
529 "painter": [g_Terrains.cliff, g_Terrains.hill],
531 "deviation": deviation,
546 function addPlateaus(constraint, size, deviation, fill)
548 var plateauTile = g_Terrains.dirt;
550 if (currentBiome() == "snowy")
551 plateauTile = g_Terrains.tier1Terrain;
553 if (currentBiome() == "alpine" || currentBiome() == "savanna")
554 plateauTile = g_Terrains.tier2Terrain;
556 if (currentBiome() == "autumn")
557 plateauTile = g_Terrains.tier4Terrain;
559 addElevation(constraint, {
560 "class": g_TileClasses.plateau,
561 "painter": [g_Terrains.cliff, plateauTile],
563 "deviation": deviation,
574 for (var i = 0; i < 40; ++i)
576 var placer = new ChainPlacer(3, 15, 1, 0.5);
577 var terrainPainter = new LayeredPainter([plateauTile, plateauTile], [3]);
578 var hillElevation = randIntInclusive(4, 18);
579 var elevationPainter = new SmoothElevationPainter(ELEVATION_MODIFY, hillElevation, hillElevation - 2);
586 paintClass(g_TileClasses.hill)
589 avoidClasses(g_TileClasses.hill, 7),
590 stayClasses(g_TileClasses.plateau, 7)
598 "func": addDecoration,
600 g_TileClasses.dirt, 15,
601 g_TileClasses.forest, 2,
602 g_TileClasses.player, 12,
603 g_TileClasses.water, 3
605 "stay": [g_TileClasses.plateau, 8],
613 g_TileClasses.forest, 2,
614 g_TileClasses.player, 12,
615 g_TileClasses.prop, 40,
616 g_TileClasses.water, 3
618 "stay": [g_TileClasses.plateau, 8],
621 "amounts": ["scarce"]
627 * Place less usual decoratives like barrels or crates.
629 function addProps(constraint, size, deviation, fill)
631 var offset = getRandomDeviation(size, deviation);
635 new SimpleObject(g_Props.skeleton, offset, 5 * offset, 0, 3 * offset + 2),
638 new SimpleObject(g_Props.barrels, offset, 2 * offset, 2, 3 * offset + 2),
639 new SimpleObject(g_Props.cart, 0, offset, 5, 2.5 * offset + 5),
640 new SimpleObject(g_Props.crate, offset, 2 * offset, 2, 2 * offset + 2),
641 new SimpleObject(g_Props.well, 0, 1, 2, 2 * offset + 2)
648 scaleByMapSize(16, 262),
649 scaleByMapSize(8, 131),
650 baseCount * scaleByMapSize(13, 200),
651 baseCount * scaleByMapSize(13, 200),
652 baseCount * scaleByMapSize(13, 200)
656 for (var i = 0; i < props.length; ++i)
658 var propCount = Math.floor(counts[i] * fill);
659 var group = new SimpleGroup(props[i], true);
660 createObjectGroupsDeprecated(group, 0, constraint, propCount, 5);
663 // Add decorative trees
664 var trees = new SimpleObject(g_Decoratives.tree, 5 * offset, 30 * offset, 2, 3 * offset + 10);
665 createObjectGroupsDeprecated(new SimpleGroup([trees], true), 0, constraint, counts[0] * 5 * fill, 5);
668 function addValleys(constraint, size, deviation, fill, baseHeight)
673 let minElevation = Math.max(-baseHeight, 1 - baseHeight / (size * (deviation + 1)));
675 var valleySlope = g_Terrains.tier1Terrain;
676 var valleyFloor = g_Terrains.tier4Terrain;
678 if (currentBiome() == "desert")
680 valleySlope = g_Terrains.tier3Terrain;
681 valleyFloor = g_Terrains.dirt;
684 if (currentBiome() == "mediterranean")
686 valleySlope = g_Terrains.tier2Terrain;
687 valleyFloor = g_Terrains.dirt;
690 if (currentBiome() == "alpine" || currentBiome() == "savanna")
691 valleyFloor = g_Terrains.tier2Terrain;
693 if (currentBiome() == "tropic")
694 valleySlope = g_Terrains.dirt;
696 if (currentBiome() == "autumn")
697 valleyFloor = g_Terrains.tier3Terrain;
699 addElevation(constraint, {
700 "class": g_TileClasses.valley,
701 "painter": [valleySlope, valleyFloor],
703 "deviation": deviation,
709 "minElevation": minElevation,
716 * Create huntable animals.
718 function addAnimals(constraint, size, deviation, fill)
720 var groupOffset = getRandomDeviation(size, deviation);
723 [new SimpleObject(g_Gaia.mainHuntableAnimal, 5 * groupOffset, 7 * groupOffset, 0, 4 * groupOffset)],
724 [new SimpleObject(g_Gaia.secondaryHuntableAnimal, 2 * groupOffset, 3 * groupOffset, 0, 2 * groupOffset)]
727 for (let animal of animals)
728 createObjectGroupsDeprecated(
729 new SimpleGroup(animal, true, g_TileClasses.animals),
732 Math.floor(30 * fill),
736 function addBerries(constraint, size, deviation, fill)
738 let groupOffset = getRandomDeviation(size, deviation);
740 createObjectGroupsDeprecated(
741 new SimpleGroup([new SimpleObject(g_Gaia.fruitBush, 5 * groupOffset, 5 * groupOffset, 0, 3 * groupOffset)], true, g_TileClasses.berries),
744 Math.floor(50 * fill),
748 function addFish(constraint, size, deviation, fill)
750 var groupOffset = getRandomDeviation(size, deviation);
753 [new SimpleObject(g_Gaia.fish, groupOffset, 2 * groupOffset, 0, 2 * groupOffset)],
754 [new SimpleObject(g_Gaia.fish, 2 * groupOffset, 4 * groupOffset, 10 * groupOffset, 20 * groupOffset)]
757 for (let fish of fishes)
758 createObjectGroupsDeprecated(
759 new SimpleGroup(fish, true, g_TileClasses.fish),
762 Math.floor(40 * fill),
766 function addForests(constraint, size, deviation, fill)
768 if (currentBiome() == "savanna")
773 g_Terrains.forestFloor2 + TERRAIN_SEPARATOR + g_Gaia.tree1,
774 g_Terrains.forestFloor2 + TERRAIN_SEPARATOR + g_Gaia.tree2,
775 g_Terrains.forestFloor2
778 g_Terrains.forestFloor1 + TERRAIN_SEPARATOR + g_Gaia.tree4,
779 g_Terrains.forestFloor1 + TERRAIN_SEPARATOR + g_Gaia.tree5,
780 g_Terrains.forestFloor1
786 [g_Terrains.forestFloor2, g_Terrains.mainTerrain, treeTypes[0]],
787 [g_Terrains.forestFloor2, treeTypes[0]]
790 [g_Terrains.forestFloor2, g_Terrains.mainTerrain, treeTypes[1]],
791 [g_Terrains.forestFloor1, treeTypes[1]]],
793 [g_Terrains.forestFloor1, g_Terrains.mainTerrain, treeTypes[0]],
794 [g_Terrains.forestFloor2, treeTypes[0]]],
796 [g_Terrains.forestFloor1, g_Terrains.mainTerrain, treeTypes[1]],
797 [g_Terrains.forestFloor1, treeTypes[1]]
801 for (let forestType of forestTypes)
803 let offset = getRandomDeviation(size, deviation);
805 new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5) * offset), Math.floor(50 * offset), 0.5),
807 new LayeredPainter(forestType, [2]),
808 paintClass(g_TileClasses.forest)
815 function addMetal(constraint, size, deviation, fill)
817 var offset = getRandomDeviation(size, deviation);
818 createObjectGroupsDeprecated(
819 new SimpleGroup([new SimpleObject(g_Gaia.metalLarge, offset, offset, 0, 4 * offset)], true, g_TileClasses.metal),
826 function addSmallMetal(constraint, size, mixes, amounts)
828 let deviation = getRandomDeviation(size, mixes);
829 createObjectGroupsDeprecated(
830 new SimpleGroup([new SimpleObject(g_Gaia.metalSmall, 2 * deviation, 5 * deviation, deviation, 3 * deviation)], true, g_TileClasses.metal),
838 * Create stone mines.
840 function addStone(constraint, size, deviation, fill)
842 var offset = getRandomDeviation(size, deviation);
846 new SimpleObject(g_Gaia.stoneSmall, 0, 2 * offset, 0, 4 * offset),
847 new SimpleObject(g_Gaia.stoneLarge, offset, offset, 0, 4 * offset)
850 new SimpleObject(g_Gaia.stoneSmall, 2 * offset, 5 * offset, offset, 3 * offset)
854 for (let mine of mines)
855 createObjectGroupsDeprecated(
856 new SimpleGroup(mine, true, g_TileClasses.rock),
864 * Create straggler trees.
866 function addStragglerTrees(constraint, size, deviation, fill)
868 // Ensure minimum distribution on african biome
869 if (currentBiome() == "savanna")
871 fill = Math.max(fill, 2);
872 size = Math.max(size, 1);
875 var trees = [g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree4];
877 var treesPerPlayer = 40;
878 var playerBonus = Math.max(1, (getNumPlayers() - 3) / 2);
880 var offset = getRandomDeviation(size, deviation);
881 var treeCount = treesPerPlayer * playerBonus * fill;
882 var totalTrees = scaleByMapSize(treeCount, treeCount);
884 var count = Math.floor(totalTrees / trees.length) * fill;
886 var max = 4 * offset;
887 var minDist = offset;
888 var maxDist = 5 * offset;
890 // More trees for the african biome
891 if (currentBiome() == "savanna")
895 minDist = 2 * offset + 1;
896 maxDist = 3 * offset + 2;
899 for (var i = 0; i < trees.length; ++i)
903 // Don't clump fruit trees
904 if (i == 2 && (currentBiome() == "desert" || currentBiome() == "mediterranean"))
907 min = Math.min(min, treesMax);
909 var group = new SimpleGroup([new SimpleObject(trees[i], min, treesMax, minDist, maxDist)], true, g_TileClasses.forest);
910 createObjectGroupsDeprecated(group, 0, constraint, count);
919 * Determine if the endline of the bluff is within the tilemap.
921 * @returns {Number} 0 if the bluff is reachable, otherwise a positive number
923 function unreachableBluff(bb, corners, baseLine, endLine)
925 // If we couldn't find a slope line
926 if (typeof baseLine.midX === "undefined" || typeof endLine.midX === "undefined")
929 // If the end points aren't on the tilemap
930 if (!g_Map.validT(endLine.x1, endLine.z1) && !g_Map.validT(endLine.x2, endLine.z2))
933 var minTilesInGroup = 1;
934 var insideBluff = false;
935 var outsideBluff = false;
937 // If there aren't enough points in each row
938 for (var x = 0; x < bb.length; ++x)
941 for (var z = 0; z < bb[x].length; ++z)
943 if (!bb[x][z].isFeature)
946 var valid = g_Map.validT(x + corners.minX, z + corners.minZ);
951 if (!insideBluff && valid)
954 if (outsideBluff && valid)
958 // We're expecting the end of the bluff
959 if (insideBluff && count < minTilesInGroup)
963 var insideBluff = false;
964 var outsideBluff = false;
966 // If there aren't enough points in each column
967 for (var z = 0; z < bb[0].length; ++z)
970 for (var x = 0; x < bb.length; ++x)
972 if (!bb[x][z].isFeature)
975 var valid = g_Map.validT(x + corners.minX, z + corners.minZ);
980 if (!insideBluff && valid)
983 if (outsideBluff && valid)
987 // We're expecting the end of the bluff
988 if (insideBluff && count < minTilesInGroup)
992 // Bluff is reachable
997 * Remove the bluff class and turn it into a plateau.
999 function removeBluff(points)
1001 for (var i = 0; i < points.length; ++i)
1002 addToClass(points[i].x, points[i].z, g_TileClasses.mountain);
1006 * Create an array of points the fill a bounding box around a terrain feature.
1008 function createBoundingBox(points, corners)
1011 var width = corners.maxX - corners.minX + 1;
1012 var length = corners.maxZ - corners.minZ + 1;
1013 for (var w = 0; w < width; ++w)
1016 for (var l = 0; l < length; ++l)
1018 var curHeight = g_Map.getHeight(w + corners.minX, l + corners.minZ);
1020 "height": curHeight,
1026 // Define the coordinates that represent the bluff
1027 for (var p = 0; p < points.length; ++p)
1030 bb[pt.x - corners.minX][pt.z - corners.minZ].isFeature = true;
1037 * Flattens the ground touching a terrain feature.
1039 function fadeToGround(bb, minX, minZ, elevation)
1041 var ground = createTerrain(g_Terrains.mainTerrain);
1042 for (var x = 0; x < bb.length; ++x)
1043 for (var z = 0; z < bb[x].length; ++z)
1046 if (!pt.isFeature && nextToFeature(bb, x, z))
1048 var newEl = smoothElevation(x + minX, z + minZ);
1049 g_Map.setHeight(x + minX, z + minZ, newEl);
1050 ground.place(x + minX, z + minZ);
1056 * Find a 45 degree line in a bounding box that does not intersect any terrain feature.
1058 function findClearLine(bb, corners, angle, baseHeight)
1060 // Angle - 0: northwest; 1: northeast; 2: southeast; 3: southwest
1061 var z = corners.maxZ;
1083 for (var x = corners.minX; x <= corners.maxX; ++x)
1090 while (x2 >= corners.minX && x2 <= corners.maxX && z2 >= corners.minZ && z2 <= corners.maxZ)
1092 var bp = bb[x2 - corners.minX][z2 - corners.minZ];
1093 if (bp.isFeature && g_Map.validT(x2, z2))
1105 var lastX = x2 - xOffset;
1106 var lastZ = z2 - zOffset;
1107 var midX = Math.floor((x + lastX) / 2);
1108 var midZ = Math.floor((z + lastZ) / 2);
1116 "height": baseHeight
1120 if (clear && (angle == 1 || angle == 2))
1123 if (!clear && (angle == 0 || angle == 3))
1131 * Returns the corners of a bounding box.
1133 function findCorners(points)
1135 // Find the bounding box of the terrain feature
1136 var mapSize = getMapSize();
1137 var minX = mapSize + 1;
1138 var minZ = mapSize + 1;
1142 for (var p = 0; p < points.length; ++p)
1146 minX = Math.min(pt.x, minX);
1147 minZ = Math.min(pt.z, minZ);
1149 maxX = Math.max(pt.x, maxX);
1150 maxZ = Math.max(pt.z, maxZ);
1162 * Finds the average elevation around a point.
1164 function smoothElevation(x, z)
1166 var min = g_Map.getHeight(x, z);
1168 for (var xOffset = -1; xOffset <= 1; ++xOffset)
1169 for (var zOffset = -1; zOffset <= 1; ++zOffset)
1171 var thisX = x + xOffset;
1172 var thisZ = z + zOffset;
1173 if (!g_Map.validH(thisX, thisZ))
1176 var height = g_Map.getHeight(thisX, thisZ);
1185 * Determines if a point in a bounding box array is next to a terrain feature.
1187 function nextToFeature(bb, x, z)
1189 for (var xOffset = -1; xOffset <= 1; ++xOffset)
1190 for (var zOffset = -1; zOffset <= 1; ++zOffset)
1192 var thisX = x + xOffset;
1193 var thisZ = z + zOffset;
1194 if (thisX < 0 || thisX >= bb.length || thisZ < 0 || thisZ >= bb[x].length || thisX == 0 && thisZ == 0)
1197 if (bb[thisX][thisZ].isFeature)
1205 * Returns a number within a random deviation of a base number.
1207 function getRandomDeviation(base, deviation)
1209 return base + randFloat(-1, 1) * Math.min(base, deviation);
1213 * Import a given digital elevation model.
1214 * Scale it to the mapsize and paint the textures specified by coordinate on it.
1216 * @return the ratio of heightmap tiles per map size tiles
1218 function paintHeightmap(mapName, func = undefined)
1221 * @property heightmap - An array with a square number of heights.
1222 * @property tilemap - The IDs of the palletmap to be painted for each heightmap tile.
1223 * @property pallet - The tile texture names used by the tilemap.
1225 let mapData = RMS.ReadJSONFile("maps/random/" + mapName + ".hmap");
1227 let mapSize = getMapSize(); // Width of the map in terrain tiles
1228 let hmSize = Math.sqrt(mapData.heightmap.length);
1229 let scale = hmSize / (mapSize + 1); // There are mapSize + 1 vertices (each 1 tile is surrounded by 2x2 vertices)
1231 for (let x = 0; x <= mapSize; ++x)
1232 for (let y = 0; y <= mapSize; ++y)
1234 let hmPoint = { "x": x * scale, "y": y * scale };
1235 let hmTile = { "x": Math.floor(hmPoint.x), "y": Math.floor(hmPoint.y) };
1236 let shift = { "x": 0, "y": 0 };
1240 else if (hmTile.x == hmSize - 1)
1242 else if (hmTile.x == hmSize - 2)
1247 else if (hmTile.y == hmSize - 1)
1249 else if (hmTile.y == hmSize - 2)
1253 for (let localXi = 0; localXi < 4; ++localXi)
1254 for (let localYi = 0; localYi < 4; ++localYi)
1255 neighbors.push(mapData.heightmap[(hmTile.x + localXi + shift.x - 1) * hmSize + (hmTile.y + localYi + shift.y - 1)]);
1257 setHeight(x, y, bicubicInterpolation(hmPoint.x - hmTile.x - shift.x, hmPoint.y - hmTile.y - shift.y, ...neighbors) / scale);
1259 if (x < mapSize && y < mapSize)
1261 let i = hmTile.x * hmSize + hmTile.y;
1262 let tile = mapData.pallet[mapData.tilemap[i]];
1263 placeTerrain(x, y, tile);