Add randomAngle helper function to abbreviate calls.
[0ad.git] / binaries / data / mods / public / maps / random / island_stronghold.js
blobb377bb537b17b727cd960df807434a3c538f37af
1 /**
2  * Returns starting position in tile coordinates for the given player.
3  */
4 function getPlayerTileCoordinates(playerIdx, teamIdx, fractionX, fractionZ)
6         let playerAngle = startAngle + (playerIdx+1) * 2 * Math.PI / teams[teamIdx].length;
8         let fx = fractionToTiles(fractionX + 0.05 * Math.cos(playerAngle));
9         let fz = fractionToTiles(fractionZ + 0.05 * Math.sin(playerAngle));
11         return [playerAngle, fx, fz, Math.round(fx), Math.round(fz)];
14 Engine.LoadLibrary("rmgen");
15 Engine.LoadLibrary("rmgen2");
16 Engine.LoadLibrary("rmbiome");
17 Engine.LoadLibrary("heightmap");
19 const g_InitialMines = 1;
20 const g_InitialMineDistance = 14;
21 const g_InitialTrees = 50;
23 setSelectedBiome();
25 const tMainTerrain = g_Terrains.mainTerrain;
26 const tForestFloor1 = g_Terrains.forestFloor1;
27 const tForestFloor2 = g_Terrains.forestFloor2;
28 const tCliff = g_Terrains.cliff;
29 const tTier1Terrain = g_Terrains.tier1Terrain;
30 const tTier2Terrain = g_Terrains.tier2Terrain;
31 const tTier3Terrain = g_Terrains.tier3Terrain;
32 const tHill = g_Terrains.hill;
33 const tTier4Terrain = g_Terrains.tier4Terrain;
34 const tShore = g_Terrains.shore;
35 const tWater = g_Terrains.water;
37 const oTree1 = g_Gaia.tree1;
38 const oTree2 = g_Gaia.tree2;
39 const oTree3 = g_Gaia.tree3;
40 const oTree4 = g_Gaia.tree4;
41 const oTree5 = g_Gaia.tree5;
42 const oFruitBush = g_Gaia.fruitBush;
43 const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal;
44 const oFish = g_Gaia.fish;
45 const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal;
46 const oStoneLarge = g_Gaia.stoneLarge;
47 const oStoneSmall = g_Gaia.stoneSmall;
48 const oMetalLarge = g_Gaia.metalLarge;
49 const oWhale = "gaia/fauna_whale_humpback";
50 const oShipwreck = "other/special_treasure_shipwreck";
51 const oShipDebris = "other/special_treasure_shipwreck_debris";
52 const oObelisk = "other/obelisk";
54 const aGrass = g_Decoratives.grass;
55 const aGrassShort = g_Decoratives.grassShort;
56 const aRockLarge = g_Decoratives.rockLarge;
57 const aRockMedium = g_Decoratives.rockMedium;
59 const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2];
60 const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1];
62 InitMap();
64 const numPlayers = getNumPlayers();
65 const mapSize = getMapSize();
67 const clPlayer = createTileClass();
68 const clHill = createTileClass();
69 const clForest = createTileClass();
70 const clDirt = createTileClass();
71 const clRock = createTileClass();
72 const clMetal = createTileClass();
73 const clFood = createTileClass();
74 const clBaseResource = createTileClass();
75 const clLand = createTileClass();
77 const shoreRadius = 6;
78 const landHeight = 3;
80 initTerrain(tWater);
82 var startAngle = randomAngle();
84 var teams = getTeamsArray();
85 var numTeams = teams.filter(team => team).length;
87 var teamNo = 0;
88 for (let i = 0; i < teams.length; ++i)
90         if (!teams[i] || isNomad())
91                 continue;
93         ++teamNo;
94         let teamAngle = startAngle + teamNo * 2 * Math.PI / numTeams;
95         let fractionX = 0.5 + 0.3 * Math.cos(teamAngle);
96         let fractionZ = 0.5 + 0.3 * Math.sin(teamAngle);
97         let teamX = fractionToTiles(fractionX);
98         let teamZ = fractionToTiles(fractionZ);
100         log("Creating island and starting entities for team " + i);
101         for (let p = 0; p < teams[i].length; ++p)
102         {
103                 let [playerAngle, fx, fz, ix, iz] = getPlayerTileCoordinates(p, i, fractionX, fractionZ);
105                 addCivicCenterAreaToClass(ix, iz, clPlayer);
107                 createArea(
108                         new ChainPlacer(2, Math.floor(scaleByMapSize(5, 11)), Math.floor(scaleByMapSize(60, 250)), 1, ix, iz, 0, [Math.floor(mapSize * 0.01)]),
109                         [
110                                 new LayeredPainter([tMainTerrain, tMainTerrain, tMainTerrain], [1, shoreRadius]),
111                                 new SmoothElevationPainter(ELEVATION_SET, landHeight, shoreRadius),
112                                 paintClass(clLand)
113                         ],
114                         null);
116                 placeCivDefaultStartingEntities(fx, fz, teams[i][p], false);
117         }
119         log("Create initial mines for team " + i);
120         for (let p = 0; p < teams[i].length; ++p)
121         {
122                 let [playerAngle, fx, fz, ix, iz] = getPlayerTileCoordinates(p, i, fractionX, fractionZ);
123                 let mAngle = randFloat(playerAngle - Math.PI / teams[i].length, playerAngle + Math.PI / teams[i].length);
125                 // Metal
126                 let mX = Math.round(fx + g_InitialMineDistance * Math.cos(mAngle));
127                 let mZ = Math.round(fz + g_InitialMineDistance * Math.sin(mAngle));
128                 let group = new SimpleGroup(
129                         [new SimpleObject(oMetalLarge, g_InitialMines, g_InitialMines, 0, 4)],
130                         true, clBaseResource, mX, mZ
131                 );
132                 createObjectGroup(group, 0, [avoidClasses(clBaseResource, 2, clPlayer, 4), stayClasses(clLand, 2)]);
134                 // Stone
135                 let sX = Math.round(fx + g_InitialMineDistance * Math.cos(mAngle + Math.PI / 4));
136                 let sZ = Math.round(fz + g_InitialMineDistance * Math.sin(mAngle + Math.PI / 4));
137                 group = new SimpleGroup(
138                         [new SimpleObject(oStoneLarge, g_InitialMines, g_InitialMines, 0, 4)],
139                         true, clBaseResource, sX, sZ
140                 );
141                 createObjectGroup(group, 0, [avoidClasses(clBaseResource, 2, clPlayer, 4), stayClasses(clLand, 2)]);
142         }
144         log("Place initial trees and animals for team " + i);
145         for (let p = 0; p < teams[i].length; ++p)
146         {
147                 let [playerAngle, fx, fz, ix, iz] = getPlayerTileCoordinates(p, i, fractionX, fractionZ);
149                 placePlayerBaseChicken({
150                         "playerID": teams[i][p],
151                         "playerX": tilesToFraction(fx),
152                         "playerZ": tilesToFraction(fz),
153                         "BaseResourceClass": clBaseResource,
154                         "baseResourceConstraint": stayClasses(clLand, 5)
155                 });
157                 // create initial berry bushes
158                 let bbAngle = Math.PI * randFloat(1, 1.5);
159                 let bbDist = 10;
160                 let bbX = Math.round(fx + bbDist * Math.cos(bbAngle));
161                 let bbZ = Math.round(fz + bbDist * Math.sin(bbAngle));
162                 let group = new SimpleGroup(
163                         [new SimpleObject(oFruitBush, 5, 5, 0, 3)],
164                         true, clBaseResource, bbX, bbZ
165                 );
166                 createObjectGroup(group, 0, [avoidClasses(clBaseResource, 4, clPlayer, 4), stayClasses(clLand, 5)]);
168                 // create initial trees
169                 let tries = 10;
170                 let tDist = 16;
171                 for (let x = 0; x < tries; ++x)
172                 {
173                         let tAngle = playerAngle + randFloat(-1, 1) * 2 * Math.PI / teams[i].length;
174                         let tX = Math.round(fx + tDist * Math.cos(tAngle));
175                         let tZ = Math.round(fz + tDist * Math.sin(tAngle));
177                         group = new SimpleGroup(
178                                 [new SimpleObject(oTree2, g_InitialTrees, g_InitialTrees, 0, 7)],
179                                 true, clBaseResource, tX, tZ
180                         );
181                         if (createObjectGroup(group, 0, [avoidClasses(clBaseResource, 4, clPlayer, 4), stayClasses(clLand, 4)]))
182                                 break;
183                 }
185                 // create huntable animals
186                 group = new SimpleGroup(
187                         [new SimpleObject(oMainHuntableAnimal, 2 * numPlayers / numTeams, 2 * numPlayers / numTeams, 0, Math.floor(mapSize * 0.2))],
188                         true, clBaseResource, teamX, teamZ
189                 );
190                 createObjectGroup(group, 0, [avoidClasses(clBaseResource, 2, clPlayer, 10), stayClasses(clLand, 5)]);
191                 group = new SimpleGroup(
192                         [new SimpleObject(oSecondaryHuntableAnimal, 4 * numPlayers / numTeams, 4 * numPlayers / numTeams, 0, Math.floor(mapSize * 0.2))],
193                         true, clBaseResource, teamX, teamZ
194                 );
195                 createObjectGroup(group, 0, [avoidClasses(clBaseResource, 2, clPlayer, 10), stayClasses(clLand, 5)]);
196         }
199 Engine.SetProgress(40);
201 log("Creating expansion islands...");
202 var landAreas = [];
203 var playerConstraint = new AvoidTileClassConstraint(clPlayer, Math.floor(scaleByMapSize(12, 16)));
204 var landConstraint = new AvoidTileClassConstraint(clLand, Math.floor(scaleByMapSize(12, 16)));
206 for (let x = 0; x < mapSize; ++x)
207         for (let z = 0; z < mapSize; ++z)
208                 if (playerConstraint.allows(x, z) && landConstraint.allows(x, z))
209                         landAreas.push([x, z]);
211 log("Creating big islands...");
212 let numIslands = scaleByMapSize(4, 14) * (isNomad() ? 2 : 1);
213 for (let i = 0; i < numIslands; ++i)
215         let landAreaLen = landAreas.length;
216         if (!landAreaLen)
217                 break;
219         let chosenPoint = pickRandom(landAreas);
221         let newIsland = createAreas(
222                 new ChainPlacer(
223                         Math.floor(scaleByMapSize(4, 8) * (isNomad() ? 2 : 1)),
224                         Math.floor(scaleByMapSize(8, 16) * (isNomad() ? 2 : 1)),
225                         Math.floor(scaleByMapSize(25, 60)),
226                         0.07,
227                         chosenPoint[0],
228                         chosenPoint[1],
229                         scaleByMapSize(30, 70)),
230                 [
231                         new LayeredPainter([tMainTerrain, tMainTerrain], [2]),
232                         new SmoothElevationPainter(ELEVATION_SET, landHeight, 6),
233                         paintClass(clLand)
234                 ],
235                 avoidClasses(clLand, 3, clPlayer, 3),
236                 1, 1
237         );
239         if (!newIsland || !newIsland.length)
240                 continue;
242         let n = 0;
243         for (let j = 0; j < landAreaLen; ++j)
244         {
245                 let x = landAreas[j][0];
246                 let z = landAreas[j][1];
248                 if (playerConstraint.allows(x, z) && landConstraint.allows(x, z))
249                         landAreas[n++] = landAreas[j];
250         }
251         landAreas.length = n;
254 playerConstraint = new AvoidTileClassConstraint(clPlayer, Math.floor(scaleByMapSize(9, 12)));
255 landConstraint = new AvoidTileClassConstraint(clLand, Math.floor(scaleByMapSize(9, 12)));
257 log("Creating small islands...");
258 numIslands = scaleByMapSize(6, 18) * scaleByMapSize(1, 3);
259 for (let i = 0; i < numIslands; ++i)
261         let landAreaLen = landAreas.length;
262         if (!landAreaLen)
263                 break;
265         let chosenPoint = pickRandom(landAreas);
266         let newIsland = createAreas(
267                 new ChainPlacer(Math.floor(scaleByMapSize(4, 7)), Math.floor(scaleByMapSize(7, 10)), Math.floor(scaleByMapSize(16, 40)), 0.07, chosenPoint[0], chosenPoint[1], scaleByMapSize(22, 40)),
268                 [
269                         new LayeredPainter([tMainTerrain, tMainTerrain], [2]),
270                         new SmoothElevationPainter(ELEVATION_SET, landHeight, 6),
271                         paintClass(clLand)
272                 ],
273                 avoidClasses(clLand, 3, clPlayer, 3),
274                 1,
275                 1);
277         if (newIsland === undefined)
278                 continue;
280         let temp = [];
281         for (let j = 0; j < landAreaLen; ++j)
282         {
283                 let x = landAreas[j][0];
284                 let z = landAreas[j][1];
286                 if (playerConstraint.allows(x, z) && landConstraint.allows(x, z))
287                         temp.push([x, z]);
288         }
289         landAreas = temp;
292 Engine.SetProgress(70);
294 log("Smoothing heightmap...");
295 for (let i = 0; i < 5; ++i)
296         globalSmoothHeightmap();
298 // repaint clLand to compensate for smoothing
299 unPaintTileClassBasedOnHeight(-10, 10, 3, clLand);
300 paintTileClassBasedOnHeight(0, 5, 3, clLand);
302 Engine.SetProgress(85);
304 createBumps(avoidClasses(clPlayer, 20));
306 createMines(
308         [new SimpleObject(oMetalLarge, 1, 1, 3, (numPlayers * 2) + 1)]
310 [avoidClasses(clForest, 1, clPlayer, 40, clRock, 20), stayClasses(clLand, 4)],
311 clMetal
314 createMines(
316         [new SimpleObject(oStoneLarge, 1, 1, 3, (numPlayers * 2) + 1)], [new SimpleObject(oStoneSmall, 2, 2, 2, (numPlayers * 2) + 1)]
318 [avoidClasses(clForest, 1, clPlayer, 40, clMetal, 20), stayClasses(clLand, 4)],
319 clRock
322 var [forestTrees, stragglerTrees] = getTreeCounts(...rBiomeTreeCount(1));
323 createForests(
324  [tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2],
325  [avoidClasses(clPlayer, 10, clForest, 20, clBaseResource, 5, clRock, 6, clMetal, 6), stayClasses(clLand, 3)],
326  clForest,
327  forestTrees);
329 log("Creating hills...");
330 createAreas(
331         new ChainPlacer(1, Math.floor(scaleByMapSize(4, 6)), Math.floor(scaleByMapSize(16, 40)), 0.5),
332         [
333                 new LayeredPainter([tCliff, tHill], [2]),
334                 new SmoothElevationPainter(ELEVATION_SET, 18, 2),
335                 paintClass(clHill)
336         ],
337         [avoidClasses(clBaseResource, 20, clHill, 15, clRock, 6, clMetal, 6), stayClasses(clLand, 0)],
338         scaleByMapSize(4, 13)
341 for (let i = 0; i < 3; ++i)
342         globalSmoothHeightmap();
344 createStragglerTrees(
345         [oTree1, oTree2, oTree4, oTree3],
346         [
347                 avoidClasses(clForest, 10, clPlayer, 20, clMetal, 6, clRock, 6, clHill, 1),
348                 stayClasses(clLand, 4)
349         ],
350         clForest,
351         stragglerTrees);
353 createFood(
354         [
355                 [new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)],
356                 [new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)]
357         ],
358         [3 * numPlayers, 3 * numPlayers],
359         [avoidClasses(clForest, 0, clPlayer, 20, clHill, 1, clRock, 6, clMetal, 6), stayClasses(clLand, 2)],
360         clFood);
362 createFood(
363         [
364                 [new SimpleObject(oFruitBush, 5, 7, 0, 4)]
365         ],
366         [3 * numPlayers],
367         [avoidClasses(clForest, 0, clPlayer, 15, clHill, 1, clFood, 4, clRock, 6, clMetal, 6), stayClasses(clLand, 2)],
368         clFood);
370 if (currentBiome() == "desert")
372         log("Creating obelisks");
373         let group = new SimpleGroup(
374                 [new SimpleObject(oObelisk, 1, 1, 0, 1)],
375                 true
376         );
377         createObjectGroupsDeprecated(
378                 group, 0,
379                 [avoidClasses(clBaseResource, 0, clHill, 0, clRock, 0, clMetal, 0, clFood, 0), stayClasses(clLand, 1)],
380                 scaleByMapSize(3, 8), 1000
381         );
384 log("Creating dirt patches...");
385 let numb = currentBiome() == "savanna" ? 3 : 1;
386 for (let size of [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)])
387         createAreas(
388                 new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, 0.5),
389                 [
390                         new LayeredPainter([[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], [1, 1]),
391                         paintClass(clDirt)
392                 ],
393                 [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 0), stayClasses(clLand, 4)],
394                 numb*scaleByMapSize(15, 45));
396 log("Creating grass patches...");
397 for (let size of [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)])
398         createAreas(
399                 new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, 0.5),
400                 new TerrainPainter(tTier4Terrain),
401                 [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 0), stayClasses(clLand, 4)],
402                 numb * scaleByMapSize(15, 45));
404 log("Creating small decorative rocks...");
405 let group = new SimpleGroup(
406         [new SimpleObject(aRockMedium, 1, 3, 0, 1)],
407         true
409 createObjectGroupsDeprecated(
410         group, 0,
411         [avoidClasses(clForest, 0, clHill, 0), stayClasses(clLand, 2)],
412         scaleByMapSize(16, 262), 50
415 log("Creating large decorative rocks...");
416 group = new SimpleGroup(
417         [new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)],
418         true
420 createObjectGroupsDeprecated(
421         group, 0,
422         [avoidClasses(clForest, 0, clHill, 0), stayClasses(clLand, 2)],
423         scaleByMapSize(8, 131), 50
426 log("Creating fish...");
427 group = new SimpleGroup(
428         [new SimpleObject(oFish, 2, 3, 0, 2)],
429         true, clFood
431 createObjectGroupsDeprecated(group, 0,
432         avoidClasses(clLand, 4, clFood, 20),
433         25 * numPlayers, 60
436 log("Creating Whales...");
437 group = new SimpleGroup(
438         [new SimpleObject(oWhale, 1, 1, 0, 3)],
439         true, clFood
441 createObjectGroupsDeprecated(group, 0,
442         [avoidClasses(clLand, 4),avoidClasses(clFood, 8)],
443         scaleByMapSize(5, 20), 100
446 log("Creating shipwrecks...");
447 group = new SimpleGroup(
448         [new SimpleObject(oShipwreck, 1, 1, 0, 1)],
449         true, clFood
451 createObjectGroupsDeprecated(group, 0,
452         [avoidClasses(clLand, 4),avoidClasses(clFood, 8)],
453         scaleByMapSize(12, 16), 100
456 log("Creating shipwreck debris...");
457 group = new SimpleGroup(
458         [new SimpleObject(oShipDebris, 1, 1, 0, 1)],
459         true, clFood
461 createObjectGroupsDeprecated(group, 0,
462         [avoidClasses(clLand, 4),avoidClasses(clFood, 8)],
463         scaleByMapSize(10, 20), 100
466 log("Creating small grass tufts...");
467 let planetm = currentBiome() == "tropic" ? 8 : 1;
468 group = new SimpleGroup(
469         [new SimpleObject(aGrassShort, 1, 2, 0, 1, -Math.PI / 8, Math.PI / 8)]
471 createObjectGroupsDeprecated(group, 0,
472         [avoidClasses(clHill, 2, clPlayer, 2, clDirt, 0), stayClasses(clLand, 3)],
473         planetm * scaleByMapSize(13, 200)
476 Engine.SetProgress(95);
478 log("Creating large grass tufts...");
479 group = new SimpleGroup(
480         [new SimpleObject(aGrass, 2, 4, 0, 1.8, -Math.PI / 8, Math.PI / 8), new SimpleObject(aGrassShort, 3, 6, 1.2,2.5, -Math.PI / 8, Math.PI / 8)]
482 createObjectGroupsDeprecated(group, 0,
483         [avoidClasses(clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), stayClasses(clLand, 5)],
484         planetm * scaleByMapSize(13, 200)
487 paintTerrainBasedOnHeight(1, 2, 0, tShore);
488 paintTerrainBasedOnHeight(getMapBaseHeight(), 1, 3, tWater);
490 placePlayersNomad(clPlayer, [stayClasses(clLand, 4), avoidClasses(clHill, 2, clForest, 1, clMetal, 4, clRock, 4, clFood, 2)]);
492 setSkySet(pickRandom(["cloudless", "cumulus", "overcast"]));
493 setSunRotation(randomAngle());
494 setSunElevation(randFloat(1/5, 1/3) * Math.PI);
495 setWaterWaviness(2);
497 Engine.SetProgress(100);
499 ExportMap();