]> git.lizzy.rs Git - dragonblocks.git/blob - engine/mapgen.js
8aeba2d212d0f92e10c24ead56f1cb1643fbcbf2
[dragonblocks.git] / engine / mapgen.js
1 /*
2  * mapgen.js
3  *
4  * Copyright 2020 Elias Fleckenstein <eliasfleckenstein@web.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  *
21  *
22  */
23
24 dragonblocks.mapgen = {};
25
26 dragonblocks.mapgen.addStructure = (name, msg, pos) => {
27         let strs = dragonblocks.world.structures;
28         strs[name] = strs[name] || [];
29         strs[name].push({msg: msg, pos: pos});
30 };
31
32 dragonblocks.mapgen.biomes = [];
33 dragonblocks.registerBiome = obj => {
34         dragonblocks.mapgen.biomes.push(obj);
35 };
36
37 dragonblocks.mapgen.materials = [];
38 dragonblocks.registerMaterial = obj => {
39         dragonblocks.mapgen.materials.push(obj);
40 };
41
42 dragonblocks.mapgen.ores = [];
43 dragonblocks.registerOre = obj => {
44         dragonblocks.mapgen.ores.push(obj);
45 };
46
47 dragonblocks.mapgen.selected = "empty";
48 dragonblocks.mapgen.list = {};
49
50 dragonblocks.generateMap = _ => {
51         dragonblocks.mapgen.list[dragonblocks.mapgen.selected]();
52 };
53
54 dragonblocks.mapgen.list["v3"] = _ => {
55         // Localize variables
56
57         let int = parseInt;
58
59         let snode = dragonblocks.setNode;
60         let gnode = dragonblocks.getNode;
61
62         let pm = dragonblocks.PixelManipulator;
63
64         let rand = dblib.random;
65
66         let height = dragonblocks.map.height;
67         let width = dragonblocks.map.width;
68
69         // Biomes
70
71         let biomeList = dragonblocks.mapgen.biomes;
72         let biomes = [];
73         let biomeBorders = [];
74
75         {
76                 for (let d = 0; d < width;) {
77                         let max = 0;
78
79                         for (let biome of biomeList)
80                                 max += biome.probability;
81
82                         let r = rand(0, max);
83
84                         for (let i in biomeList) {
85                                 r -= biomeList[i].probability;
86
87                                 if (r <= 0) {
88                                         biomeBorders.push(d);
89
90                                         let border = Math.min(d + rand(biomeList[i].size[0], biomeList[i].size[1]), width);
91
92                                         dragonblocks.mapgen.addStructure(biomeList[i].name, "(" + d + " - " + border + ", *)", {x: int((d + border)/2), y: 5});
93
94                                         for(; d < border; d++)
95                                                 biomes.push(i);
96
97                                         break;
98                                 }
99                         }
100                 }
101         }
102
103         // Terrain shape
104
105         let ground = [];
106
107         {
108                 let levels = [
109                         {
110                                 name: "mountain_up",
111                                 probability: 3,
112                                 rise: [-3, -2, -2, -2, -1, -1, -1],
113                                 size: 15,
114                                 minsize: 15,
115                                 high: 10,
116                                 next: "mountain_down",
117                         },
118                         {
119                                 name: "mountain_down",
120                                 probability: 0,
121                                 rise: [3, 2, 2, 2, 1, 1, 1],
122                                 size: 15,
123                                 minsize: 15,
124                                 low: 50,
125                         },
126                         {
127                                 name: "hill_up",
128                                 probability: 4,
129                                 rise: [0, 0, 0, 0, 0, -1],
130                                 size: 20,
131                                 minsize: 0,
132                                 high: 20,
133                         },
134                         {
135                                 name: "hill_down",
136                                 probability: 4,
137                                 rise: [0, 0, 0, 0, 0, 1],
138                                 size: 20,
139                                 minsize: 0,
140                                 low: 50,
141                         },
142                         {
143                                 name: "ocean_border_start",
144                                 probability: 3,
145                                 rise: [1, 1, 1, 0],
146                                 size: 0,
147                                 minsize: 100,
148                                 low: 60,
149                                 next: "ocean",
150                         },
151                         {
152                                 name: "ocean",
153                                 probability: 0,
154                                 rise: [0, 0, 0, 0, 0, 0, 0, 1, -1],
155                                 size: 5,
156                                 minsize: 20,
157                                 high: 50,
158                                 next: "ocean_border_end",
159                         },
160                         {
161                                 name: "ocean_border_end",
162                                 probability: 0,
163                                 rise: [-1, -1, -1, 0],
164                                 size: 10,
165                                 minsize: 10,
166                         },
167                         {
168                                 name: "flat",
169                                 probability: 8,
170                                 rise: [0],
171                                 size: 10,
172                                 minsize: 0,
173                                 low: 50,
174                                 high: 30,
175                         }
176                 ];
177
178                 let maxprob = 0;
179
180                 for (let lvl of levels) {
181                         levels[lvl.name] = lvl;
182                         maxprob += lvl.probability;
183                 }
184
185                 let level = levels.flat;
186                 let leftsize = level.minsize;
187                 let groundLast = 40 * height / 100;
188                 let structAdded = false;
189
190                 for (let x = 0; x < width; x++){
191                         if (level.high && level.high * height / 100 > groundLast || level.low && level.low * height / 100 < groundLast || leftsize <= 0 && rand(0, level.size) == 0) {
192                                 if (! structAdded) {
193                                         let start = x - level.minsize + leftsize;
194                                         let end = x;
195
196                                         let gx = int((start + end) / 2);
197                                         let gy = ground[gx] - 3;
198
199                                         dragonblocks.mapgen.addStructure(level.name, "(" + start + " - " + end + ", *)", {x: gx, y: gy});
200
201                                         structAdded = true;
202                                 }
203
204                                 if (level.next) {
205                                         level = levels[level.next];
206                                 } else {
207                                         let r = rand(0, maxprob);
208
209                                         for (let lvl of levels) {
210                                                 r -= lvl.probability;
211
212                                                 if (r <= 0) {
213                                                         level = lvl;
214                                                         break;
215                                                 }
216                                         }
217                                 }
218
219                                 leftsize = level.minsize;
220                                 x--;
221                                 continue;
222                         }
223
224                         structAdded = false;
225                         leftsize--;
226                         groundLast += level.rise[rand(0, level.rise.length - 1)];
227
228                         ground[x] = groundLast;
229                 }
230         }
231
232         // Ores
233
234         {
235                 let setOre = (x, y, ore) => {
236                         if (! ground[x] || y < ground[x] || (y / height  * 100 - 50) < ore.deep)
237                                 return false;
238
239                         snode(x, y, ore.name);
240
241                         return true;
242                 };
243
244                 for (let x = 0; x < width; x++) {
245                         let y, g;
246
247                         y = g = ground[x];
248
249                         let biome = biomeList[biomes[x]];
250
251                         for (; y < g + 1; y++)
252                                 snode(x, y, biome.surface);
253
254                         for (; y < g + 5; y++)
255                                 snode(x, y, biome.ground);
256
257                         for (; y < height; y++) {
258                                 snode(x, y, biome.underground);
259
260                                 for (let ore of dragonblocks.mapgen.ores) {
261                                         if (dblib.random(0, ore.factor) == 0) {
262                                                 if (setOre(x, y, ore))
263                                                         dragonblocks.mapgen.addStructure(ore.name, "(" + x + ", " + y + ")", {x: x, y: y});
264
265                                                 for (let i = 0; i < ore.clustersize; i++)
266                                                         setOre(x + dblib.random(-2, 2), y + dblib.random(-2, 2), ore);
267                                         }
268                                 }
269                         }
270                 }
271         }
272
273         // Water
274
275         let water = [];
276
277         {
278                 let top, bottom, start;
279
280                 top = int(height / 2);
281
282                 for (let x = 0; x < width; x++) {
283                         let biome = biomeList[biomes[x]];
284                         if (ground[x] > top) {
285                                 start = start || x;
286                                 water[x] = true;
287
288                                 snode(x, top, biome.watertop);
289
290                                 let y = top + 1;
291
292                                 for(; y < ground[x]; y++)
293                                         snode(x, y, biome.water);
294
295                                 for(; y < ground[x] + 5; y++)
296                                         snode(x, y, biome.floor);
297                         } else if (start) {
298                                 let end = x - 1;
299                                 dragonblocks.mapgen.addStructure("water", "(" + start + " - " + end + ", " + top + ")", {x: int((start + end) / 2), y: top});
300                                 start = 0;
301                         }
302                 }
303
304                 if (start) {
305                         let end = width;
306                         dragonblocks.mapgen.addStructure("water", "(" + start + " - " + end + ", " + top + ")", {x: int((start + end) / 2), y: top});
307                 }
308         }
309
310         // Trees
311
312         {
313                 let nextTree = 0;
314
315                 for (let x = 0; x < width; x++) {
316                         if (x >= nextTree && ! water[x]) {
317                                 let g = ground[x];
318                                 let biome = biomeList[biomes[x]];
319
320                                 for (let tree of biome.trees) {
321                                         if (Math.random() <= tree.chance) {
322                                                 snode(x, g - 1, tree.sapling);
323                                                 gnode(x, g - 1) && dragonblocks.finishTimer("growTimer", gnode(x, g - 1).meta);
324                                                 nextTree = x + tree.width;
325                                                 break;
326                                         }
327                                 }
328                         }
329                 }
330         }
331
332         // Ressource Blobs (e.g. Gravel, Dirt)
333
334         {
335                 let belowGround = (node, x, y) => {
336                         return y > ground[x]
337                 };
338
339                 function structure(x, y, mat) {
340                         new pm([["§" + mat, mat], [mat, mat]])
341                                 .addFunction(belowGround)
342                                 .apply(x, y);
343
344                         let sides = [
345                                 new pm([[mat, mat], ["§", ""]]),
346                                 new pm([["§", "", mat], ["", "", mat]]),
347                                 new pm([["§", ""], ["", ""], [mat, mat]]),
348                                 new pm([[mat, "§"], [mat, ""]]),
349                         ];
350
351                         for (let side of sides)
352                                 side.addFunction(belowGround);
353
354                         let moresides = [
355                                 new pm([[mat, mat], ["", ""], ["§", ""]]),
356                                 new pm([["§", "", "", mat], ["", "", "", mat]]),
357                                 new pm([["§", ""], ["", ""], ["", ""], [mat, mat]]),
358                                 new pm([[mat, "", "§"], [mat, "", ""]]),
359                         ];
360
361                         for (let moreside of moresides)
362                                 moreside.addFunction(belowGround);
363
364                         let corners = [
365                                 new pm([[mat, ""], ["", "§"]]),
366                                 new pm([["", "", mat], ["§", "", ""]]),
367                                 new pm([["§", "", ""], ["", "", ""], ["", "", mat]]),
368                                 new pm([["§", "", ""], ["", "", ""], ["", "", mat]]),
369                         ];
370
371                         for (let corner of corners)
372                                 corner.addFunction(belowGround);
373
374                         for (let i in sides) {
375                                 if (Math.random() * 1.2 < 1){
376                                         sides[i].apply(x, y);
377
378                                         for (let j = i; j <= int(i) + 1; j++){
379                                                 let corner = corners[j] || corners[0];
380
381                                                 if (Math.random() * 1.5 < 1)
382                                                         corner.apply(x, y);
383                                         }
384
385                                         if (Math.random() * 2 < 1)
386                                                 moresides[i].apply(x, y);
387                                 }
388                         }
389                 }
390
391                 for (let material of dragonblocks.mapgen.materials)
392                         for (let i = 0; i < width / material.factor; i++)
393                                 structure(rand(0, width), rand(0, height), material.name);
394         }
395
396         // Caves
397
398         {
399                 let cave = (x, y, r) => {
400                         r *= 2;
401
402                         let cavepm = new pm([
403                                 ["",    "air",  "air", "air",    ""],
404                                 ["air", "air",  "air", "air", "air"],
405                                 ["air", "air", "§air", "air", "air"],
406                                 ["air", "air",  "air", "air", "air"],
407                                 ["",    "air",  "air", "air",    ""],
408                         ]);
409
410                         cavepm.addFunction((node, x, y) => {
411                                 if (y < ground[x])
412                                         return false;
413
414                                 if (dblib.random(0, r) == 0)
415                                         cave(x, y, r);
416                         });
417
418                         cavepm.apply(x, y);
419                 };
420
421                 let newCave = (x, y) => {
422                         let r = dblib.random(0, 10) + 1;
423
424                         if (y < ground[x] + 10)
425                                 return false;
426
427                         cave(x, y, r);
428
429                         dragonblocks.mapgen.addStructure("cave", "(" + x + ", " + y + ")", {x: x, y: y});
430                 };
431
432                 let r = dblib.random(width / 5, width / 15);
433
434                 for (let i = 0; i < r; i++)
435                         newCave(rand(0, width), rand(0, height));
436         }
437 };
438
439 dragonblocks.mapgen.list["flat"] = _ => {
440         for (let x = 0; x < dragonblocks.map.width; x++) {
441                 let y = 0;
442
443                 for(; y < dragonblocks.map.height - 5; y++)
444                         dragonblocks.setNode(x, y, "air");
445
446                 for(; y < dragonblocks.map.height - 4; y++)
447                         dragonblocks.setNode(x, y, "dirt:grass");
448
449                 for(; y < dragonblocks.map.height - 3; y++)
450                         dragonblocks.setNode(x, y, "dirt:dirt");
451
452                 for(; y < dragonblocks.map.height; y++)
453                         dragonblocks.setNode(x, y, "core:stone");
454         }
455 };
456
457 dragonblocks.mapgen.list["empty"] = _ => {};
458