1 /*
2 Minetest
3 Copyright (C) 2016-2019 Duane Robertson <duane@duanerobertson.com>
4 Copyright (C) 2016-2019 paramat
5 
6 Based on Valleys Mapgen by Gael de Sailly
7 (https://forum.minetest.net/viewtopic.php?f=9&t=11430)
8 and mapgen_v7, mapgen_flat by kwolekr and paramat.
9 
10 Licensing changed by permission of Gael de Sailly.
11 
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU Lesser General Public License as published by
14 the Free Software Foundation; either version 2.1 of the License, or
15 (at your option) any later version.
16 
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 GNU Lesser General Public License for more details.
21 
22 You should have received a copy of the GNU Lesser General Public License along
23 with this program; if not, write to the Free Software Foundation, Inc.,
24 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 */
26 
27 
28 #include "mapgen.h"
29 #include "voxel.h"
30 #include "noise.h"
31 #include "mapblock.h"
32 #include "mapnode.h"
33 #include "map.h"
34 #include "nodedef.h"
35 #include "voxelalgorithms.h"
36 //#include "profiler.h" // For TimeTaker
37 #include "settings.h" // For g_settings
38 #include "emerge.h"
39 #include "dungeongen.h"
40 #include "mg_biome.h"
41 #include "mg_ore.h"
42 #include "mg_decoration.h"
43 #include "mapgen_valleys.h"
44 #include "cavegen.h"
45 #include <cmath>
46 
47 
48 FlagDesc flagdesc_mapgen_valleys[] = {
49 	{"altitude_chill",   MGVALLEYS_ALT_CHILL},
50 	{"humid_rivers",     MGVALLEYS_HUMID_RIVERS},
51 	{"vary_river_depth", MGVALLEYS_VARY_RIVER_DEPTH},
52 	{"altitude_dry",     MGVALLEYS_ALT_DRY},
53 	{NULL,               0}
54 };
55 
56 
MapgenValleys(MapgenValleysParams * params,EmergeParams * emerge)57 MapgenValleys::MapgenValleys(MapgenValleysParams *params, EmergeParams *emerge)
58 	: MapgenBasic(MAPGEN_VALLEYS, params, emerge)
59 {
60 	// NOTE: MapgenValleys has a hard dependency on BiomeGenOriginal
61 	m_bgen = (BiomeGenOriginal *)biomegen;
62 
63 	spflags            = params->spflags;
64 	altitude_chill     = params->altitude_chill;
65 	river_depth_bed    = params->river_depth + 1.0f;
66 	river_size_factor  = params->river_size / 100.0f;
67 
68 	cave_width         = params->cave_width;
69 	large_cave_depth   = params->large_cave_depth;
70 	small_cave_num_min = params->small_cave_num_min;
71 	small_cave_num_max = params->small_cave_num_max;
72 	large_cave_num_min = params->large_cave_num_min;
73 	large_cave_num_max = params->large_cave_num_max;
74 	large_cave_flooded = params->large_cave_flooded;
75 	cavern_limit       = params->cavern_limit;
76 	cavern_taper       = params->cavern_taper;
77 	cavern_threshold   = params->cavern_threshold;
78 	dungeon_ymin       = params->dungeon_ymin;
79 	dungeon_ymax       = params->dungeon_ymax;
80 
81 	//// 2D Terrain noise
82 	noise_filler_depth       = new Noise(&params->np_filler_depth,       seed, csize.X, csize.Z);
83 	noise_inter_valley_slope = new Noise(&params->np_inter_valley_slope, seed, csize.X, csize.Z);
84 	noise_rivers             = new Noise(&params->np_rivers,             seed, csize.X, csize.Z);
85 	noise_terrain_height     = new Noise(&params->np_terrain_height,     seed, csize.X, csize.Z);
86 	noise_valley_depth       = new Noise(&params->np_valley_depth,       seed, csize.X, csize.Z);
87 	noise_valley_profile     = new Noise(&params->np_valley_profile,     seed, csize.X, csize.Z);
88 
89 	//// 3D Terrain noise
90 	// 1-up 1-down overgeneration
91 	noise_inter_valley_fill = new Noise(&params->np_inter_valley_fill,
92 		seed, csize.X, csize.Y + 2, csize.Z);
93 	// 1-down overgeneraion
94 	MapgenBasic::np_cave1    = params->np_cave1;
95 	MapgenBasic::np_cave2    = params->np_cave2;
96 	MapgenBasic::np_cavern   = params->np_cavern;
97 	MapgenBasic::np_dungeons = params->np_dungeons;
98 }
99 
100 
~MapgenValleys()101 MapgenValleys::~MapgenValleys()
102 {
103 	delete noise_filler_depth;
104 	delete noise_inter_valley_fill;
105 	delete noise_inter_valley_slope;
106 	delete noise_rivers;
107 	delete noise_terrain_height;
108 	delete noise_valley_depth;
109 	delete noise_valley_profile;
110 }
111 
112 
MapgenValleysParams()113 MapgenValleysParams::MapgenValleysParams():
114 	np_filler_depth       (0.0,   1.2,  v3f(256,  256,  256),  1605,  3, 0.5,  2.0),
115 	np_inter_valley_fill  (0.0,   1.0,  v3f(256,  512,  256),  1993,  6, 0.8,  2.0),
116 	np_inter_valley_slope (0.5,   0.5,  v3f(128,  128,  128),  746,   1, 1.0,  2.0),
117 	np_rivers             (0.0,   1.0,  v3f(256,  256,  256),  -6050, 5, 0.6,  2.0),
118 	np_terrain_height     (-10.0, 50.0, v3f(1024, 1024, 1024), 5202,  6, 0.4,  2.0),
119 	np_valley_depth       (5.0,   4.0,  v3f(512,  512,  512),  -1914, 1, 1.0,  2.0),
120 	np_valley_profile     (0.6,   0.50, v3f(512,  512,  512),  777,   1, 1.0,  2.0),
121 	np_cave1              (0.0,   12.0, v3f(61,   61,   61),   52534, 3, 0.5,  2.0),
122 	np_cave2              (0.0,   12.0, v3f(67,   67,   67),   10325, 3, 0.5,  2.0),
123 	np_cavern             (0.0,   1.0,  v3f(768,  256,  768),  59033, 6, 0.63, 2.0),
124 	np_dungeons           (0.9,   0.5,  v3f(500,  500,  500),  0,     2, 0.8,  2.0)
125 {
126 }
127 
128 
readParams(const Settings * settings)129 void MapgenValleysParams::readParams(const Settings *settings)
130 {
131 	settings->getFlagStrNoEx("mgvalleys_spflags", spflags, flagdesc_mapgen_valleys);
132 	settings->getU16NoEx("mgvalleys_altitude_chill",       altitude_chill);
133 	settings->getS16NoEx("mgvalleys_large_cave_depth",     large_cave_depth);
134 	settings->getU16NoEx("mgvalleys_small_cave_num_min",   small_cave_num_min);
135 	settings->getU16NoEx("mgvalleys_small_cave_num_max",   small_cave_num_max);
136 	settings->getU16NoEx("mgvalleys_large_cave_num_min",   large_cave_num_min);
137 	settings->getU16NoEx("mgvalleys_large_cave_num_max",   large_cave_num_max);
138 	settings->getFloatNoEx("mgvalleys_large_cave_flooded", large_cave_flooded);
139 	settings->getU16NoEx("mgvalleys_river_depth",          river_depth);
140 	settings->getU16NoEx("mgvalleys_river_size",           river_size);
141 	settings->getFloatNoEx("mgvalleys_cave_width",         cave_width);
142 	settings->getS16NoEx("mgvalleys_cavern_limit",         cavern_limit);
143 	settings->getS16NoEx("mgvalleys_cavern_taper",         cavern_taper);
144 	settings->getFloatNoEx("mgvalleys_cavern_threshold",   cavern_threshold);
145 	settings->getS16NoEx("mgvalleys_dungeon_ymin",         dungeon_ymin);
146 	settings->getS16NoEx("mgvalleys_dungeon_ymax",         dungeon_ymax);
147 
148 	settings->getNoiseParams("mgvalleys_np_filler_depth",       np_filler_depth);
149 	settings->getNoiseParams("mgvalleys_np_inter_valley_fill",  np_inter_valley_fill);
150 	settings->getNoiseParams("mgvalleys_np_inter_valley_slope", np_inter_valley_slope);
151 	settings->getNoiseParams("mgvalleys_np_rivers",             np_rivers);
152 	settings->getNoiseParams("mgvalleys_np_terrain_height",     np_terrain_height);
153 	settings->getNoiseParams("mgvalleys_np_valley_depth",       np_valley_depth);
154 	settings->getNoiseParams("mgvalleys_np_valley_profile",     np_valley_profile);
155 
156 	settings->getNoiseParams("mgvalleys_np_cave1",              np_cave1);
157 	settings->getNoiseParams("mgvalleys_np_cave2",              np_cave2);
158 	settings->getNoiseParams("mgvalleys_np_cavern",             np_cavern);
159 	settings->getNoiseParams("mgvalleys_np_dungeons",           np_dungeons);
160 }
161 
162 
writeParams(Settings * settings) const163 void MapgenValleysParams::writeParams(Settings *settings) const
164 {
165 	settings->setFlagStr("mgvalleys_spflags", spflags, flagdesc_mapgen_valleys);
166 	settings->setU16("mgvalleys_altitude_chill",       altitude_chill);
167 	settings->setS16("mgvalleys_large_cave_depth",     large_cave_depth);
168 	settings->setU16("mgvalleys_small_cave_num_min",   small_cave_num_min);
169 	settings->setU16("mgvalleys_small_cave_num_max",   small_cave_num_max);
170 	settings->setU16("mgvalleys_large_cave_num_min",   large_cave_num_min);
171 	settings->setU16("mgvalleys_large_cave_num_max",   large_cave_num_max);
172 	settings->setFloat("mgvalleys_large_cave_flooded", large_cave_flooded);
173 	settings->setU16("mgvalleys_river_depth",          river_depth);
174 	settings->setU16("mgvalleys_river_size",           river_size);
175 	settings->setFloat("mgvalleys_cave_width",         cave_width);
176 	settings->setS16("mgvalleys_cavern_limit",         cavern_limit);
177 	settings->setS16("mgvalleys_cavern_taper",         cavern_taper);
178 	settings->setFloat("mgvalleys_cavern_threshold",   cavern_threshold);
179 	settings->setS16("mgvalleys_dungeon_ymin",         dungeon_ymin);
180 	settings->setS16("mgvalleys_dungeon_ymax",         dungeon_ymax);
181 
182 	settings->setNoiseParams("mgvalleys_np_filler_depth",       np_filler_depth);
183 	settings->setNoiseParams("mgvalleys_np_inter_valley_fill",  np_inter_valley_fill);
184 	settings->setNoiseParams("mgvalleys_np_inter_valley_slope", np_inter_valley_slope);
185 	settings->setNoiseParams("mgvalleys_np_rivers",             np_rivers);
186 	settings->setNoiseParams("mgvalleys_np_terrain_height",     np_terrain_height);
187 	settings->setNoiseParams("mgvalleys_np_valley_depth",       np_valley_depth);
188 	settings->setNoiseParams("mgvalleys_np_valley_profile",     np_valley_profile);
189 
190 	settings->setNoiseParams("mgvalleys_np_cave1",              np_cave1);
191 	settings->setNoiseParams("mgvalleys_np_cave2",              np_cave2);
192 	settings->setNoiseParams("mgvalleys_np_cavern",             np_cavern);
193 	settings->setNoiseParams("mgvalleys_np_dungeons",           np_dungeons);
194 }
195 
196 
setDefaultSettings(Settings * settings)197 void MapgenValleysParams::setDefaultSettings(Settings *settings)
198 {
199 	settings->setDefault("mgvalleys_spflags", flagdesc_mapgen_valleys,
200 		MGVALLEYS_ALT_CHILL | MGVALLEYS_HUMID_RIVERS |
201 		MGVALLEYS_VARY_RIVER_DEPTH | MGVALLEYS_ALT_DRY);
202 }
203 
204 
205 /////////////////////////////////////////////////////////////////
206 
207 
makeChunk(BlockMakeData * data)208 void MapgenValleys::makeChunk(BlockMakeData *data)
209 {
210 	// Pre-conditions
211 	assert(data->vmanip);
212 	assert(data->nodedef);
213 
214 	//TimeTaker t("makeChunk");
215 
216 	this->generating = true;
217 	this->vm = data->vmanip;
218 	this->ndef = data->nodedef;
219 
220 	v3s16 blockpos_min = data->blockpos_min;
221 	v3s16 blockpos_max = data->blockpos_max;
222 	node_min = blockpos_min * MAP_BLOCKSIZE;
223 	node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
224 	full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
225 	full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
226 
227 	blockseed = getBlockSeed2(full_node_min, seed);
228 
229 	// Generate biome noises. Note this must be executed strictly before
230 	// generateTerrain, because generateTerrain depends on intermediate
231 	// biome-related noises.
232 	m_bgen->calcBiomeNoise(node_min);
233 
234 	// Generate terrain
235 	s16 stone_surface_max_y = generateTerrain();
236 
237 	// Create heightmap
238 	updateHeightmap(node_min, node_max);
239 
240 	// Place biome-specific nodes and build biomemap
241 	if (flags & MG_BIOMES) {
242 		generateBiomes();
243 	}
244 
245 	// Generate tunnels, caverns and large randomwalk caves
246 	if (flags & MG_CAVES) {
247 		// Generate tunnels first as caverns confuse them
248 		generateCavesNoiseIntersection(stone_surface_max_y);
249 
250 		// Generate caverns
251 		bool near_cavern = generateCavernsNoise(stone_surface_max_y);
252 
253 		// Generate large randomwalk caves
254 		if (near_cavern)
255 			// Disable large randomwalk caves in this mapchunk by setting
256 			// 'large cave depth' to world base. Avoids excessive liquid in
257 			// large caverns and floating blobs of overgenerated liquid.
258 			generateCavesRandomWalk(stone_surface_max_y,
259 				-MAX_MAP_GENERATION_LIMIT);
260 		else
261 			generateCavesRandomWalk(stone_surface_max_y, large_cave_depth);
262 	}
263 
264 	// Generate the registered ores
265 	if (flags & MG_ORES)
266 		m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
267 
268 	// Dungeon creation
269 	if (flags & MG_DUNGEONS)
270 		generateDungeons(stone_surface_max_y);
271 
272 	// Generate the registered decorations
273 	if (flags & MG_DECORATIONS)
274 		m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
275 
276 	// Sprinkle some dust on top after everything else was generated
277 	if (flags & MG_BIOMES)
278 		dustTopNodes();
279 
280 	updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
281 
282 	if (flags & MG_LIGHT)
283 		calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
284 			full_node_min, full_node_max);
285 
286 	this->generating = false;
287 
288 	//printf("makeChunk: %lums\n", t.stop());
289 }
290 
291 
getSpawnLevelAtPoint(v2s16 p)292 int MapgenValleys::getSpawnLevelAtPoint(v2s16 p)
293 {
294 	// Check if in a river channel
295 	float n_rivers = NoisePerlin2D(&noise_rivers->np, p.X, p.Y, seed);
296 	if (std::fabs(n_rivers) <= river_size_factor)
297 		// Unsuitable spawn point
298 		return MAX_MAP_GENERATION_LIMIT;
299 
300 	float n_slope          = NoisePerlin2D(&noise_inter_valley_slope->np, p.X, p.Y, seed);
301 	float n_terrain_height = NoisePerlin2D(&noise_terrain_height->np, p.X, p.Y, seed);
302 	float n_valley         = NoisePerlin2D(&noise_valley_depth->np, p.X, p.Y, seed);
303 	float n_valley_profile = NoisePerlin2D(&noise_valley_profile->np, p.X, p.Y, seed);
304 
305 	float valley_d = n_valley * n_valley;
306 	float base = n_terrain_height + valley_d;
307 	float river = std::fabs(n_rivers) - river_size_factor;
308 	float tv = std::fmax(river / n_valley_profile, 0.0f);
309 	float valley_h = valley_d * (1.0f - std::exp(-tv * tv));
310 	float surface_y = base + valley_h;
311 	float slope = n_slope * valley_h;
312 	float river_y = base - 1.0f;
313 
314 	// Raising the maximum spawn level above 'water_level + 16' is necessary for custom
315 	// parameters that set average terrain level much higher than water_level.
316 	s16 max_spawn_y = std::fmax(
317 		noise_terrain_height->np.offset +
318 		noise_valley_depth->np.offset * noise_valley_depth->np.offset,
319 		water_level + 16);
320 
321 	// Starting spawn search at max_spawn_y + 128 ensures 128 nodes of open
322 	// space above spawn position. Avoids spawning in possibly sealed voids.
323 	for (s16 y = max_spawn_y + 128; y >= water_level; y--) {
324 		float n_fill = NoisePerlin3D(&noise_inter_valley_fill->np, p.X, y, p.Y, seed);
325 		float surface_delta = (float)y - surface_y;
326 		float density = slope * n_fill - surface_delta;
327 
328 		if (density > 0.0f) {  // If solid
329 			// Sometimes surface level is below river water level in places that are not
330 			// river channels.
331 			if (y < water_level || y > max_spawn_y || y < (s16)river_y)
332 				// Unsuitable spawn point
333 				return MAX_MAP_GENERATION_LIMIT;
334 
335 			// y + 2 because y is surface and due to biome 'dust' nodes.
336 			return y + 2;
337 		}
338 	}
339 	// Unsuitable spawn position, no ground found
340 	return MAX_MAP_GENERATION_LIMIT;
341 }
342 
343 
generateTerrain()344 int MapgenValleys::generateTerrain()
345 {
346 	MapNode n_air(CONTENT_AIR);
347 	MapNode n_river_water(c_river_water_source);
348 	MapNode n_stone(c_stone);
349 	MapNode n_water(c_water_source);
350 
351 	noise_inter_valley_slope->perlinMap2D(node_min.X, node_min.Z);
352 	noise_rivers->perlinMap2D(node_min.X, node_min.Z);
353 	noise_terrain_height->perlinMap2D(node_min.X, node_min.Z);
354 	noise_valley_depth->perlinMap2D(node_min.X, node_min.Z);
355 	noise_valley_profile->perlinMap2D(node_min.X, node_min.Z);
356 
357 	noise_inter_valley_fill->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
358 
359 	const v3s16 &em = vm->m_area.getExtent();
360 	s16 surface_max_y = -MAX_MAP_GENERATION_LIMIT;
361 	u32 index_2d = 0;
362 
363 	for (s16 z = node_min.Z; z <= node_max.Z; z++)
364 	for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) {
365 		float n_slope          = noise_inter_valley_slope->result[index_2d];
366 		float n_rivers         = noise_rivers->result[index_2d];
367 		float n_terrain_height = noise_terrain_height->result[index_2d];
368 		float n_valley         = noise_valley_depth->result[index_2d];
369 		float n_valley_profile = noise_valley_profile->result[index_2d];
370 
371 		float valley_d = n_valley * n_valley;
372 		// 'base' represents the level of the river banks
373 		float base = n_terrain_height + valley_d;
374 		// 'river' represents the distance from the river edge
375 		float river = std::fabs(n_rivers) - river_size_factor;
376 		// Use the curve of the function 1-exp(-(x/a)^2) to model valleys.
377 		// 'valley_h' represents the height of the terrain, from the rivers.
378 		float tv = std::fmax(river / n_valley_profile, 0.0f);
379 		float valley_h = valley_d * (1.0f - std::exp(-tv * tv));
380 		// Approximate height of the terrain
381 		float surface_y = base + valley_h;
382 		float slope = n_slope * valley_h;
383 		// River water surface is 1 node below river banks
384 		float river_y = base - 1.0f;
385 
386 		// Rivers are placed where 'river' is negative
387 		if (river < 0.0f) {
388 			// Use the function -sqrt(1-x^2) which models a circle
389 			float tr = river / river_size_factor + 1.0f;
390 			float depth = (river_depth_bed *
391 				std::sqrt(std::fmax(0.0f, 1.0f - tr * tr)));
392 			// There is no logical equivalent to this using rangelim
393 			surface_y = std::fmin(
394 				std::fmax(base - depth, (float)(water_level - 3)),
395 				surface_y);
396 			slope = 0.0f;
397 		}
398 
399 		// Optionally vary river depth according to heat and humidity
400 		if (spflags & MGVALLEYS_VARY_RIVER_DEPTH) {
401 			float t_heat = m_bgen->heatmap[index_2d];
402 			float heat = (spflags & MGVALLEYS_ALT_CHILL) ?
403 				// Match heat value calculated below in
404 				// 'Optionally decrease heat with altitude'.
405 				// In rivers, 'ground height ignoring riverbeds' is 'base'.
406 				// As this only affects river water we can assume y > water_level.
407 				t_heat + 5.0f - (base - water_level) * 20.0f / altitude_chill :
408 				t_heat;
409 			float delta = m_bgen->humidmap[index_2d] - 50.0f;
410 			if (delta < 0.0f) {
411 				float t_evap = (heat - 32.0f) / 300.0f;
412 				river_y += delta * std::fmax(t_evap, 0.08f);
413 			}
414 		}
415 
416 		// Highest solid node in column
417 		s16 column_max_y = surface_y;
418 		u32 index_3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X);
419 		u32 index_data = vm->m_area.index(x, node_min.Y - 1, z);
420 
421 		for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
422 			if (vm->m_data[index_data].getContent() == CONTENT_IGNORE) {
423 				float n_fill = noise_inter_valley_fill->result[index_3d];
424 				float surface_delta = (float)y - surface_y;
425 				// Density = density noise + density gradient
426 				float density = slope * n_fill - surface_delta;
427 
428 				if (density > 0.0f) {
429 					vm->m_data[index_data] = n_stone; // Stone
430 					if (y > surface_max_y)
431 						surface_max_y = y;
432 					if (y > column_max_y)
433 						column_max_y = y;
434 				} else if (y <= water_level) {
435 					vm->m_data[index_data] = n_water; // Water
436 				} else if (y <= (s16)river_y) {
437 					vm->m_data[index_data] = n_river_water; // River water
438 				} else {
439 					vm->m_data[index_data] = n_air; // Air
440 				}
441 			}
442 
443 			VoxelArea::add_y(em, index_data, 1);
444 			index_3d += ystride;
445 		}
446 
447 		// Optionally increase humidity around rivers
448 		if (spflags & MGVALLEYS_HUMID_RIVERS) {
449 			// Compensate to avoid increasing average humidity
450 			m_bgen->humidmap[index_2d] *= 0.8f;
451 			// Ground height ignoring riverbeds
452 			float t_alt = std::fmax(base, (float)column_max_y);
453 			float water_depth = (t_alt - base) / 4.0f;
454 			m_bgen->humidmap[index_2d] *=
455 				1.0f + std::pow(0.5f, std::fmax(water_depth, 1.0f));
456 		}
457 
458 		// Optionally decrease humidity with altitude
459 		if (spflags & MGVALLEYS_ALT_DRY) {
460 			// Ground height ignoring riverbeds
461 			float t_alt = std::fmax(base, (float)column_max_y);
462 			// Only decrease above water_level
463 			if (t_alt > water_level)
464 				m_bgen->humidmap[index_2d] -=
465 					(t_alt - water_level) * 10.0f / altitude_chill;
466 		}
467 
468 		// Optionally decrease heat with altitude
469 		if (spflags & MGVALLEYS_ALT_CHILL) {
470 			// Compensate to avoid reducing the average heat
471 			m_bgen->heatmap[index_2d] += 5.0f;
472 			// Ground height ignoring riverbeds
473 			float t_alt = std::fmax(base, (float)column_max_y);
474 			// Only decrease above water_level
475 			if (t_alt > water_level)
476 				m_bgen->heatmap[index_2d] -=
477 					(t_alt - water_level) * 20.0f / altitude_chill;
478 		}
479 	}
480 
481 	return surface_max_y;
482 }
483