summaryrefslogtreecommitdiff
path: root/caverealms/init.lua
blob: f3b968ad229faaf66455f4364a3d2e52e2fd5941 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
-- caverealms v.0.3 by HeroOfTheWinds
-- original cave code modified from paramat's subterrain
-- For Minetest 0.4.8 stable
-- Depends default
-- License: code WTFPL


caverealms = {} --create a container for functions and constants

--grab a shorthand for the filepath of the mod
local modpath = minetest.get_modpath(minetest.get_current_modname())

--load companion lua files
dofile(modpath.."/config.lua") --configuration file; holds various constants
dofile(modpath.."/crafting.lua") --crafting recipes
dofile(modpath.."/nodes.lua") --node definitions
dofile(modpath.."/functions.lua") --function definitions

if caverealms.config.falling_icicles == true then
	dofile(modpath.."/falling_ice.lua") --complicated function for falling icicles
	print("[caverealms] falling icicles enabled.")
end

-- Parameters

local YMIN = caverealms.config.ymin -- Approximate realm limits.
local YMAX = caverealms.config.ymax
local DEEPCAVES_YMAX = caverealms.config.deepcaves_ymax
local DEEP_DEEP_CAVES_YMAZ = caverealms.config.deepdeepcaves_ymax
local TCAVE = caverealms.config.tcave --0.5 -- Cave threshold. 1 = small rare caves, 0.5 = 1/3rd ground volume, 0 = 1/2 ground volume
local BLEND = 128 -- Cave blend distance near YMIN, YMAX

local STAGCHA = caverealms.config.stagcha --0.002 --chance of stalagmites
local STALCHA = caverealms.config.stalcha --0.003 --chance of stalactites
local CRYSTAL = caverealms.config.crystal --0.007 --chance of glow crystal formations
local GEMCHA = caverealms.config.gemcha --0.03 --chance of small glow gems
local MUSHCHA = caverealms.config.mushcha --0.04 --chance of mushrooms
local MYCCHA = caverealms.config.myccha --0.03 --chance of mycena mushrooms
local WORMCHA = caverealms.config.wormcha --0.03 --chance of glow worms
local GIANTCHA = caverealms.config.giantcha --0.001 -- chance of giant mushrooms
local ICICHA = caverealms.config.icicha --0.035 -- chance of icicles

-- 3D noise for caves

local np_cave = {
	offset = 0,
	scale = 1,
	spread = {x=512, y=256, z=512}, -- squashed 2:1
	seed = 59033,
	octaves = 6,
	persist = 0.63
}

-- 3D noise for wave

local np_wave = {
	offset = 0,
	scale = 1,
	spread = {x=256, y=256, z=256},
	seed = -400000000089,
	octaves = 3,
	persist = 0.67
}

-- 2D noise for biome

local np_biome = {
	offset = 0,
	scale = 1,
	spread = {x=250, y=250, z=250},
	seed = 9130,
	octaves = 3,
	persist = 0.5
}

-- Stuff

subterrain = {}

local yblmin = YMIN + BLEND * 1.5
local yblmax = YMAX - BLEND * 1.5

-- On generated function

