forked from paramat/snowdrift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
init.lua
298 lines (262 loc) · 9.14 KB
/
init.lua
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
-- Parameters
snowdrift = {}
snowdrift.upperLimit = 1000
local PRECTIM = 15 -- Time scale for precipitation variation, in minutes
local PRECTHR = 0.35 -- Precipitation noise threshold, -1 to 1:
-- -1 = precipitation all the time
-- 0 = precipitation half the time
-- 1 = no precipitation
local DROPLPOS = 100 -- Raindrop light-tested positions per cycle
-- Maximum number of raindrops spawned per 0.5s
local RAINGAIN = 0.5 -- Rain sound volume
local NISVAL = 8 -- Overcast sky RGB value at night (brightness)
local DASVAL = 159 -- Overcast sky RGB value in daytime (brightness)
local DROPRAD = 24 -- Radius in which drops are created
local spawner_density = 5 -- Square root of the number of particle spawners to be created.
-- Higher numbers = finer light detection, but more network usage
local np_prec = {
offset = 0,
scale = 1,
spread = {x = PRECTIM, y = PRECTIM, z = PRECTIM},
seed = 813,
octaves = 1,
persist = 0,
lacunarity = 2.0,
flags = "defaults"
}
-- These 2 must match biome heat and humidity noise parameters for a world
local np_temp = {
offset = 50,
scale = 50,
spread = {x = 1000, y = 1000, z = 1000},
seed = 5349,
octaves = 3,
persist = 0.5,
lacunarity = 2.0,
flags = "defaults"
}
local np_humid = {
offset = 50,
scale = 50,
spread = {x = 1000, y = 1000, z = 1000},
seed = 842,
octaves = 3,
persist = 0.5,
lacunarity = 2.0,
flags = "defaults"
}
-- End parameters
-- Stuff
local SDSQUARED = spawner_density*spawner_density
local SRANGE = DROPRAD/spawner_density
local difsval = DASVAL - NISVAL
local grad = 14 / 95
local yint = 1496 / 95
local alt_chill = 20 / 90
-- Initialise noise objects to nil
local nobj_temp = nil
local nobj_humid = nil
local nobj_prec = nil
-- Globalstep function
local handles = {}
local volumes = {}
local timer = 0
snowdrift.get_precip = function(pos)
if not pos then return "none" end
pos = {x=math.floor(pos.x+0.5),y=math.floor(pos.y+0.5),z=math.floor(pos.z+0.5)}
nobj_temp = nobj_temp or minetest.get_perlin(np_temp)
nobj_humid = nobj_humid or minetest.get_perlin(np_humid)
nobj_prec = nobj_prec or minetest.get_perlin(np_prec)
local nval_temp = nobj_temp:get2d({x = pos.x, y = pos.z}) - pos.y*alt_chill
local nval_humid = nobj_humid:get2d({x = pos.x, y = pos.z})
local nval_prec = nobj_prec:get2d({x = (os.clock() / 60)%30000, y = 0})
local freeze = (nval_temp) < 28
local precip = nval_prec > PRECTHR and
nval_humid - grad * nval_temp > yint
return (precip and (freeze and "snow" or "rain") or "none")
end
minetest.register_globalstep(function(dtime)
timer = timer + dtime
if timer < 0.75 then
return
end
timer = 0
for _, player in ipairs(minetest.get_connected_players()) do
local player_name = player:get_player_name()
local pos = player:get_pos()
if not pos then return end
local ppos = {x=math.floor(pos.x+0.5),y=math.floor(pos.y+0.5),z=math.floor(pos.z+0.5)}
-- Point just above player, for rounding weirdness that happens at negative y coordinates.
local pposy = ppos.y
local dlight = minetest.get_node_light(ppos, 0.5)
if not dlight then return end -- Light is sometimes nil. Why? Don't ask me.
local skylit = minetest.get_modpath("minetest_systemd") and
minetestd.utils.get_natural_light(pos) or
(minetest.get_node_light(pos, 0.5) - minetest.get_node_light(pos, 0))
if (pposy < snowdrift.upperLimit) then
local pposx = ppos.x
local pposz = ppos.z
local ppos = {x = pposx, y = pposy, z = pposz}
nobj_temp = nobj_temp or minetest.get_perlin(np_temp)
nobj_humid = nobj_humid or minetest.get_perlin(np_humid)
nobj_prec = nobj_prec or minetest.get_perlin(np_prec)
local nval_temp = nobj_temp:get2d({x = pposx, y = pposz}) - pos.y*alt_chill
--altitude chill factor in mg_valleys
local nval_humid = nobj_humid:get2d({x = pposx, y = pposz})
local nval_prec = nobj_prec:get2d({x = (os.clock() / 60)%30000, y = 0})
-- Biome system: Frozen biomes below heat 35,
-- deserts below line 14 * t - 95 * h = -1496
-- h = (14 * t + 1496) / 95
-- h = 14/95 * t + 1496/95
-- where 14/95 is gradient and 1496/95 is y intersection
-- h - 14/95 t = 1496/95 y intersection
-- so area above line is
-- h - 14/95 t > 1496/95
local freeze = nval_temp < 28
-- minetest.chat_send_all(nval_temp)
local precip = nval_prec > PRECTHR and
nval_humid - grad * nval_temp > yint
local prec_abs = nval_prec + 1
local thr_scale = math.max(1 - PRECTHR, 0.05) -- Don't divide by zero
local thr_abs = PRECTHR + 1
local rainfall = (prec_abs - thr_abs) / thr_scale
-- How heavily is it raining (on a scale from 0 to 1 over the bounds we've set)?
rainfall = math.max(rainfall, 0.05) --If we passed the threshhold, at least rain a little
-- UNoccasionally reset player sky
-- if math.random() < 0.1 then
if pposy > -60 and precip then -- Below -60 is dark sky, don't grey the sky here.
-- Set overcast sky
local sval
local time = minetest.get_timeofday()
if time >= 0.5 then
time = 1 - time
end
-- Sky brightness transitions:
-- First transition (24000 -) 4500, (1 -) 0.1875
-- Last transition (24000 -) 5750, (1 -) 0.2396
if time <= 0.1875 then
sval = NISVAL
elseif time >= 0.2396 then
sval = DASVAL
else
sval = math.floor(NISVAL +
((time - 0.1875) / 0.0521) * difsval)
end
-- Set sky to overcast bluish-grey
player:set_sky({r = sval, g = sval, b = math.floor(sval*1.2), a = 255},
"plain", {}, false)
else
-- Reset sky to normal
player:set_sky({}, "regular", {}, true)
end
--end
if (skylit == 0) or freeze or not precip then -- Snow, no weather, or cold = STOP
if handles[player_name] then
-- Stop sound if playing
minetest.sound_stop(handles[player_name])
handles[player_name] = nil
volumes[player_name] = nil
end
else
local vol = RAINGAIN * --Fade out sound as player goes out of open sky.
(skylit/15)^2 *
rainfall
if handles[player_name] then
if volumes[player_name] ~= vol then
minetest.sound_fade(handles[player_name], (vol - volumes[player_name])/2, vol)
volumes[player_name] = vol
end
else
-- Start sound if not playing
local handle = minetest.sound_play(
"snowdrift_rain",
{
to_player = player_name,
gain = vol, -- Fade as we get out of skylit area
loop = true,
}
)
handles[player_name] = handle
volumes[player_name] = vol
end
end
local node = minetest.get_node({x=ppos.x,y=math.floor(pos.y+1.9),z=ppos.z})
if node and minetest.registered_nodes[node.name] then --Don't show particles when underwater.
if minetest.registered_nodes[node.name].liquidtype ~= "none" then
return
end
end
-- Rainfall
local lposx, lposz, spawny, spawnx, spawnz, spos, weather
local ndrops = math.ceil((DROPLPOS/16)*rainfall)
for lpos = 1, SDSQUARED do
lposx = pposx - DROPRAD + ((math.floor(lpos/spawner_density) + 0.5) * 2 * SRANGE)
lposz = pposz - DROPRAD + (((lpos % spawner_density) + 0.5) * 2 * SRANGE)
spos = {x = lposx, y = pposy + 10, z = lposz}
-- minetest.set_node(spos, {name="default:glass"})
weather = snowdrift.get_precip(spos)
if minetest.get_node_light(spos, 0.5) == 15 then
if weather == "rain" then
minetest.add_particlespawner({
amount = ndrops,
time = 5,
minpos = {x=spos.x-SRANGE,y=spos.y,z=spos.z-SRANGE},
maxpos = {x=spos.x+SRANGE,y=spos.y,z=spos.z+SRANGE},
minvel = {x = 0.0, y = -14.0, z = 0.0},
maxvel = {x = 0.0, y = -12.0, z = 0.0},
minacc = {x = 0, y = 0, z = 0},
maxacc = {x = 0, y = 0, z = 0},
minexptime = 2,
maxexptime = 2,
minsize = 2.8,
maxsize = 2.8,
collisiondetection = true,
collision_removal = true,
vertical = true,
texture = "snowdrift_raindrop.png",
playername = player_name
})
elseif weather == "snow" then
minetest.add_particlespawner({
amount = ndrops,
time = 5,
minpos = {x=spos.x-SRANGE,y=spos.y,z=spos.z-SRANGE},
maxpos = {x=spos.x+SRANGE,y=spos.y,z=spos.z+SRANGE},
minvel = {x = -0.5, y = -2.5, z = -0.5},
maxvel = {x = 0.5, y = -2.0, z = 0.5},
minacc = {x = 0, y = 0, z = 0},
maxacc = {x = 0, y = 0, z = 0},
minexptime = 24,
maxexptime = 24,
minsize = 1,
maxsize = 1,
collisiondetection = true,
collision_removal = true,
vertical = false,
texture = "snowdrift_snowflake" ..
math.random(1, 12) .. ".png",
playername = player_name
})
end
end
end
elseif handles[player_name] then
-- Stop sound when player goes above y limit
minetest.sound_stop(handles[player_name])
handles[player_name] = nil
volumes[player_name] = nil
end
end
end)
if minetest.settings:get_bool("snowdrift.enable_abms") then
dofile(minetest.get_modpath("snowdrift").."/abms.lua")
end
-- Stop sound and remove player handle on leaveplayer
minetest.register_on_leaveplayer(function(player)
local player_name = player:get_player_name()
if handles[player_name] then
minetest.sound_stop(handles[player_name])
handles[player_name] = nil
volumes[player_name] = nil
end
end)