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