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 rendered = createAreas(
40 new ChainPlacer(Math.floor(minSize * offset), Math.floor(maxSize * offset), Math.floor(spread * offset), 0.5),
42 new LayeredPainter([g_Terrains.cliff, g_Terrains.mainTerrain, constrastTerrain], [2, 3]),
43 new SmoothElevationPainter(ELEVATION_MODIFY, Math.floor(elevation * offset), 2),
44 paintClass(g_TileClasses.bluff)
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) * Math.euclidDistance2D(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));
391 pMinSize = Math.min(pMinSize, pMaxSize);
392 pMaxSize = Math.min(pMaxSize, el.maxSize);
393 pMinSize = Math.max(pMaxSize, el.minSize);
394 pSmooth = Math.max(pSmooth, 1);
397 new ChainPlacer(pMinSize, pMaxSize, pSpread, 0.5),
399 new LayeredPainter(el.painter, [widths.concat(pSmooth)]),
400 new SmoothElevationPainter(elType, pElevation, pSmooth),
409 * Create rolling hills.
411 function addHills(constraint, size, deviation, fill)
413 addElevation(constraint, {
414 "class": g_TileClasses.hill,
415 "painter": [g_Terrains.mainTerrain, g_Terrains.mainTerrain],
417 "deviation": deviation,
430 * Create random lakes with fish in it.
432 function addLakes(constraint, size, deviation, fill)
434 var lakeTile = g_Terrains.water;
436 if (currentBiome() == "temperate" || currentBiome() == "tropic")
437 lakeTile = g_Terrains.dirt;
439 if (currentBiome() == "mediterranean")
440 lakeTile = g_Terrains.tier2Terrain;
442 if (currentBiome() == "autumn")
443 lakeTile = g_Terrains.shore;
445 addElevation(constraint, {
446 "class": g_TileClasses.water,
447 "painter": [lakeTile, lakeTile],
449 "deviation": deviation,
464 g_TileClasses.fish, 12,
465 g_TileClasses.hill, 8,
466 g_TileClasses.mountain, 8,
467 g_TileClasses.player, 8
469 "stay": [g_TileClasses.water, 7],
472 "amounts": ["normal", "many", "tons"]
476 var group = new SimpleGroup([new SimpleObject(g_Decoratives.rockMedium, 1, 3, 1, 3)], true, g_TileClasses.dirt);
477 createObjectGroupsDeprecated(group, 0, [stayClasses(g_TileClasses.water, 1), borderClasses(g_TileClasses.water, 4, 3)], 1000, 100);
479 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);
480 createObjectGroupsDeprecated(group, 0, [stayClasses(g_TileClasses.water, 2), borderClasses(g_TileClasses.water, 4, 3)], 1000, 100);
484 * Universal function to create layered patches.
486 function addLayeredPatches(constraint, size, deviation, fill)
489 var maxRadius = Math.floor(scaleByMapSize(3, 5));
490 var count = fill * scaleByMapSize(15, 45);
493 scaleByMapSize(3, 6),
494 scaleByMapSize(5, 10),
495 scaleByMapSize(8, 21)
498 for (let patchSize of patchSizes)
500 var offset = getRandomDeviation(size, deviation);
501 var patchMinRadius = Math.floor(minRadius * offset);
502 var patchMaxRadius = Math.floor(maxRadius * offset);
505 new ChainPlacer(Math.min(patchMinRadius, patchMaxRadius), patchMaxRadius, Math.floor(patchSize * offset), 0.5),
509 [g_Terrains.mainTerrain, g_Terrains.tier1Terrain],
510 [g_Terrains.tier1Terrain, g_Terrains.tier2Terrain],
511 [g_Terrains.tier2Terrain, g_Terrains.tier3Terrain],
512 [g_Terrains.tier4Terrain]
515 paintClass(g_TileClasses.dirt)
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 hillElevation = randIntInclusive(4, 18);
578 new ChainPlacer(3, 15, 1, 0.5),
580 new LayeredPainter([plateauTile, plateauTile], [3]),
581 new SmoothElevationPainter(ELEVATION_MODIFY, hillElevation, hillElevation - 2),
582 paintClass(g_TileClasses.hill)
585 avoidClasses(g_TileClasses.hill, 7),
586 stayClasses(g_TileClasses.plateau, 7)
593 "func": addDecoration,
595 g_TileClasses.dirt, 15,
596 g_TileClasses.forest, 2,
597 g_TileClasses.player, 12,
598 g_TileClasses.water, 3
600 "stay": [g_TileClasses.plateau, 8],
608 g_TileClasses.forest, 2,
609 g_TileClasses.player, 12,
610 g_TileClasses.prop, 40,
611 g_TileClasses.water, 3
613 "stay": [g_TileClasses.plateau, 8],
616 "amounts": ["scarce"]
622 * Place less usual decoratives like barrels or crates.
624 function addProps(constraint, size, deviation, fill)
626 var offset = getRandomDeviation(size, deviation);
630 new SimpleObject(g_Props.skeleton, offset, 5 * offset, 0, 3 * offset + 2),
633 new SimpleObject(g_Props.barrels, offset, 2 * offset, 2, 3 * offset + 2),
634 new SimpleObject(g_Props.cart, 0, offset, 5, 2.5 * offset + 5),
635 new SimpleObject(g_Props.crate, offset, 2 * offset, 2, 2 * offset + 2),
636 new SimpleObject(g_Props.well, 0, 1, 2, 2 * offset + 2)
643 scaleByMapSize(16, 262),
644 scaleByMapSize(8, 131),
645 baseCount * scaleByMapSize(13, 200),
646 baseCount * scaleByMapSize(13, 200),
647 baseCount * scaleByMapSize(13, 200)
651 for (var i = 0; i < props.length; ++i)
653 var propCount = Math.floor(counts[i] * fill);
654 var group = new SimpleGroup(props[i], true);
655 createObjectGroupsDeprecated(group, 0, constraint, propCount, 5);
658 // Add decorative trees
659 var trees = new SimpleObject(g_Decoratives.tree, 5 * offset, 30 * offset, 2, 3 * offset + 10);
660 createObjectGroupsDeprecated(new SimpleGroup([trees], true), 0, constraint, counts[0] * 5 * fill, 5);
663 function addValleys(constraint, size, deviation, fill, baseHeight)
668 let minElevation = Math.max(-baseHeight, 1 - baseHeight / (size * (deviation + 1)));
670 var valleySlope = g_Terrains.tier1Terrain;
671 var valleyFloor = g_Terrains.tier4Terrain;
673 if (currentBiome() == "desert")
675 valleySlope = g_Terrains.tier3Terrain;
676 valleyFloor = g_Terrains.dirt;
679 if (currentBiome() == "mediterranean")
681 valleySlope = g_Terrains.tier2Terrain;
682 valleyFloor = g_Terrains.dirt;
685 if (currentBiome() == "alpine" || currentBiome() == "savanna")
686 valleyFloor = g_Terrains.tier2Terrain;
688 if (currentBiome() == "tropic")
689 valleySlope = g_Terrains.dirt;
691 if (currentBiome() == "autumn")
692 valleyFloor = g_Terrains.tier3Terrain;
694 addElevation(constraint, {
695 "class": g_TileClasses.valley,
696 "painter": [valleySlope, valleyFloor],
698 "deviation": deviation,
704 "minElevation": minElevation,
711 * Create huntable animals.
713 function addAnimals(constraint, size, deviation, fill)
715 var groupOffset = getRandomDeviation(size, deviation);
718 [new SimpleObject(g_Gaia.mainHuntableAnimal, 5 * groupOffset, 7 * groupOffset, 0, 4 * groupOffset)],
719 [new SimpleObject(g_Gaia.secondaryHuntableAnimal, 2 * groupOffset, 3 * groupOffset, 0, 2 * groupOffset)]
722 for (let animal of animals)
723 createObjectGroupsDeprecated(
724 new SimpleGroup(animal, true, g_TileClasses.animals),
727 Math.floor(30 * fill),
731 function addBerries(constraint, size, deviation, fill)
733 let groupOffset = getRandomDeviation(size, deviation);
735 createObjectGroupsDeprecated(
736 new SimpleGroup([new SimpleObject(g_Gaia.fruitBush, 5 * groupOffset, 5 * groupOffset, 0, 3 * groupOffset)], true, g_TileClasses.berries),
739 Math.floor(50 * fill),
743 function addFish(constraint, size, deviation, fill)
745 var groupOffset = getRandomDeviation(size, deviation);
748 [new SimpleObject(g_Gaia.fish, groupOffset, 2 * groupOffset, 0, 2 * groupOffset)],
749 [new SimpleObject(g_Gaia.fish, 2 * groupOffset, 4 * groupOffset, 10 * groupOffset, 20 * groupOffset)]
752 for (let fish of fishes)
753 createObjectGroupsDeprecated(
754 new SimpleGroup(fish, true, g_TileClasses.fish),
757 Math.floor(40 * fill),
761 function addForests(constraint, size, deviation, fill)
763 if (currentBiome() == "savanna")
768 g_Terrains.forestFloor2 + TERRAIN_SEPARATOR + g_Gaia.tree1,
769 g_Terrains.forestFloor2 + TERRAIN_SEPARATOR + g_Gaia.tree2,
770 g_Terrains.forestFloor2
773 g_Terrains.forestFloor1 + TERRAIN_SEPARATOR + g_Gaia.tree4,
774 g_Terrains.forestFloor1 + TERRAIN_SEPARATOR + g_Gaia.tree5,
775 g_Terrains.forestFloor1
781 [g_Terrains.forestFloor2, g_Terrains.mainTerrain, treeTypes[0]],
782 [g_Terrains.forestFloor2, treeTypes[0]]
785 [g_Terrains.forestFloor2, g_Terrains.mainTerrain, treeTypes[1]],
786 [g_Terrains.forestFloor1, treeTypes[1]]],
788 [g_Terrains.forestFloor1, g_Terrains.mainTerrain, treeTypes[0]],
789 [g_Terrains.forestFloor2, treeTypes[0]]],
791 [g_Terrains.forestFloor1, g_Terrains.mainTerrain, treeTypes[1]],
792 [g_Terrains.forestFloor1, treeTypes[1]]
796 for (let forestType of forestTypes)
798 let offset = getRandomDeviation(size, deviation);
800 new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5) * offset), Math.floor(50 * offset), 0.5),
802 new LayeredPainter(forestType, [2]),
803 paintClass(g_TileClasses.forest)
810 function addMetal(constraint, size, deviation, fill)
812 var offset = getRandomDeviation(size, deviation);
813 createObjectGroupsDeprecated(
814 new SimpleGroup([new SimpleObject(g_Gaia.metalLarge, offset, offset, 0, 4 * offset)], true, g_TileClasses.metal),
821 function addSmallMetal(constraint, size, mixes, amounts)
823 let deviation = getRandomDeviation(size, mixes);
824 createObjectGroupsDeprecated(
825 new SimpleGroup([new SimpleObject(g_Gaia.metalSmall, 2 * deviation, 5 * deviation, deviation, 3 * deviation)], true, g_TileClasses.metal),
833 * Create stone mines.
835 function addStone(constraint, size, deviation, fill)
837 var offset = getRandomDeviation(size, deviation);
841 new SimpleObject(g_Gaia.stoneSmall, 0, 2 * offset, 0, 4 * offset),
842 new SimpleObject(g_Gaia.stoneLarge, offset, offset, 0, 4 * offset)
845 new SimpleObject(g_Gaia.stoneSmall, 2 * offset, 5 * offset, offset, 3 * offset)
849 for (let mine of mines)
850 createObjectGroupsDeprecated(
851 new SimpleGroup(mine, true, g_TileClasses.rock),
859 * Create straggler trees.
861 function addStragglerTrees(constraint, size, deviation, fill)
863 // Ensure minimum distribution on african biome
864 if (currentBiome() == "savanna")
866 fill = Math.max(fill, 2);
867 size = Math.max(size, 1);
870 var trees = [g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree4];
872 var treesPerPlayer = 40;
873 var playerBonus = Math.max(1, (getNumPlayers() - 3) / 2);
875 var offset = getRandomDeviation(size, deviation);
876 var treeCount = treesPerPlayer * playerBonus * fill;
877 var totalTrees = scaleByMapSize(treeCount, treeCount);
879 var count = Math.floor(totalTrees / trees.length) * fill;
881 var max = 4 * offset;
882 var minDist = offset;
883 var maxDist = 5 * offset;
885 // More trees for the african biome
886 if (currentBiome() == "savanna")
890 minDist = 2 * offset + 1;
891 maxDist = 3 * offset + 2;
894 for (var i = 0; i < trees.length; ++i)
898 // Don't clump fruit trees
899 if (i == 2 && (currentBiome() == "desert" || currentBiome() == "mediterranean"))
902 min = Math.min(min, treesMax);
904 var group = new SimpleGroup([new SimpleObject(trees[i], min, treesMax, minDist, maxDist)], true, g_TileClasses.forest);
905 createObjectGroupsDeprecated(group, 0, constraint, count);
914 * Determine if the endline of the bluff is within the tilemap.
916 * @returns {Number} 0 if the bluff is reachable, otherwise a positive number
918 function unreachableBluff(bb, corners, baseLine, endLine)
920 // If we couldn't find a slope line
921 if (typeof baseLine.midX === "undefined" || typeof endLine.midX === "undefined")
924 // If the end points aren't on the tilemap
925 if (!g_Map.validT(endLine.x1, endLine.z1) && !g_Map.validT(endLine.x2, endLine.z2))
928 var minTilesInGroup = 1;
929 var insideBluff = false;
930 var outsideBluff = false;
932 // If there aren't enough points in each row
933 for (var x = 0; x < bb.length; ++x)
936 for (var z = 0; z < bb[x].length; ++z)
938 if (!bb[x][z].isFeature)
941 var valid = g_Map.validT(x + corners.minX, z + corners.minZ);
946 if (!insideBluff && valid)
949 if (outsideBluff && valid)
953 // We're expecting the end of the bluff
954 if (insideBluff && count < minTilesInGroup)
958 var insideBluff = false;
959 var outsideBluff = false;
961 // If there aren't enough points in each column
962 for (var z = 0; z < bb[0].length; ++z)
965 for (var x = 0; x < bb.length; ++x)
967 if (!bb[x][z].isFeature)
970 var valid = g_Map.validT(x + corners.minX, z + corners.minZ);
975 if (!insideBluff && valid)
978 if (outsideBluff && valid)
982 // We're expecting the end of the bluff
983 if (insideBluff && count < minTilesInGroup)
987 // Bluff is reachable
992 * Remove the bluff class and turn it into a plateau.
994 function removeBluff(points)
996 for (var i = 0; i < points.length; ++i)
997 addToClass(points[i].x, points[i].z, g_TileClasses.mountain);
1001 * Create an array of points the fill a bounding box around a terrain feature.
1003 function createBoundingBox(points, corners)
1006 var width = corners.maxX - corners.minX + 1;
1007 var length = corners.maxZ - corners.minZ + 1;
1008 for (var w = 0; w < width; ++w)
1011 for (var l = 0; l < length; ++l)
1013 var curHeight = g_Map.getHeight(w + corners.minX, l + corners.minZ);
1015 "height": curHeight,
1021 // Define the coordinates that represent the bluff
1022 for (var p = 0; p < points.length; ++p)
1025 bb[pt.x - corners.minX][pt.z - corners.minZ].isFeature = true;
1032 * Flattens the ground touching a terrain feature.
1034 function fadeToGround(bb, minX, minZ, elevation)
1036 var ground = createTerrain(g_Terrains.mainTerrain);
1037 for (var x = 0; x < bb.length; ++x)
1038 for (var z = 0; z < bb[x].length; ++z)
1041 if (!pt.isFeature && nextToFeature(bb, x, z))
1043 var newEl = smoothElevation(x + minX, z + minZ);
1044 g_Map.setHeight(x + minX, z + minZ, newEl);
1045 ground.place(x + minX, z + minZ);
1051 * Find a 45 degree line in a bounding box that does not intersect any terrain feature.
1053 function findClearLine(bb, corners, angle, baseHeight)
1055 // Angle - 0: northwest; 1: northeast; 2: southeast; 3: southwest
1056 var z = corners.maxZ;
1078 for (var x = corners.minX; x <= corners.maxX; ++x)
1085 while (x2 >= corners.minX && x2 <= corners.maxX && z2 >= corners.minZ && z2 <= corners.maxZ)
1087 var bp = bb[x2 - corners.minX][z2 - corners.minZ];
1088 if (bp.isFeature && g_Map.validT(x2, z2))
1100 var lastX = x2 - xOffset;
1101 var lastZ = z2 - zOffset;
1102 var midX = Math.floor((x + lastX) / 2);
1103 var midZ = Math.floor((z + lastZ) / 2);
1111 "height": baseHeight
1115 if (clear && (angle == 1 || angle == 2))
1118 if (!clear && (angle == 0 || angle == 3))
1126 * Returns the corners of a bounding box.
1128 function findCorners(points)
1130 // Find the bounding box of the terrain feature
1131 var mapSize = getMapSize();
1132 var minX = mapSize + 1;
1133 var minZ = mapSize + 1;
1137 for (var p = 0; p < points.length; ++p)
1141 minX = Math.min(pt.x, minX);
1142 minZ = Math.min(pt.z, minZ);
1144 maxX = Math.max(pt.x, maxX);
1145 maxZ = Math.max(pt.z, maxZ);
1157 * Finds the average elevation around a point.
1159 function smoothElevation(x, z)
1161 var min = g_Map.getHeight(x, z);
1163 for (var xOffset = -1; xOffset <= 1; ++xOffset)
1164 for (var zOffset = -1; zOffset <= 1; ++zOffset)
1166 var thisX = x + xOffset;
1167 var thisZ = z + zOffset;
1168 if (!g_Map.validH(thisX, thisZ))
1171 var height = g_Map.getHeight(thisX, thisZ);
1180 * Determines if a point in a bounding box array is next to a terrain feature.
1182 function nextToFeature(bb, x, z)
1184 for (var xOffset = -1; xOffset <= 1; ++xOffset)
1185 for (var zOffset = -1; zOffset <= 1; ++zOffset)
1187 var thisX = x + xOffset;
1188 var thisZ = z + zOffset;
1189 if (thisX < 0 || thisX >= bb.length || thisZ < 0 || thisZ >= bb[x].length || thisX == 0 && thisZ == 0)
1192 if (bb[thisX][thisZ].isFeature)
1200 * Returns a number within a random deviation of a base number.
1202 function getRandomDeviation(base, deviation)
1204 return base + randFloat(-1, 1) * Math.min(base, deviation);
1208 * Import a given digital elevation model.
1209 * Scale it to the mapsize and paint the textures specified by coordinate on it.
1211 * @return the ratio of heightmap tiles per map size tiles
1213 function paintHeightmap(mapName, func = undefined)
1216 * @property heightmap - An array with a square number of heights.
1217 * @property tilemap - The IDs of the palletmap to be painted for each heightmap tile.
1218 * @property pallet - The tile texture names used by the tilemap.
1220 let mapData = RMS.ReadJSONFile("maps/random/" + mapName + ".hmap");
1222 let mapSize = getMapSize(); // Width of the map in terrain tiles
1223 let hmSize = Math.sqrt(mapData.heightmap.length);
1224 let scale = hmSize / (mapSize + 1); // There are mapSize + 1 vertices (each 1 tile is surrounded by 2x2 vertices)
1226 for (let x = 0; x <= mapSize; ++x)
1227 for (let y = 0; y <= mapSize; ++y)
1229 let hmPoint = { "x": x * scale, "y": y * scale };
1230 let hmTile = { "x": Math.floor(hmPoint.x), "y": Math.floor(hmPoint.y) };
1231 let shift = { "x": 0, "y": 0 };
1235 else if (hmTile.x == hmSize - 1)
1237 else if (hmTile.x == hmSize - 2)
1242 else if (hmTile.y == hmSize - 1)
1244 else if (hmTile.y == hmSize - 2)
1248 for (let localXi = 0; localXi < 4; ++localXi)
1249 for (let localYi = 0; localYi < 4; ++localYi)
1250 neighbors.push(mapData.heightmap[(hmTile.x + localXi + shift.x - 1) * hmSize + (hmTile.y + localYi + shift.y - 1)]);
1252 setHeight(x, y, bicubicInterpolation(hmPoint.x - hmTile.x - shift.x, hmPoint.y - hmTile.y - shift.y, ...neighbors) / scale);
1254 if (x < mapSize && y < mapSize)
1256 let i = hmTile.x * hmSize + hmTile.y;
1257 let tile = mapData.pallet[mapData.tilemap[i]];
1258 placeTerrain(x, y, tile);