Line data Source code
1 0 : var g_Props = {
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",
7 : };
8 :
9 : /**
10 : * Prevent circular patterns around the CC by marking a random chain of circles there to be ignored by bluffs.
11 : */
12 : function markPlayerAvoidanceArea(playerPosition, radius)
13 : {
14 0 : for (let position of playerPosition)
15 0 : createArea(
16 : new ChainPlacer(3, 6, scaleByMapSize(25, 60), Infinity, position, radius),
17 : new TileClassPainter(g_TileClasses.bluffIgnore),
18 : undefined,
19 : scaleByMapSize(7, 14));
20 :
21 0 : createArea(
22 : new MapBoundsPlacer(),
23 : new TileClassPainter(g_TileClasses.bluffIgnore),
24 : new NearTileClassConstraint(g_TileClasses.baseResource, 5));
25 : }
26 :
27 : /**
28 : * Paints a ramp from the given positions to t
29 : * Bluffs might surround playerbases either entirely or unfairly.
30 : */
31 : function createBluffsPassages(playerPosition)
32 : {
33 0 : g_Map.log("Creating passages towards the center");
34 0 : for (let position of playerPosition)
35 : {
36 0 : let successful = true;
37 0 : for (let tryCount = 0; tryCount < 80; ++tryCount)
38 : {
39 0 : let angle = position.angleTo(g_Map.getCenter()) + randFloat(-1, 1) * Math.PI / 2;
40 0 : let start = Vector2D.add(position, new Vector2D(defaultPlayerBaseRadius() * 0.7, 0).rotate(angle).perpendicular()).round();
41 0 : let end = Vector2D.add(position, new Vector2D(defaultPlayerBaseRadius() * randFloat(1.7, 2), 0).rotate(angle).perpendicular()).round();
42 :
43 0 : if (g_TileClasses.forest.has(end) || !stayClasses(g_TileClasses.bluff, 12).allows(end))
44 0 : continue;
45 :
46 0 : if ((g_Map.getHeight(end.clone().floor()) - g_Map.getHeight(start.clone().floor())) / start.distanceTo(end) > 1.5)
47 0 : continue;
48 :
49 0 : let area = createPassage({
50 : "start": start,
51 : "end": end,
52 : "startWidth": scaleByMapSize(10, 20),
53 : "endWidth": scaleByMapSize(10, 14),
54 : "smoothWidth": 3,
55 : "terrain": g_Terrains.mainTerrain,
56 : "tileClass": g_TileClasses.bluffsPassage
57 : });
58 :
59 0 : for (let point of area.getPoints())
60 0 : g_Map.deleteTerrainEntity(point);
61 :
62 0 : createArea(
63 : new MapBoundsPlacer(),
64 : new TerrainPainter(g_Terrains.cliff),
65 : [
66 : new StayAreasConstraint([area]),
67 : new SlopeConstraint(2, Infinity)
68 : ]);
69 :
70 0 : break;
71 : }
72 : }
73 : }
74 :
75 : /**
76 : * Create bluffs, i.e. a slope hill reachable from ground level.
77 : * Fill it with wood, mines, animals and decoratives.
78 : *
79 : * @param {Array} constraint - where to place them
80 : * @param {number} size - size of the bluffs (1.2 would be 120% of normal)
81 : * @param {number} deviation - degree of deviation from the defined size (0.2 would be 20% plus/minus)
82 : * @param {number} fill - size of map to fill (1.5 would be 150% of normal)
83 : * @param {number} baseHeight - elevation of the floor, making the bluff reachable
84 : */
85 : function addBluffs(constraint, size, deviation, fill, baseHeight)
86 : {
87 0 : g_Map.log("Creating bluffs");
88 :
89 0 : let elevation = 30;
90 :
91 : // Percent of the length of the bluff determining the entrance area
92 0 : let margin = 0.08;
93 :
94 0 : let constrastTerrain = g_Terrains.tier2Terrain;
95 :
96 0 : if (currentBiome() == "generic/india")
97 0 : constrastTerrain = g_Terrains.dirt;
98 :
99 0 : if (currentBiome() == "generic/autumn")
100 0 : constrastTerrain = g_Terrains.tier3Terrain;
101 :
102 0 : for (let i = 0; i < fill * 15; ++i)
103 : {
104 0 : let bluffDeviation = getRandomDeviation(size, deviation);
105 :
106 : // Pick a random bluff location and shape
107 0 : let areasBluff = createAreas(
108 : new ChainPlacer(5 * bluffDeviation, 7 * bluffDeviation, 100 * bluffDeviation, 0.5),
109 : undefined,
110 : constraint,
111 : 1);
112 :
113 0 : if (!areasBluff.length || !areasBluff[0].getPoints().length)
114 0 : continue;
115 :
116 : // Get a random starting position for the baseline and the endline
117 0 : let angle = randIntInclusive(0, 3);
118 0 : let opposingAngle = (angle + 2) % 4;
119 :
120 : // Find the edges of the bluff
121 : let baseLine;
122 : let endLine;
123 :
124 : // If we can't access the bluff, try different angles
125 0 : let retries = 0;
126 0 : let bluffPassable = false;
127 0 : while (!bluffPassable && retries++ < 4)
128 : {
129 0 : baseLine = findClearLine(areasBluff[0], angle);
130 0 : endLine = findClearLine(areasBluff[0], opposingAngle);
131 0 : bluffPassable = isBluffPassable(areasBluff[0], baseLine, endLine);
132 :
133 0 : angle = (angle + 1) % 4;
134 0 : opposingAngle = (angle + 2) % 4;
135 : }
136 :
137 0 : if (!bluffPassable)
138 0 : continue;
139 :
140 : // Paint bluff texture and elevation
141 0 : createArea(
142 : new MapBoundsPlacer(),
143 : [
144 : new LayeredPainter([g_Terrains.mainTerrain, constrastTerrain], [5]),
145 : new SmoothElevationPainter(ELEVATION_MODIFY, elevation * bluffDeviation, 2),
146 : new TileClassPainter(g_TileClasses.bluff)
147 : ],
148 : new StayAreasConstraint(areasBluff));
149 :
150 0 : let slopeLength = (1 - margin) * Vector2D.average([baseLine.start, baseLine.end]).distanceTo(Vector2D.average([endLine.start, endLine.end]));
151 :
152 : // Adjust the height of each point in the bluff
153 0 : for (let point of areasBluff[0].getPoints())
154 : {
155 0 : let dist = Math.abs(distanceOfPointFromLine(baseLine.start, baseLine.end, point));
156 0 : g_Map.setHeight(point, Math.max(g_Map.getHeight(point) * (1 - dist / slopeLength) - 2, baseHeight));
157 : }
158 :
159 : // Flatten all points adjacent to but not on the bluff
160 0 : createArea(
161 : new MapBoundsPlacer(),
162 : [
163 : new SmoothingPainter(1, 1, 1),
164 : new TerrainPainter(g_Terrains.mainTerrain)
165 : ],
166 : new AdjacentToAreaConstraint(areasBluff));
167 :
168 : // Paint cliffs
169 0 : createArea(
170 : new MapBoundsPlacer(),
171 : new TerrainPainter(g_Terrains.cliff),
172 : [
173 : new StayAreasConstraint(areasBluff),
174 : new SlopeConstraint(2, Infinity)
175 : ]);
176 :
177 : // Performance improvement
178 0 : createArea(
179 : new MapBoundsPlacer(),
180 : new TileClassPainter(g_TileClasses.bluffIgnore),
181 : new NearTileClassConstraint(g_TileClasses.bluff, 8));
182 : }
183 :
184 0 : addElements([
185 : {
186 : "func": addHills,
187 : "avoid": [
188 : g_TileClasses.hill, 3,
189 : g_TileClasses.player, 20,
190 : g_TileClasses.valley, 2,
191 : g_TileClasses.water, 2
192 : ],
193 : "stay": [g_TileClasses.bluff, 3],
194 : "sizes": g_AllSizes,
195 : "mixes": g_AllMixes,
196 : "amounts": g_AllAmounts
197 : }
198 : ]);
199 :
200 0 : addElements([
201 : {
202 : "func": addLayeredPatches,
203 : "avoid": [
204 : g_TileClasses.dirt, 5,
205 : g_TileClasses.forest, 2,
206 : g_TileClasses.mountain, 2,
207 : g_TileClasses.player, 12,
208 : g_TileClasses.water, 3
209 : ],
210 : "stay": [g_TileClasses.bluff, 5],
211 : "sizes": ["normal"],
212 : "mixes": ["normal"],
213 : "amounts": ["normal"]
214 : }
215 : ]);
216 :
217 0 : addElements([
218 : {
219 : "func": addDecoration,
220 : "avoid": [
221 : g_TileClasses.forest, 2,
222 : g_TileClasses.player, 12,
223 : g_TileClasses.water, 3
224 : ],
225 : "stay": [g_TileClasses.bluff, 5],
226 : "sizes": ["normal"],
227 : "mixes": ["normal"],
228 : "amounts": ["normal"]
229 : }
230 : ]);
231 :
232 0 : addElements([
233 : {
234 : "func": addProps,
235 : "avoid": [
236 : g_TileClasses.forest, 2,
237 : g_TileClasses.player, 12,
238 : g_TileClasses.prop, 40,
239 : g_TileClasses.water, 3
240 : ],
241 : "stay": [
242 : g_TileClasses.bluff, 7,
243 : g_TileClasses.mountain, 7
244 : ],
245 : "sizes": ["normal"],
246 : "mixes": ["normal"],
247 : "amounts": ["scarce"]
248 : }
249 : ]);
250 :
251 0 : addElements(shuffleArray([
252 : {
253 : "func": addForests,
254 : "avoid": [
255 : g_TileClasses.berries, 5,
256 : g_TileClasses.forest, 18,
257 : g_TileClasses.metal, 5,
258 : g_TileClasses.mountain, 5,
259 : g_TileClasses.player, 20,
260 : g_TileClasses.rock, 5,
261 : g_TileClasses.water, 2
262 : ],
263 : "stay": [g_TileClasses.bluff, 6],
264 : "sizes": g_AllSizes,
265 : "mixes": g_AllMixes,
266 : "amounts": ["normal", "many", "tons"]
267 : },
268 : {
269 : "func": addMetal,
270 : "avoid": [
271 : g_TileClasses.berries, 5,
272 : g_TileClasses.forest, 5,
273 : g_TileClasses.mountain, 2,
274 : g_TileClasses.player, 50,
275 : g_TileClasses.rock, 15,
276 : g_TileClasses.metal, 40,
277 : g_TileClasses.water, 3
278 : ],
279 : "stay": [g_TileClasses.bluff, 6],
280 : "sizes": ["normal"],
281 : "mixes": ["same"],
282 : "amounts": ["normal"]
283 : },
284 : {
285 : "func": addStone,
286 : "avoid": [
287 : g_TileClasses.berries, 5,
288 : g_TileClasses.forest, 5,
289 : g_TileClasses.mountain, 2,
290 : g_TileClasses.player, 50,
291 : g_TileClasses.rock, 40,
292 : g_TileClasses.metal, 15,
293 : g_TileClasses.water, 3
294 : ],
295 : "stay": [g_TileClasses.bluff, 6],
296 : "sizes": ["normal"],
297 : "mixes": ["same"],
298 : "amounts": ["normal"]
299 : }
300 : ]));
301 :
302 0 : let savanna = currentBiome() == "generic/savanna";
303 0 : addElements(shuffleArray([
304 : {
305 : "func": addStragglerTrees,
306 : "avoid": [
307 : g_TileClasses.berries, 5,
308 : g_TileClasses.forest, 10,
309 : g_TileClasses.metal, 5,
310 : g_TileClasses.mountain, 1,
311 : g_TileClasses.player, 12,
312 : g_TileClasses.rock, 5,
313 : g_TileClasses.water, 5
314 : ],
315 : "stay": [g_TileClasses.bluff, 6],
316 : "sizes": savanna ? ["big"] : g_AllSizes,
317 : "mixes": savanna ? ["varied"] : g_AllMixes,
318 : "amounts": savanna ? ["tons"] : ["normal", "many", "tons"]
319 : },
320 : {
321 : "func": addAnimals,
322 : "avoid": [
323 : g_TileClasses.animals, 20,
324 : g_TileClasses.forest, 5,
325 : g_TileClasses.mountain, 1,
326 : g_TileClasses.player, 20,
327 : g_TileClasses.rock, 5,
328 : g_TileClasses.metal, 5,
329 : g_TileClasses.water, 3
330 : ],
331 : "stay": [g_TileClasses.bluff, 6],
332 : "sizes": g_AllSizes,
333 : "mixes": g_AllMixes,
334 : "amounts": ["normal", "many", "tons"]
335 : },
336 : {
337 : "func": addBerries,
338 : "avoid": [
339 : g_TileClasses.berries, 50,
340 : g_TileClasses.forest, 5,
341 : g_TileClasses.metal, 10,
342 : g_TileClasses.mountain, 2,
343 : g_TileClasses.player, 20,
344 : g_TileClasses.rock, 10,
345 : g_TileClasses.water, 3
346 : ],
347 : "stay": [g_TileClasses.bluff, 6],
348 : "sizes": g_AllSizes,
349 : "mixes": g_AllMixes,
350 : "amounts": ["normal", "many", "tons"]
351 : }
352 : ]));
353 : }
354 :
355 : /**
356 : * Add grass, rocks and bushes.
357 : */
358 : function addDecoration(constraint, size, deviation, fill)
359 : {
360 0 : g_Map.log("Creating decoration");
361 :
362 0 : var offset = getRandomDeviation(size, deviation);
363 0 : var decorations = [
364 : [
365 : new SimpleObject(g_Decoratives.rockMedium, offset, 3 * offset, 0, offset)
366 : ],
367 : [
368 : new SimpleObject(g_Decoratives.rockLarge, offset, 2 * offset, 0, offset),
369 : new SimpleObject(g_Decoratives.rockMedium, offset, 3 * offset, 0, 2 * offset)
370 : ],
371 : [
372 : new SimpleObject(g_Decoratives.grassShort, offset, 2 * offset, 0, offset)
373 : ],
374 : [
375 : new SimpleObject(g_Decoratives.grass, 2 * offset, 4 * offset, 0, 1.8 * offset),
376 : new SimpleObject(g_Decoratives.grassShort, 3 * offset, 6 * offset, 1.2 * offset, 2.5 * offset)
377 : ],
378 : [
379 : new SimpleObject(g_Decoratives.bushMedium, offset, 2 * offset, 0, 2 * offset),
380 : new SimpleObject(g_Decoratives.bushSmall, 2 * offset, 4 * offset, 0, 2 * offset)
381 : ]
382 : ];
383 :
384 0 : var baseCount = 1;
385 0 : if (currentBiome() == "generic/india")
386 0 : baseCount = 8;
387 :
388 0 : var counts = [
389 : scaleByMapSize(16, 262),
390 : scaleByMapSize(8, 131),
391 : baseCount * scaleByMapSize(13, 200),
392 : baseCount * scaleByMapSize(13, 200),
393 : baseCount * scaleByMapSize(13, 200)
394 : ];
395 :
396 0 : for (var i = 0; i < decorations.length; ++i)
397 : {
398 0 : var decorCount = Math.floor(counts[i] * fill);
399 0 : var group = new SimpleGroup(decorations[i], true);
400 0 : createObjectGroupsDeprecated(group, 0, constraint, decorCount, 5);
401 : }
402 : }
403 :
404 : /**
405 : * Create varying elevations.
406 : *
407 : * @param {Array} constraint - avoid/stay-classes
408 : *
409 : * @param {Object} el - the element to be rendered, for example:
410 : * "class": g_TileClasses.hill,
411 : * "painter": [g_Terrains.mainTerrain, g_Terrains.mainTerrain],
412 : * "size": 1,
413 : * "deviation": 0.2,
414 : * "fill": 1,
415 : * "count": scaleByMapSize(4, 8),
416 : * "minSize": Math.floor(scaleByMapSize(3, 8)),
417 : * "maxSize": Math.floor(scaleByMapSize(5, 10)),
418 : * "spread": Math.floor(scaleByMapSize(10, 20)),
419 : * "minElevation": 6,
420 : * "maxElevation": 12,
421 : * "steepness": 1.5
422 : */
423 :
424 : function addElevation(constraint, el)
425 : {
426 0 : var count = el.fill * el.count;
427 0 : var minSize = el.minSize;
428 0 : var maxSize = el.maxSize;
429 0 : var spread = el.spread;
430 :
431 0 : var elType = ELEVATION_MODIFY;
432 0 : if (el.class == g_TileClasses.water)
433 0 : elType = ELEVATION_SET;
434 :
435 0 : var widths = [];
436 :
437 : // Allow for shore and cliff rendering
438 0 : for (var s = el.painter.length; s > 2; --s)
439 0 : widths.push(1);
440 :
441 0 : for (var i = 0; i < count; ++i)
442 : {
443 0 : var elevation = randIntExclusive(el.minElevation, el.maxElevation);
444 0 : var smooth = Math.floor(elevation / el.steepness);
445 :
446 0 : var offset = getRandomDeviation(el.size, el.deviation);
447 0 : var pMinSize = Math.floor(minSize * offset);
448 0 : var pMaxSize = Math.floor(maxSize * offset);
449 0 : var pSpread = Math.floor(spread * offset);
450 0 : var pSmooth = Math.abs(Math.floor(smooth * offset));
451 0 : var pElevation = Math.floor(elevation * offset);
452 :
453 0 : pElevation = Math.max(el.minElevation, Math.min(pElevation, el.maxElevation));
454 0 : pMinSize = Math.min(pMinSize, pMaxSize);
455 0 : pMaxSize = Math.min(pMaxSize, el.maxSize);
456 0 : pMinSize = Math.max(pMaxSize, el.minSize);
457 0 : pSmooth = Math.max(pSmooth, 1);
458 :
459 0 : createAreas(
460 : new ChainPlacer(pMinSize, pMaxSize, pSpread, 0.5),
461 : [
462 : new LayeredPainter(el.painter, [widths.concat(pSmooth)]),
463 : new SmoothElevationPainter(elType, pElevation, pSmooth),
464 : new TileClassPainter(el.class)
465 : ],
466 : constraint,
467 : 1);
468 : }
469 : }
470 :
471 : /**
472 : * Create rolling hills.
473 : */
474 : function addHills(constraint, size, deviation, fill)
475 : {
476 0 : g_Map.log("Creating hills");
477 :
478 0 : addElevation(constraint, {
479 : "class": g_TileClasses.hill,
480 : "painter": [g_Terrains.mainTerrain, g_Terrains.mainTerrain],
481 : "size": size,
482 : "deviation": deviation,
483 : "fill": fill,
484 : "count": 8,
485 : "minSize": 5,
486 : "maxSize": 8,
487 : "spread": 20,
488 : "minElevation": 6,
489 : "maxElevation": 12,
490 : "steepness": 1.5
491 : });
492 :
493 0 : createArea(
494 : new MapBoundsPlacer(),
495 : new TileClassPainter(g_TileClasses.bluffIgnore),
496 : new NearTileClassConstraint(g_TileClasses.hill, 6));
497 : }
498 :
499 : /**
500 : * Create random lakes with fish in it.
501 : */
502 : function addLakes(constraint, size, deviation, fill)
503 : {
504 0 : g_Map.log("Creating lakes");
505 :
506 0 : var lakeTile = g_Terrains.water;
507 :
508 0 : if (currentBiome() == "generic/temperate" || currentBiome() == "generic/india")
509 0 : lakeTile = g_Terrains.dirt;
510 :
511 0 : if (currentBiome() == "generic/aegean")
512 0 : lakeTile = g_Terrains.tier2Terrain;
513 :
514 0 : if (currentBiome() == "generic/autumn")
515 0 : lakeTile = g_Terrains.shore;
516 :
517 0 : addElevation(constraint, {
518 : "class": g_TileClasses.water,
519 : "painter": [lakeTile, lakeTile],
520 : "size": size,
521 : "deviation": deviation,
522 : "fill": fill,
523 : "count": 6,
524 : "minSize": 7,
525 : "maxSize": 9,
526 : "spread": 70,
527 : "minElevation": -15,
528 : "maxElevation": -2,
529 : "steepness": 1.5
530 : });
531 :
532 0 : addElements([
533 : {
534 : "func": addFish,
535 : "avoid": [
536 : g_TileClasses.fish, 12,
537 : g_TileClasses.hill, 8,
538 : g_TileClasses.mountain, 8,
539 : g_TileClasses.player, 8
540 : ],
541 : "stay": [g_TileClasses.water, 7],
542 : "sizes": g_AllSizes,
543 : "mixes": g_AllMixes,
544 : "amounts": ["normal", "many", "tons"]
545 : }
546 : ]);
547 :
548 0 : var group = new SimpleGroup([new SimpleObject(g_Decoratives.rockMedium, 1, 3, 1, 3)], true, g_TileClasses.dirt);
549 0 : createObjectGroupsDeprecated(group, 0, [stayClasses(g_TileClasses.water, 1), borderClasses(g_TileClasses.water, 4, 3)], 1000, 100);
550 :
551 0 : 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);
552 0 : createObjectGroupsDeprecated(group, 0, [stayClasses(g_TileClasses.water, 2), borderClasses(g_TileClasses.water, 4, 3)], 1000, 100);
553 : }
554 :
555 : /**
556 : * Universal function to create layered patches.
557 : */
558 : function addLayeredPatches(constraint, size, deviation, fill)
559 : {
560 0 : g_Map.log("Creating layered patches");
561 :
562 0 : var minRadius = 1;
563 0 : var maxRadius = Math.floor(scaleByMapSize(3, 5));
564 0 : var count = fill * scaleByMapSize(15, 45);
565 :
566 0 : var patchSizes = [
567 : scaleByMapSize(3, 6),
568 : scaleByMapSize(5, 10),
569 : scaleByMapSize(8, 21)
570 : ];
571 :
572 0 : for (let patchSize of patchSizes)
573 : {
574 0 : var offset = getRandomDeviation(size, deviation);
575 0 : var patchMinRadius = Math.floor(minRadius * offset);
576 0 : var patchMaxRadius = Math.floor(maxRadius * offset);
577 :
578 0 : createAreas(
579 : new ChainPlacer(Math.min(patchMinRadius, patchMaxRadius), patchMaxRadius, Math.floor(patchSize * offset), 0.5),
580 : [
581 : new LayeredPainter(
582 : [
583 : [g_Terrains.mainTerrain, g_Terrains.tier1Terrain],
584 : [g_Terrains.tier1Terrain, g_Terrains.tier2Terrain],
585 : [g_Terrains.tier2Terrain, g_Terrains.tier3Terrain],
586 : [g_Terrains.tier4Terrain]
587 : ],
588 : [1, 1]),
589 : new TileClassPainter(g_TileClasses.dirt)
590 : ],
591 : constraint,
592 : count * offset);
593 : }
594 : }
595 :
596 : /**
597 : * Create steep mountains.
598 : */
599 : function addMountains(constraint, size, deviation, fill)
600 : {
601 0 : g_Map.log("Creating mountains");
602 :
603 0 : addElevation(constraint, {
604 : "class": g_TileClasses.mountain,
605 : "painter": [g_Terrains.cliff, g_Terrains.hill],
606 : "size": size,
607 : "deviation": deviation,
608 : "fill": fill,
609 : "count": 8,
610 : "minSize": 2,
611 : "maxSize": 4,
612 : "spread": 100,
613 : "minElevation": 100,
614 : "maxElevation": 120,
615 : "steepness": 4
616 : });
617 : }
618 :
619 : /**
620 : * Create plateaus.
621 : */
622 : function addPlateaus(constraint, size, deviation, fill)
623 : {
624 0 : g_Map.log("Creating plateaus");
625 :
626 0 : var plateauTile = g_Terrains.dirt;
627 :
628 0 : if (currentBiome() == "generic/arctic")
629 0 : plateauTile = g_Terrains.tier1Terrain;
630 :
631 0 : if (currentBiome() == "generic/alpine" || currentBiome() == "generic/savanna")
632 0 : plateauTile = g_Terrains.tier2Terrain;
633 :
634 0 : if (currentBiome() == "generic/autumn")
635 0 : plateauTile = g_Terrains.tier4Terrain;
636 :
637 0 : addElevation(constraint, {
638 : "class": g_TileClasses.plateau,
639 : "painter": [g_Terrains.cliff, plateauTile],
640 : "size": size,
641 : "deviation": deviation,
642 : "fill": fill,
643 : "count": 15,
644 : "minSize": 2,
645 : "maxSize": 4,
646 : "spread": 200,
647 : "minElevation": 20,
648 : "maxElevation": 30,
649 : "steepness": 8
650 : });
651 :
652 0 : for (var i = 0; i < 40; ++i)
653 : {
654 0 : var hillElevation = randIntInclusive(4, 18);
655 0 : createAreas(
656 : new ChainPlacer(3, 15, 1, 0.5),
657 : [
658 : new LayeredPainter([plateauTile, plateauTile], [3]),
659 : new SmoothElevationPainter(ELEVATION_MODIFY, hillElevation, hillElevation - 2),
660 : new TileClassPainter(g_TileClasses.hill)
661 : ],
662 : [
663 : avoidClasses(g_TileClasses.hill, 7),
664 : stayClasses(g_TileClasses.plateau, 7)
665 : ],
666 : 1);
667 : }
668 :
669 0 : addElements([
670 : {
671 : "func": addDecoration,
672 : "avoid": [
673 : g_TileClasses.dirt, 15,
674 : g_TileClasses.forest, 2,
675 : g_TileClasses.player, 12,
676 : g_TileClasses.water, 3
677 : ],
678 : "stay": [g_TileClasses.plateau, 8],
679 : "sizes": ["normal"],
680 : "mixes": ["normal"],
681 : "amounts": ["tons"]
682 : },
683 : {
684 : "func": addProps,
685 : "avoid": [
686 : g_TileClasses.forest, 2,
687 : g_TileClasses.player, 12,
688 : g_TileClasses.prop, 40,
689 : g_TileClasses.water, 3
690 : ],
691 : "stay": [g_TileClasses.plateau, 8],
692 : "sizes": ["normal"],
693 : "mixes": ["normal"],
694 : "amounts": ["scarce"]
695 : }
696 : ]);
697 : }
698 :
699 : /**
700 : * Place less usual decoratives like barrels or crates.
701 : */
702 : function addProps(constraint, size, deviation, fill)
703 : {
704 0 : g_Map.log("Creating rare actors");
705 :
706 0 : var offset = getRandomDeviation(size, deviation);
707 :
708 0 : var props = [
709 : [
710 : new SimpleObject(g_Props.skeleton, offset, 5 * offset, 0, 3 * offset + 2),
711 : ],
712 : [
713 : new SimpleObject(g_Props.barrels, offset, 2 * offset, 2, 3 * offset + 2),
714 : new SimpleObject(g_Props.cart, 0, offset, 5, 2.5 * offset + 5),
715 : new SimpleObject(g_Props.crate, offset, 2 * offset, 2, 2 * offset + 2),
716 : new SimpleObject(g_Props.well, 0, 1, 2, 2 * offset + 2)
717 : ]
718 : ];
719 :
720 0 : var baseCount = 1;
721 :
722 0 : var counts = [
723 : scaleByMapSize(16, 262),
724 : scaleByMapSize(8, 131),
725 : baseCount * scaleByMapSize(13, 200),
726 : baseCount * scaleByMapSize(13, 200),
727 : baseCount * scaleByMapSize(13, 200)
728 : ];
729 :
730 : // Add small props
731 0 : for (var i = 0; i < props.length; ++i)
732 : {
733 0 : var propCount = Math.floor(counts[i] * fill);
734 0 : var group = new SimpleGroup(props[i], true);
735 0 : createObjectGroupsDeprecated(group, 0, constraint, propCount, 5);
736 : }
737 :
738 : // Add decorative trees
739 0 : var trees = new SimpleObject(g_Decoratives.tree, 5 * offset, 30 * offset, 2, 3 * offset + 10);
740 0 : createObjectGroupsDeprecated(new SimpleGroup([trees], true), 0, constraint, counts[0] * 5 * fill, 5);
741 : }
742 :
743 : function addValleys(constraint, size, deviation, fill, baseHeight)
744 : {
745 0 : if (baseHeight < 6)
746 0 : return;
747 :
748 0 : g_Map.log("Creating valleys");
749 :
750 0 : let minElevation = Math.max(-baseHeight, 1 - baseHeight / (size * (deviation + 1)));
751 :
752 0 : var valleySlope = g_Terrains.tier1Terrain;
753 0 : var valleyFloor = g_Terrains.tier4Terrain;
754 :
755 0 : if (currentBiome() == "generic/sahara")
756 : {
757 0 : valleySlope = g_Terrains.tier3Terrain;
758 0 : valleyFloor = g_Terrains.dirt;
759 : }
760 :
761 0 : if (currentBiome() == "generic/aegean")
762 : {
763 0 : valleySlope = g_Terrains.tier2Terrain;
764 0 : valleyFloor = g_Terrains.dirt;
765 : }
766 :
767 0 : if (currentBiome() == "generic/alpine" || currentBiome() == "generic/savanna")
768 0 : valleyFloor = g_Terrains.tier2Terrain;
769 :
770 0 : if (currentBiome() == "generic/india")
771 0 : valleySlope = g_Terrains.dirt;
772 :
773 0 : if (currentBiome() == "generic/autumn")
774 0 : valleyFloor = g_Terrains.tier3Terrain;
775 :
776 0 : addElevation(constraint, {
777 : "class": g_TileClasses.valley,
778 : "painter": [valleySlope, valleyFloor],
779 : "size": size,
780 : "deviation": deviation,
781 : "fill": fill,
782 : "count": 8,
783 : "minSize": 5,
784 : "maxSize": 8,
785 : "spread": 30,
786 : "minElevation": minElevation,
787 : "maxElevation": -2,
788 : "steepness": 4
789 : });
790 : }
791 :
792 : /**
793 : * Create huntable animals.
794 : */
795 : function addAnimals(constraint, size, deviation, fill)
796 : {
797 0 : g_Map.log("Creating animals");
798 :
799 0 : var groupOffset = getRandomDeviation(size, deviation);
800 :
801 0 : var animals = [
802 : [new SimpleObject(g_Gaia.mainHuntableAnimal, 5 * groupOffset, 7 * groupOffset, 0, 4 * groupOffset)],
803 : [new SimpleObject(g_Gaia.secondaryHuntableAnimal, 2 * groupOffset, 3 * groupOffset, 0, 2 * groupOffset)]
804 : ];
805 :
806 0 : for (let animal of animals)
807 0 : createObjectGroupsDeprecated(
808 : new SimpleGroup(animal, true, g_TileClasses.animals),
809 : 0,
810 : constraint,
811 : Math.floor(30 * fill),
812 : 50);
813 : }
814 :
815 : function addBerries(constraint, size, deviation, fill)
816 : {
817 0 : g_Map.log("Creating berries");
818 :
819 0 : let groupOffset = getRandomDeviation(size, deviation);
820 :
821 0 : createObjectGroupsDeprecated(
822 : new SimpleGroup([new SimpleObject(g_Gaia.fruitBush, 5 * groupOffset, 5 * groupOffset, 0, 3 * groupOffset)], true, g_TileClasses.berries),
823 : 0,
824 : constraint,
825 : Math.floor(50 * fill),
826 : 40);
827 : }
828 :
829 : function addFish(constraint, size, deviation, fill)
830 : {
831 0 : g_Map.log("Creating fish");
832 :
833 0 : var groupOffset = getRandomDeviation(size, deviation);
834 :
835 0 : var fishes = [
836 : [new SimpleObject(g_Gaia.fish, groupOffset, 2 * groupOffset, 0, 2 * groupOffset)],
837 : [new SimpleObject(g_Gaia.fish, 2 * groupOffset, 4 * groupOffset, 10 * groupOffset, 20 * groupOffset)]
838 : ];
839 :
840 0 : for (let fish of fishes)
841 0 : createObjectGroupsDeprecated(
842 : new SimpleGroup(fish, true, g_TileClasses.fish),
843 : 0,
844 : constraint,
845 : Math.floor(40 * fill),
846 : 50);
847 : }
848 :
849 : function addForests(constraint, size, deviation, fill)
850 : {
851 0 : if (currentBiome() == "generic/savanna")
852 0 : return;
853 :
854 0 : g_Map.log("Creating forests");
855 :
856 0 : let treeTypes = [
857 : [
858 : g_Terrains.forestFloor2 + TERRAIN_SEPARATOR + g_Gaia.tree1,
859 : g_Terrains.forestFloor2 + TERRAIN_SEPARATOR + g_Gaia.tree2,
860 : g_Terrains.forestFloor2
861 : ],
862 : [
863 : g_Terrains.forestFloor1 + TERRAIN_SEPARATOR + g_Gaia.tree4,
864 : g_Terrains.forestFloor1 + TERRAIN_SEPARATOR + g_Gaia.tree5,
865 : g_Terrains.forestFloor1
866 : ]
867 : ];
868 :
869 0 : let forestTypes = [
870 : [
871 : [g_Terrains.forestFloor2, g_Terrains.mainTerrain, treeTypes[0]],
872 : [g_Terrains.forestFloor2, treeTypes[0]]
873 : ],
874 : [
875 : [g_Terrains.forestFloor2, g_Terrains.mainTerrain, treeTypes[1]],
876 : [g_Terrains.forestFloor1, treeTypes[1]]],
877 : [
878 : [g_Terrains.forestFloor1, g_Terrains.mainTerrain, treeTypes[0]],
879 : [g_Terrains.forestFloor2, treeTypes[0]]],
880 : [
881 : [g_Terrains.forestFloor1, g_Terrains.mainTerrain, treeTypes[1]],
882 : [g_Terrains.forestFloor1, treeTypes[1]]
883 : ]
884 : ];
885 :
886 0 : for (let forestType of forestTypes)
887 : {
888 0 : let offset = getRandomDeviation(size, deviation);
889 0 : createAreas(
890 : new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5) * offset), Math.floor(50 * offset), 0.5),
891 : [
892 : new LayeredPainter(forestType, [2]),
893 : new TileClassPainter(g_TileClasses.forest)
894 : ],
895 : constraint,
896 : 10 * fill);
897 : }
898 : }
899 :
900 : function addMetal(constraint, size, deviation, fill)
901 : {
902 0 : g_Map.log("Creating metal mines");
903 :
904 0 : var offset = getRandomDeviation(size, deviation);
905 0 : createObjectGroupsDeprecated(
906 : new SimpleGroup([new SimpleObject(g_Gaia.metalLarge, offset, offset, 0, 4 * offset)], true, g_TileClasses.metal),
907 : 0,
908 : constraint,
909 : 1 + 20 * fill,
910 : 100);
911 : }
912 :
913 : function addSmallMetal(constraint, size, mixes, amounts)
914 : {
915 0 : g_Map.log("Creating small metal mines");
916 :
917 0 : let deviation = getRandomDeviation(size, mixes);
918 0 : createObjectGroupsDeprecated(
919 : new SimpleGroup([new SimpleObject(g_Gaia.metalSmall, 2 * deviation, 5 * deviation, deviation, 3 * deviation)], true, g_TileClasses.metal),
920 : 0,
921 : constraint,
922 : 1 + 20 * amounts,
923 : 100);
924 : }
925 :
926 : /**
927 : * Create stone mines.
928 : */
929 : function addStone(constraint, size, deviation, fill)
930 : {
931 0 : g_Map.log("Creating stone mines");
932 :
933 0 : var offset = getRandomDeviation(size, deviation);
934 :
935 0 : var mines = [
936 : [
937 : new SimpleObject(g_Gaia.stoneSmall, 0, 2 * offset, 0, 4 * offset),
938 : new SimpleObject(g_Gaia.stoneLarge, offset, offset, 0, 4 * offset)
939 : ],
940 : [
941 : new SimpleObject(g_Gaia.stoneSmall, 2 * offset, 5 * offset, offset, 3 * offset)
942 : ]
943 : ];
944 :
945 0 : for (let mine of mines)
946 0 : createObjectGroupsDeprecated(
947 : new SimpleGroup(mine, true, g_TileClasses.rock),
948 : 0,
949 : constraint,
950 : 1 + 20 * fill,
951 : 100);
952 : }
953 :
954 : /**
955 : * Create straggler trees.
956 : */
957 : function addStragglerTrees(constraint, size, deviation, fill)
958 : {
959 0 : g_Map.log("Creating straggler trees");
960 :
961 : // Ensure minimum distribution on african biome
962 0 : if (currentBiome() == "generic/savanna")
963 : {
964 0 : fill = Math.max(fill, 2);
965 0 : size = Math.max(size, 1);
966 : }
967 :
968 0 : var trees = [g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree4];
969 :
970 0 : var treesPerPlayer = 40;
971 0 : var playerBonus = Math.max(1, (getNumPlayers() - 3) / 2);
972 :
973 0 : var offset = getRandomDeviation(size, deviation);
974 0 : var treeCount = treesPerPlayer * playerBonus * fill;
975 0 : var totalTrees = scaleByMapSize(treeCount, treeCount);
976 :
977 0 : var count = Math.floor(totalTrees / trees.length) * fill;
978 0 : var min = offset;
979 0 : var max = 4 * offset;
980 0 : var minDist = offset;
981 0 : var maxDist = 5 * offset;
982 :
983 : // More trees for the african biome
984 0 : if (currentBiome() == "generic/savanna")
985 : {
986 0 : min = 3 * offset;
987 0 : max = 5 * offset;
988 0 : minDist = 2 * offset + 1;
989 0 : maxDist = 3 * offset + 2;
990 : }
991 :
992 0 : for (var i = 0; i < trees.length; ++i)
993 : {
994 0 : var treesMax = max;
995 :
996 : // Don't clump fruit trees
997 0 : if (i == 2 && (currentBiome() == "generic/sahara" || currentBiome() == "generic/aegean"))
998 0 : treesMax = 1;
999 :
1000 0 : min = Math.min(min, treesMax);
1001 :
1002 0 : var group = new SimpleGroup([new SimpleObject(trees[i], min, treesMax, minDist, maxDist)], true, g_TileClasses.forest);
1003 0 : createObjectGroupsDeprecated(group, 0, constraint, count);
1004 : }
1005 : }
1006 :
1007 : /**
1008 : * Determine if the endline of the bluff is within the tilemap.
1009 : */
1010 : function isBluffPassable(bluffArea, baseLine, endLine)
1011 : {
1012 0 : if (!baseLine ||
1013 : !endLine ||
1014 : !g_Map.validTilePassable(endLine.start) &&
1015 : !g_Map.validTilePassable(endLine.end))
1016 0 : return false;
1017 :
1018 0 : let minTilesInGroup = 2;
1019 0 : let insideBluff = false;
1020 0 : let outsideBluff = false;
1021 :
1022 : // If there aren't enough points in each row
1023 0 : let corners = getBoundingBox(bluffArea.getPoints());
1024 0 : for (let x = corners.min.x; x <= corners.max.x; ++x)
1025 : {
1026 0 : let count = 0;
1027 0 : for (let y = corners.min.y; y <= corners.max.y; ++y)
1028 : {
1029 0 : let pos = new Vector2D(x, y);
1030 0 : if (!bluffArea.contains(pos))
1031 0 : continue;
1032 :
1033 0 : let valid = g_Map.validTilePassable(pos);
1034 0 : if (valid)
1035 0 : ++count;
1036 :
1037 0 : if (valid)
1038 0 : insideBluff = true;
1039 :
1040 0 : if (outsideBluff && valid)
1041 0 : return false;
1042 : }
1043 :
1044 : // We're expecting the end of the bluff
1045 0 : if (insideBluff && count < minTilesInGroup)
1046 0 : outsideBluff = true;
1047 : }
1048 :
1049 0 : insideBluff = false;
1050 0 : outsideBluff = false;
1051 :
1052 : // If there aren't enough points in each column
1053 0 : for (let y = corners.min.y; y <= corners.max.y; ++y)
1054 : {
1055 0 : let count = 0;
1056 0 : for (let x = corners.min.x; x <= corners.max.x; ++x)
1057 : {
1058 0 : let pos = new Vector2D(x, y);
1059 0 : if (!bluffArea.contains(pos))
1060 0 : continue;
1061 :
1062 0 : let valid = g_Map.validTilePassable(pos.add(corners.min));
1063 0 : if (valid)
1064 0 : ++count;
1065 :
1066 0 : if (valid)
1067 0 : insideBluff = true;
1068 :
1069 0 : if (outsideBluff && valid)
1070 0 : return false;
1071 : }
1072 :
1073 : // We're expecting the end of the bluff
1074 0 : if (insideBluff && count < minTilesInGroup)
1075 0 : outsideBluff = true;
1076 : }
1077 :
1078 0 : return true;
1079 : }
1080 :
1081 : /**
1082 : * Find a 45 degree line that does not intersect with the bluff.
1083 : */
1084 : function findClearLine(bluffArea, angle)
1085 : {
1086 0 : let corners = getBoundingBox(bluffArea.getPoints());
1087 :
1088 : // Angle - 0: northwest; 1: northeast; 2: southeast; 3: southwest
1089 : let offset;
1090 : let y;
1091 0 : switch (angle)
1092 : {
1093 : case 0:
1094 0 : offset = new Vector2D(-1, -1);
1095 0 : y = corners.max.y;
1096 0 : break;
1097 : case 1:
1098 0 : offset = new Vector2D(1, -1);
1099 0 : y = corners.max.y;
1100 0 : break;
1101 : case 2:
1102 0 : offset = new Vector2D(1, 1);
1103 0 : y = corners.min.y;
1104 0 : break;
1105 : case 3:
1106 0 : offset = new Vector2D(-1, 1);
1107 0 : y = corners.min.y;
1108 0 : break;
1109 : default:
1110 0 : throw new Error("Unknown angle " + angle);
1111 : }
1112 :
1113 : let clearLine;
1114 0 : for (let x = corners.min.x; x <= corners.max.x; ++x)
1115 : {
1116 0 : let start = new Vector2D(x, y);
1117 :
1118 0 : let intersectsBluff = false;
1119 0 : let end = start.clone();
1120 :
1121 0 : while (end.x >= corners.min.x && end.x <= corners.max.x && end.y >= corners.min.y && end.y <= corners.max.y)
1122 : {
1123 0 : if (bluffArea.contains(end) && g_Map.validTilePassable(end))
1124 : {
1125 0 : intersectsBluff = true;
1126 0 : break;
1127 : }
1128 0 : end.add(offset);
1129 : }
1130 :
1131 0 : if (!intersectsBluff)
1132 0 : clearLine = {
1133 : "start": start,
1134 : "end": end.sub(offset)
1135 : };
1136 :
1137 0 : if (intersectsBluff ? (angle == 0 || angle == 3) : (angle == 1 || angle == 2))
1138 0 : break;
1139 : }
1140 :
1141 0 : return clearLine;
1142 : }
1143 :
1144 : /**
1145 : * Returns a number within a random deviation of a base number.
1146 : */
1147 : function getRandomDeviation(base, deviation)
1148 : {
1149 0 : return base + randFloat(-1, 1) * Math.min(base, deviation);
1150 : }
|