]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/server/biomes.c
d596e87450971f2fa3e76d48f914edace1350f12
[dragonblocks_alpha.git] / src / server / biomes.c
1 #include <math.h>
2 #include "server/biomes.h"
3 #include "server/mapgen.h"
4 #include "server/server_map.h"
5 #include "util.h"
6
7 Biome get_biome(v2s32 pos, f64 *factor)
8 {
9         for (Biome i = 0; i < BIOME_COUNT; i++) {
10                 BiomeDef *def = &biomes[i];
11                 f64 f = def->probability == 1.0 ? 1.0 : (smooth2d(U32(pos.x) / def->threshold, U32(pos.y) / def->threshold, 0, seed + def->offset) * 0.5 - 0.5 + def->probability) / def->probability;
12
13                 if (f > 0.0) {
14                         if (factor)
15                                 *factor = f;
16                         return i;
17                 }
18         }
19
20         return BIOME_COUNT;
21 }
22
23 // mountain biome
24
25 static s32 height_mountain(v2s32 pos, f64 factor, s32 height, unused void *row_data, unused void *block_data)
26 {
27         return pow((height + 96) * pow(((smooth2d(U32(pos.x) / 48.0, U32(pos.y) / 48.0, 0, seed + SO_MOUNTAIN_HEIGHT) + 1.0) * 256.0 + 128.0), factor), 1.0 / (factor + 1.0)) - 96;
28 }
29
30 static Node generate_mountain(unused v3s32 pos, s32 diff, unused f64 humidity, unused f64 temperature, unused f64 factor, unused MapBlock *block, unused List *changed_blocks, unused void *row_data, unused void *block_data)
31 {
32         return diff <= 0 ? NODE_STONE : NODE_AIR;
33 }
34
35 // ocean biome
36
37 typedef enum
38 {
39         OL_BEACH_EDGE,
40         OL_BEACH,
41         OL_OCEAN,
42         OL_DEEP_OCEAN,
43         OL_COUNT
44 } OceanLevel;
45
46 static f64 ocean_level_start[OL_COUNT] = {
47         0.0,
48         0.1,
49         0.2,
50         0.5,
51 };
52
53 typedef struct
54 {
55         bool has_vulcano;
56         v2s32 vulcano_pos;
57 } OceanBlockData;
58
59 typedef struct
60 {
61         bool vulcano;
62         bool vulcano_crater;
63         s32 vulcano_height;
64         s32 vulcano_crater_top;
65         Node vulcano_stone;
66 } OceanRowData;
67
68 static const f64 vulcano_radius = 256.0;
69 static const f64 vulcano_block_offset = vulcano_radius * 2.0 / MAPBLOCK_SIZE;
70
71 static OceanLevel get_ocean_level(f64 factor)
72 {
73         if (factor >= ocean_level_start[OL_DEEP_OCEAN])
74                 return OL_DEEP_OCEAN;
75         else if (factor >= ocean_level_start[OL_OCEAN])
76                 return OL_OCEAN;
77         else if (factor >= ocean_level_start[OL_BEACH])
78                 return OL_BEACH;
79
80         return OL_BEACH_EDGE;
81 }
82
83 static f64 get_ocean_level_factor(f64 factor, OceanLevel level)
84 {
85         f64 start, end;
86         start = ocean_level_start[level];
87         end = ++level == OL_COUNT ? 1.0 : ocean_level_start[level];
88
89         return (factor - start) / (end - start);
90 }
91
92 static bool is_vulcano(v2s32 pos)
93 {
94         f64 factor;
95         return smooth2d(U32(pos.x), U32(pos.y), 0, seed + SO_VULCANO) > 0.0 && get_biome((v2s32) {pos.x * MAPBLOCK_SIZE, pos.y * MAPBLOCK_SIZE}, &factor) == BIOME_OCEAN && get_ocean_level(factor) == OL_DEEP_OCEAN;
96 }
97
98 static bool find_near_vulcano(v2s32 pos, v2s32 *result)
99 {
100         f64 x = pos.x / vulcano_block_offset;
101         f64 z = pos.y / vulcano_block_offset;
102
103         s32 lx, lz;
104         lx = floor(x);
105         lz = floor(z);
106
107         s32 hx, hz;
108         hx = ceil(x);
109         hz = ceil(z);
110
111         for (s32 ix = lx; ix <= hx; ix++) {
112                 for (s32 iz = lz; iz <= hz; iz++) {
113                         v2s32 vulcano_pos = {ix * 32, iz * 32};
114
115                         if (is_vulcano(vulcano_pos)) {
116                                 *result = vulcano_pos;
117                                 return true;
118                         }
119                 }
120         }
121
122         return false;
123 }
124
125 static f64 mix(f64 a, f64 b, f64 f)
126 {
127         return (a * (1.0 - f) + b * f);
128 }
129
130 static inline f64 min(f64 a, f64 b)
131 {
132         return a < b ? a : b;
133 }
134
135 static inline f64 max(f64 a, f64 b)
136 {
137         return a > b ? a : b;
138 }
139
140 static f64 distance(v2s32 a, v2s32 b)
141 {
142         return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
143 }
144
145 static s32 calculate_ocean_floor(f64 factor, s32 height)
146 {
147         switch (get_ocean_level(factor)) {
148                 case OL_BEACH_EDGE:
149                         return mix(height + 1, 0, pow(get_ocean_level_factor(factor, OL_BEACH_EDGE), 0.8));
150
151                 case OL_BEACH:
152                         return 0;
153
154                 case OL_OCEAN:
155                         return mix(0, -10, pow(get_ocean_level_factor(factor, OL_OCEAN), 0.5));
156
157                 case OL_DEEP_OCEAN:
158                         return mix(-10, -50, pow(get_ocean_level_factor(factor, OL_DEEP_OCEAN), 0.5));
159
160                 default:
161                         break;
162         }
163
164         return height;
165 }
166
167 static void preprocess_block_ocean(MapBlock *block, unused List *changed_blocks, void *block_data)
168 {
169         OceanBlockData *data = block_data;
170
171         v2s32 vulcano_pos;
172         if ((data->has_vulcano = find_near_vulcano((v2s32) {block->pos.x, block->pos.z}, &vulcano_pos)))
173                 data->vulcano_pos = (v2s32) {vulcano_pos.x * MAPBLOCK_SIZE, vulcano_pos.y * MAPBLOCK_SIZE};
174 }
175
176 static void preprocess_row_ocean(v2s32 pos, unused s32 height, unused f64 factor, void *row_data, void *block_data)
177 {
178         OceanRowData *rdata = row_data;
179         OceanBlockData *bdata = block_data;
180         rdata->vulcano = false;
181
182         if (bdata->has_vulcano) {
183                 f64 dist = distance(pos, bdata->vulcano_pos);
184
185                 if (dist < vulcano_radius) {
186                         f64 crater_factor = pow(asin(1.0 - dist / vulcano_radius), 2.0);
187                         f64 vulcano_height = (pnoise2d(U32(pos.x) / 100.0, U32(pos.y) / 100.0, 0.2, 2, seed + SO_VULCANO_HEIGHT) * 0.5 + 0.5) * 128.0 * crater_factor + 1.0 - 30.0;
188                         bool is_crater = vulcano_height > 0;
189
190                         if (! is_crater)
191                                 vulcano_height = min(vulcano_height + 5.0, 0.0);
192
193                         if (vulcano_height < 0)
194                                 vulcano_height *= 2.0;
195
196                         rdata->vulcano = true;
197                         rdata->vulcano_crater = is_crater;
198                         rdata->vulcano_height = floor(vulcano_height + 0.5);
199                         rdata->vulcano_crater_top = 50 + floor((pnoise2d(U32(pos.x) / 3.0, U32(pos.y) / 3.0, 0.0, 1, seed + SO_VULCANO_CRATER_TOP) * 0.5 + 0.5) * 3.0 + 0.5);
200                         rdata->vulcano_stone = is_crater ? ((pnoise2d(U32(pos.x) / 16.0, U32(pos.y) / 16.0, 0.85, 3, seed + SO_VULCANO_STONE) * 0.5 + 0.5) * crater_factor > 0.4 ? NODE_VULCANO_STONE : NODE_STONE) : NODE_SAND;
201                 }
202         }
203 }
204
205 static s32 height_ocean(unused v2s32 pos, f64 factor, s32 height, void *row_data, unused void *block_data)
206 {
207         OceanRowData *rdata = row_data;
208         s32 ocean_floor = calculate_ocean_floor(factor, height);
209
210         return rdata->vulcano ? max(ocean_floor, rdata->vulcano_height) : ocean_floor;
211 }
212
213 static Node generate_ocean(v3s32 pos, s32 diff, unused f64 humidity, unused f64 temperature, unused f64 factor, unused MapBlock *block, unused List *changed_blocks, void *row_data, unused void *block_data)
214 {
215         OceanRowData *rdata = row_data;
216
217         if (rdata->vulcano && rdata->vulcano_crater) {
218                 if (diff <= -5)
219                         return pos.y <= 45 ? NODE_LAVA : NODE_AIR;
220                 else if (diff <= 0)
221                         return pos.y <= rdata->vulcano_crater_top ? rdata->vulcano_stone : NODE_AIR;
222                 else
223                         return NODE_AIR;
224         } else {
225                 if (diff <= -5)
226                         return NODE_STONE;
227                 else if (diff <= 0)
228                         return NODE_SAND;
229                 else if (pos.y <= 0)
230                         return NODE_WATER;
231         }
232
233         return NODE_AIR;
234 }
235
236 // hills biome
237
238 static s32 height_hills(unused v2s32 pos, unused f64 factor, s32 height, unused void *row_data, unused void *block_data)
239 {
240         return height;
241 }
242
243 static Node generate_hills(v3s32 pos, s32 diff, unused f64 humidity, unused f64 temperature, unused f64 factor, unused MapBlock *block, List *changed_blocks, unused void *row_data, unused void *block_data)
244 {
245         if (diff == 2 && smooth2d(U32(pos.x), U32(pos.z), 0, seed + SO_BOULDER_CENTER) > 0.999) {
246                 for (s8 bx = -1; bx <= 1; bx++) {
247                         for (s8 by = -1; by <= 1; by++) {
248                                 for (s8 bz = -1; bz <= 1; bz++) {
249                                         v3s32 bpos = {pos.x + bx, pos.y + by, pos.z + bz};
250                                         if (smooth3d(bpos.x, bpos.y, bpos.z, 0, seed + SO_BOULDER) > 0.0)
251                                                 mapgen_set_node(bpos, map_node_create(NODE_STONE), MGS_BOULDERS, changed_blocks);
252                                 }
253                         }
254                 }
255         }
256
257         if (diff <= -5)
258                 return NODE_STONE;
259         else if (diff <= -1)
260                 return NODE_DIRT;
261         else if (diff <= 0)
262                 return NODE_GRASS;
263
264         return NODE_AIR;
265 }
266
267 BiomeDef biomes[BIOME_COUNT] = {
268         {
269                 .probability = 0.2,
270                 .offset = SO_MOUNTAIN,
271                 .threshold = 1024.0,
272                 .snow = true,
273                 .height = &height_mountain,
274                 .generate = &generate_mountain,
275                 .block_data_size = 0,
276                 .preprocess_block = NULL,
277                 .row_data_size = 0,
278                 .preprocess_row = NULL,
279         },
280         {
281                 .probability = 0.2,
282                 .offset = SO_OCEAN,
283                 .threshold = 2048.0,
284                 .snow = false,
285                 .height = &height_ocean,
286                 .generate = &generate_ocean,
287                 .block_data_size = sizeof(OceanBlockData),
288                 .preprocess_block = &preprocess_block_ocean,
289                 .row_data_size = sizeof(OceanRowData),
290                 .preprocess_row = &preprocess_row_ocean,
291
292         },
293         {
294                 .probability = 1.0,
295                 .offset = SO_NONE,
296                 .threshold = 0.0,
297                 .snow = true,
298                 .height = &height_hills,
299                 .generate = &generate_hills,
300                 .block_data_size = 0,
301                 .preprocess_block = NULL,
302                 .row_data_size = 0,
303                 .preprocess_row = NULL,
304         },
305 };