minetest.register_on_generated(function(minp, maxp, seed)
	--if out of range of caverealms limits
	if minp.y > YMAX or maxp.y < YMIN then
		return --quit; otherwise, you'd have stalagmites all over the place
	end

	--easy reference to commonly used values
	--local t1 = os.clock()
	local x1 = maxp.x
	local y1 = maxp.y
	local z1 = maxp.z
	local x0 = minp.x
	local y0 = minp.y
	local z0 = minp.z

	--print ("[caverealms] chunk minp ("..x0.." "..y0.." "..z0..")") --tell people you are generating a chunk

	local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
	local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
	local data = vm:get_data()

	--grab content IDs
	local c_air = minetest.get_content_id("air")
	local c_stone = minetest.get_content_id("default:stone")
	local c_water = minetest.get_content_id("default:water_source")
	local c_lava = minetest.get_content_id("default:lava_source")
	local c_lava_flowing = minetest.get_content_id("default:lava_flowing")
	local c_ice = minetest.get_content_id("default:ice")
	local c_thinice = minetest.get_content_id("caverealms:thin_ice")
	local c_crystal = minetest.get_content_id("caverealms:glow_crystal")
	local c_gem1 = minetest.get_content_id("caverealms:glow_gem")
	local c_gem2 = minetest.get_content_id("caverealms:glow_gem_2")
	local c_gem3 = minetest.get_content_id("caverealms:glow_gem_3")
	local c_gem4 = minetest.get_content_id("caverealms:glow_gem_4")
	local c_gem5 = minetest.get_content_id("caverealms:glow_gem_5")
	local c_moss = minetest.get_content_id("caverealms:stone_with_moss")
	local c_lichen = minetest.get_content_id("caverealms:stone_with_lichen")
	local c_algae = minetest.get_content_id("caverealms:stone_with_algae")
	local c_fungus = minetest.get_content_id("caverealms:fungus")
	local c_mycena = minetest.get_content_id("caverealms:mycena")
	local c_worm = minetest.get_content_id("caverealms:glow_worm")
	local c_iciu = minetest.get_content_id("caverealms:icicle_up")
	local c_icid = minetest.get_content_id("caverealms:icicle_down")
	local c_coal_block = minetest.get_content_id("default:coalblock")
	local c_diamond_block = minetest.get_content_id("default:diamondblock")


	local c_obsidian = {
		minetest.get_content_id("caverealms:obsidian"),
		minetest.get_content_id("caverealms:obsidian_2"),
		minetest.get_content_id("caverealms:obsidian_3"),
		minetest.get_content_id("caverealms:obsidian_4"),
	}

	local allow_deep_cave_biomes = false
	if minp.y <= DEEPCAVES_YMAX then
		allow_deep_cave_biomes = true
	end

	local c_hard_rock
	if minetest.get_modpath("morestones") then
		if (minp.y <= DEEP_DEEP_CAVES_YMAZ) then
			c_hard_rock = minetest.get_content_id("morestones:travertine")
		else
			c_hard_rock = minetest.get_content_id("morestones:comendite")
		end
	elseif minetest.get_modpath("gloopblocks") then
		c_hard_rock = minetest.get_content_id("default:basalt")
	else
		c_hard_rock = minetest.get_content_id("default:stone_with_diamond")
	end


	local deep_cave_shell_type = math.random()

	--mandatory values
	local sidelen = x1 - x0 + 1 --length of a mapblock
	local chulens = {x=sidelen, y=sidelen, z=sidelen} --table of chunk edges
	local minposxyz = {x=x0, y=y0, z=z0} --bottom corner
	local minposxz = {x=x0, y=z0} --2D bottom corner

	local nvals_cave = minetest.get_perlin_map(np_cave, chulens):get3dMap_flat(minposxyz) --cave noise for structure
	local nvals_wave = minetest.get_perlin_map(np_wave, chulens):get3dMap_flat(minposxyz) --wavy structure of cavern ceilings and floors
	local nvals_biome = minetest.get_perlin_map(np_biome, chulens):get2dMap_flat({x=x0+150, y=z0+50}) --2D noise for biomes (will be 3D humidity/temp later)

	local nixyz = 1 --3D node index
	local nixz = 1 --2D node index
	local nixyz2 = 1 --second 3D index for second loop


	for z = z0, z1 do -- for each xy plane progressing northwards
		--structure loop
		for y = y0, y1 do -- for each x row progressing upwards
			local tcave --declare variable
			--determine the overal cave threshold
			if y < yblmin then
				tcave = TCAVE + ((yblmin - y) / BLEND) ^ 2
			elseif y > yblmax then
				tcave = TCAVE + ((y - yblmax) / BLEND) ^ 2
			else
				tcave = TCAVE
			end
			local vi = area:index(x0, y, z) --current node index
			for x = x0, x1 do -- for each node do
				if (nvals_cave[nixyz] + nvals_wave[nixyz])/2 > tcave then --if node falls within cave threshold
					data[vi] = c_air --hollow it out to make the cave
				end
				--increment indices
				nixyz = nixyz + 1
				vi = vi + 1
			end
		end

		--decoration loop
		for y = y0, y1 do -- for each x row progressing upwards
			local tcave --same as above
			if y < yblmin then
				tcave = TCAVE + ((yblmin - y) / BLEND) ^ 2
			elseif y > yblmax then
				tcave = TCAVE + ((y - yblmax) / BLEND) ^ 2
			else
				tcave = TCAVE
			end
			local vi = area:index(x0, y, z)
			for x = x0, x1 do -- for each node do

				--determine biome
				local biome = false --preliminary declaration
				n_biome = nvals_biome[nixz] --make an easier reference to the noise
				--compare noise values to determine a biome
				if n_biome >= 0 and n_biome < 0.5 then
					if allow_deep_cave_biomes then --and n_biome >= 0.25 then
						biome = 6 -- obsidian
					else
						biome = 1 --moss
					end
				elseif n_biome <= -0.5 then
					biome = 2 --fungal
				elseif n_biome >= 0.5 then
					if n_biome >= 0.7 then
						biome = 5 -- deep glaciated
					else
						biome = 4 -- glaciated
					end
				else
					biome = 3 --algae
				end

				if math.floor(((nvals_cave[nixyz2] + nvals_wave[nixyz2])/2)*100) == math.floor(tcave*100) then
					--ceiling
					local ai = area:index(x,y+1,z) --above index
					if data[ai] == c_stone and data[vi] == c_air then --ceiling
						if biome == 6 then
							if deep_cave_shell_type > 0.65 then
								data[ai] = c_obsidian[math.random(1, #c_obsidian)]
								data[vi] = c_obsidian[math.random(1, #c_obsidian)]
							elseif deep_cave_shell_type > 0.35 then
								data[ai] = c_coal_block
								data[vi] = c_coal_block
							elseif deep_cave_shell_type > 0.15 then
								data[ai] = c_hard_rock
								data[vi] = c_hard_rock
							else
								data[ai] = c_diamond_block
								data[vi] = c_diamond_block
							end
						end

						if math.random() < ICICHA and (biome == 4 or biome == 5) then
							data[vi] = c_icid
						end
						if math.random() < WORMCHA then
							data[vi] = c_worm
							local bi = area:index(x,y-1,z)
							data[bi] = c_worm
							if math.random(2) == 1 then
								local bbi = area:index(x,y-2,z)
								data[bbi] = c_worm
								if math.random(2) ==1 then
									local bbbi = area:index(x,y-3,z)
									data[bbbi] = c_worm
								end
							end
						end
						if math.random() < STALCHA then
							caverealms:stalactite(x,y,z, area, data)
						end
						if math.random() < CRYSTAL then
							caverealms:crystal_stalactite(x,y,z, area, data, biome)
						end
					end
					--ground
					local bi = area:index(x,y-1,z) --below index
					if data[bi] == c_stone and data[vi] == c_air then --ground
						local ai = area:index(x,y+1,z)
						--place floor material, add plants/decorations
						if biome == 1 then
							data[vi] = c_moss
							if math.random() < GEMCHA then
								-- gems of random size
								local gems = { c_gem1, c_gem2, c_gem3, c_gem4, c_gem5 }
								local gidx = math.random(1, 12)
								if gidx > 5 then
									gidx = 1
								end
								data[ai] = gems[gidx]
							end
						elseif biome == 2 then
							data[vi] = c_lichen
							if math.random() < MUSHCHA then --mushrooms
								data[ai] = c_fungus
							end
							if math.random() < MYCCHA then --mycena mushrooms
								data[ai] = c_mycena
							end
							if math.random() < GIANTCHA then --giant mushrooms
								caverealms:giant_shroom(x, y, z, area, data)
							end
						elseif biome == 3 then
							data[vi] = c_algae
						elseif biome == 4 then
							data[vi] = c_thinice
							local bi = area:index(x,y-1,z)
							data[bi] = c_thinice
							if math.random() < ICICHA then --if glaciated, place icicles
								data[ai] = c_iciu
							end
						elseif biome == 5 then
							data[vi] = c_ice
							local bi = area:index(x,y-1,z)
							data[bi] = c_ice
							if math.random() < ICICHA then --if glaciated, place icicles
								data[ai] = c_iciu
							end
						elseif biome == 6 then
							local bi = area:index(x,y-1,z)
							if deep_cave_shell_type > 0.65 then
								data[bi] = c_obsidian[math.random(1, #c_obsidian)]
								data[vi] = c_obsidian[math.random(1, #c_obsidian)]
							elseif deep_cave_shell_type > 0.35 then
								data[bi] = c_coal_block
								data[vi] = c_coal_block
							else
								data[bi] = c_hard_rock
								data[vi] = c_hard_rock
							end
						else
							print("[caverealms] Unknown cave biome")
						end

						if math.random() < STAGCHA then
							caverealms:stalagmite(x,y,z, area, data)
						end
						if math.random() < CRYSTAL or (biome == 6 and math.random() < CRYSTAL*1.5) then
							caverealms:crystal_stalagmite(x,y,z, area, data, biome)
						end
					end

				end
				nixyz2 = nixyz2 + 1
				nixz = nixz + 1
				vi = vi + 1
			end
			nixz = nixz - sidelen --shift the 2D index back
		end
		nixz = nixz + sidelen --shift the 2D index up a layer
	end

	--send data back to voxelmanip
	vm:set_data(data)
	--calc lighting
	vm:set_lighting({day=0, night=0})
	vm:calc_lighting()
	--write it to world
	vm:write_to_map()

	--local chugent = math.ceil((os.clock() - t1) * 1000) --grab how long it took
	--print ("[caverealms] "..chugent.." ms") --tell people how long
end)


print("[caverealms] loaded!")