code:
# Moon phases
Make your moon follow a realistic cycle

__Important__: This mod requires at least Minetest 5.2.0 for the game's new sky API.
Make sure you have the latest version installed or [update your game](https://www.minetest.net/downloads/).

## Dependencies
This mod has no hard dependencies whatsoever, so you can use it as you will.
If you are also using *climate_api* then it will use the included skybox registration instead of overriding the player configuration. Thus, moon_phases will will be compatible with your weather packs.

## Changes in v2.0
- New classic moon textures!
- Skybox changes. The sky will brighten up during a full moon and turn dark during a new moon.
- Less stored data: Moon cycle now used Minetest's ``get_day_count()`` and only stores a date offset when changed via commands.
- Climate API integration. Use moon phases with your favorite weather mod

## Commands
This mod comes with two commands to print or change the current moon phase.
- Use ``/moonphase`` to view the currently active phase.
- Use ``/set_moonphase <phase>`` to change it. ``<phase>`` has to be a full number between 1 and 8.
- Use ``/set_moonstyle <style>`` to choose a texture preset. ``classic`` will result in a quadratic moon
	inspired by default Minetest. ``realistic`` will result in 256x images of the real moon.

In order to change the phase, you will need the corresponding privilege.
Use ``/grant <player> moonphase`` to grant it.

## Mod Integration
Just like the chat commands, this mod provides a LUA api for accessing the moon phase.
It contains a method called ``moon_phases.get_phase()`` that will return a numeric value representing the current moon phase.
You can also set the phase via ``moon_phases.set_phase(phase)`` where ``phase`` is an integer between 1 and 8.
The texture style of a specific player can be set with ``moon_phases.set_style(player, style)`` where ``style`` referes to either
``classic`` or ``realistic``.

## Configuration
The mod provides the option to change the length of the moon cycle.
By default, the moon texture will change every four (in-game) nights.
This results in a total cycle of 32 days.

You can also set the default texture style for all players. You can choose between the same options as with the ``/set_moonstyle`` command.

## LICENSE
All source code is written by me and licensed under GNU LESSER GENERAL PUBLIC LICENSE version 3.
You can find a copy of that license in the repository.

## Media
All moon textures marked as "classic" are made by Cap for this mod and usable under a *CC BY-SA (3.0)* license.

All included "realistic" moon textures are resized versions of graphics from *NASA's Scientific Visualization Studio* by [Ernie Wright](https://svs.gsfc.nasa.gov/cgi-bin/search.cgi?person=1059).
These images are part of the Public Domain as *CC BY-SA 3.0*.
You can access the entire (high resolution) album on [their website](https://svs.gsfc.nasa.gov/4769#28564). See [NASA's media guidelines](https://www.nasa.gov/multimedia/guidelines/index.html) for more information on licensing.


code:
minetest.register_privilege("moonphase", {
	description = "Change the phase of the moon",
	give_to_singleplayer = false
})

minetest.register_chatcommand("moonphase", {
	description ="Display current moon phase",
	func = function(playername, param)
		local msg = "Current moon phase: " .. moon_phases.get_phase()
			.. "\nRun set_moonphase [1-8] to change this."
		minetest.chat_send_player(playername, msg)
	end
})

minetest.register_chatcommand("set_moonphase", {
	params = "<phase>",
	description = "Set moon phase to given value",
	privs = { moonphase = true },
	func = function(playername, param)
		if param == nil or param == "" then
			minetest.chat_send_player(playername, "Provide a number between 1 and 8")
		else
			local change = moon_phases.set_phase(param)
			if change then
				minetest.chat_send_player(playername, "Moon phase changed successfully")
			else
				minetest.chat_send_player(playername, "Invalid argument. Provide a number between 1 and 8.")
			end
		end
	end
})

minetest.register_chatcommand("set_moonstyle", {
	params = "<style>",
	description = "Set your moon's texture style to the given preset",
	func = function(playername, param)
		if param == nil or param == "" then
			minetest.chat_send_player(playername, "Provide a texture style. Possible styles are classic or realistic")
		else
			local player = minetest.get_player_by_name(playername)
			local change = moon_phases.set_style(player, param)
			if change then
				minetest.chat_send_player(playername, "Moon texture changed successfully")
			else
				minetest.chat_send_player(playername, "Invalid argument. Provide a valid preset.")
			end
		end
	end
})

repo_name: korkut/MeseCraft

license: mit


code:
local mod_climate_api = minetest.get_modpath("climate_api") ~= nil
local mod_skylayer = minetest.get_modpath("skylayer") ~= nil

local modpath = minetest.get_modpath("moon_phases");

local GSCYCLE = 0.5								-- global step cycle in seconds
local DEFAULT_LENGTH = 4					-- default moon cycle length in days
local DEFAULT_STYLE = "classic"		-- default texture style
local PHASE_COUNT = 8							-- number of phases to go through

-- retrieve mod configuration
local PHASE_LENGTH = tonumber(minetest.settings:get("moon_phases_cycle") or DEFAULT_LENGTH)
local TEXTURE_STYLE = minetest.settings:get("moon_phases_style") or DEFAULT_STYLE

local sky_color = {
	"#1d293aff",
	"#1c4b8dff",
	nil,
	"#579dffff",
	nil,
	"#1c4b8dff",
	"#1d293aff",
	"#000000ff"
}

local horizon_color = {
	"#243347ff",
	"#235fb3ff",
	nil,
	"#73aeffff",
	nil,
	"#3079dfff",
	"#173154ff",
	"#000000ff"
}

moon_phases = {}
local phase = 1
local state = minetest.get_mod_storage()

-- calculate current moon phase from date
-- and stored date offset
local function calculate_phase()
	local time = minetest.get_timeofday()
	local day = minetest.get_day_count() + state:get_int("date_offset")
	if time > 0.5 then
		day = day + 1
	end
	return ((math.ceil(day / PHASE_LENGTH) - 1) % PHASE_COUNT) + 1
end

-- return the current moon phase
function moon_phases.get_phase()
	return phase
end

-- set the moon texture of a player to the given phase
local function set_texture(player, phase)
	if not player.get_stars then return end -- check for new sky API
	local meta_data = player:get_meta()
	local style = meta_data:get_string("moon_phases:texture_style")
	if style ~= "classic" and style ~= "realistic" then
		style = TEXTURE_STYLE
	end
	local texture = "moon_" .. phase .. "_" .. style .. ".png"
	local name = "moon_phases:cycle"
	local sky = {}
	sky.sky_data = {
		type = "regular",
		sky_color = {
			night_sky = sky_color[phase],
			night_horizon = horizon_color[phase]
		}
	}
	sky.moon_data = {
		visible = true,
		texture = texture,
		scale = 0.8
	}
	local playername = player:get_player_name()
	if mod_climate_api then
		sky.priority = 0
		climate_api.skybox.add(playername, name, sky)
	elseif mod_skylayer then
		sky.name = name
		skylayer.add_layer(playername, sky)
	else
		player:set_sky(sky.sky_data)
		player:set_moon(sky.moon_data)
	end
end

-- check for day changes
local function handle_time_progression()
	local n_phase = calculate_phase()
	if n_phase ~= phase then
		phase = n_phase
		for _, player in ipairs(minetest.get_connected_players()) do
			set_texture(player, phase)
		end
	end
end

-- set the current moon phase
-- @param phase int Phase between 1 and PHASE_COUNT
function moon_phases.set_phase(nphase)
	nphase = math.floor(tonumber(nphase))
	if (not nphase) or nphase < 1 or nphase > PHASE_COUNT then
		return false
	end
	local day = minetest.get_day_count()
	local date_offset = state:get_int("date_offset")
	local progress = (day + date_offset) % PHASE_LENGTH
	local phase_offset = (nphase - phase + PHASE_COUNT) % PHASE_COUNT
	local add_offset = ((phase_offset * PHASE_LENGTH) + date_offset - progress)
	state:set_int("date_offset", add_offset)
	handle_time_progression()
	return true
end

-- set the moon's texture style for the given player
function moon_phases.set_style(player, style)
	if style ~= nil and style ~= "classic" and style ~= "realistic" then
		return false
	end
	local meta_data = player:get_meta()
	meta_data:set_string("moon_phases:texture_style", style)
	set_texture(player, state:get_int("phase"))
	return true
end

-- set the moon texture of newly joined player
minetest.register_on_joinplayer(function(player)
	set_texture(player, phase)
end)

-- check for day changes and call handlers
local timer = math.huge
minetest.register_globalstep(function(dtime)
	timer = timer + dtime
	if timer < GSCYCLE then return end
	handle_time_progression()
	timer = 0
end)

-- make moon phase available to weather effects
if mod_climate_api then
	climate_api.register_global_influence("moonphase", moon_phases.get_phase)
end

-- include API for chat commands
dofile(modpath .. "/commands.lua")


code:
name = moon_phases
title = Moon Phases
author = TestificateMods
description = """
Changes the moon to follow a cycle between eight different phases.
Expect the sky to change every four nights (or configure a custom schedule).
Includes realistic 256x256px moon textures from NASA photographies as well as
square vanilla themed 16x16px textures

Requires at least Minetest 5.2.0
"""
optional_depends = climate_api, skylayer


code:
# The moon will switch to the next phase every X days.
moon_phases_cycle (Change moon phase every X days) int 4

# Use classic for a default-inspired set of textures.
# Use realistic for a set of down-scaled NASA photography.
moon_phases_style (Choose a default texture style) enum classic classic,realistic


code:
# Regional Weather
A weather pack for [__Climate API__](https://github.com/t-affeldt/climate_api) by Till Affeldt (TestificateMods)

![](https://raw.githubusercontent.com/t-affeldt/regional_weather/master/screenshot.webp)

Not every biome is the same and neither should their weather be.
Regional Weather controls its effects with the local climate in mind.
Experience the humid air of the rain forest and harsh desert sandstorms.

## Troubleshooting
Regional Weather depends on [Climate API](https://github.com/t-affeldt/climate_api) in order to function. Generally speaking, most mods should be compatible to it.

If you notice __odd movement speeds__ or jump heights of players, you should check for mods that also modify player physics. Use a compatibility mod like [player_monoids](https://github.com/minetest-mods/player_monoids) or [playerphysics](https://forum.minetest.net/viewtopic.php?t=22172) to get rid of this problem. This requires the conflicting mod to also support the chosen compatibility layer.

Mods that __modify the sky__ (including skybox, moon, sun, stars and clouds) are sadly not fully compatible because they conflict with Climate API's sky system. You should deactivate the sky features in either mod. You can do this in Climate API's settings using the ``Override the skybox`` option. If you're a mod maker then you can also optionally depend on climate_api and use ``climate_api.skybox.add_layer(playername, layer_name, options)`` to register your skybox change in a compatible way. Note that you need __at least Minetest v5.2.0__ for skybox changes to have any effect.

Conflicting skybox changes include the ``weather`` mod included in vanilla __Minetest Game__. You will want to disable that mod in order to use the more advanced cloud system introduced by Climate API. Head to ``Settings → All Settings → Games → Minetest Game`` and set ``Enable weather`` to ``Disabled``. This setting will only exist if you are using Minetest Game v5.2.0 or higher.

If you experience __performance issues__, the *Performance* section of Climate API's configuration section is a great place to start looking for a solution.

The following mods are recommended to be installed alongside Regional Weather:
- [Climate API](https://github.com/t-affeldt/climate_api) (required): The necessary weather engine that this mod is built upon
- [Moon Phases](https://github.com/t-affeldt/minetest_moon_phase): Complements weather effects with dynamic sky changes and a full moon cycle
- [Sailing Kit](https://github.com/t-affeldt/sailing_kit) (Fork): Uses Climate API's new wind system to sail across the sea.
- [Lightning](https://github.com/minetest-mods/lightning): Adds to heavy rain by enabling additional lightning effects
- [Farming](https://github.com/minetest/minetest_game/tree/master/mods/farming) (as part of MTG) or [Farming Redo](https://forum.minetest.net/viewtopic.php?t=9019): Add farmland and crops to grow food. Farmland wil turn wet during rain effects.
- [Fire](https://github.com/minetest/minetest_game/tree/master/mods/fire) (as part of MTG): Adds fires that can be caused by lightning strikes and other effects and will be extinguished during rain effects.
- [Pedology](https://forum.minetest.net/viewtopic.php?f=11&t=9429) Adds a lot of nodes with dynamic wetness and dryness states.
- [Ambience](https://notabug.org/TenPlus1/ambience): Plays some nice ambient sound effects based on where you are.

For easier installation, you can get a lot of these mods as part of my [Climate Modpack](https://github.com/t-affeldt/climate).

## Configuration Options
You can find all mod configuration options in your Minetest launcher.
Go to ``Settings → All Settings → Mods → regional_weather`` to change them.
Also check out the options inside the ``climate_api`` section for additional configuration options, including performance tweaks and feature switches.

### Features
- ``Place snow layers`` (default true):
	If set to true, snow layers will stack up during snowy weather.
- ``Freeze river water`` (default true):
	If set to true, river water sources will freeze at low temperatures and melt when it gets warmer again.
	This process does not affect regular ice blocks because it adds its own temporary ones.
- ``Place rain puddles`` (default true):
	If set to true, water puddles will form during rain or when snow layers have melted.
- ``Hydrate farmland`` (default true):
	If set to true, rain will cause dry farmland to turn wet.
	Requires *farming* or *farming_redo* mod.
- ``Extinguish fire`` (bool true):
	If set to true, fires will be extinguished during rain showers.
	Requires *fire* mod.
- ``Wetten pedology nodes`` (default true):
	If set to true, rain will wetten or dry nodes from pedology mod.
	Requires *pedology* mod.

### World Configuration
- ``Maximum height of weather effects`` (default 120):
	No visual effects will be applied above this height.
	This value defaults to normal cloud height (120 nodes above sea level).
- ``Minimum height of weather effects`` (default -50):
	No visual effects will be applied below this height.
	This will prevent unwanted visuals within large underground caves.
- ``Cloud height`` (default 120)
	Average height of cloud bases
- ``Cloud height variation`` (default 40)
	Maxmial variation of cloud height from base value

## License information
### Source Code
Unless otherwise stated, this source code is written entirely by myself.
You are free to use it under a GNU Lesser General Public License version 3.
You can find respective rights and conditions in the attached [LICENSE](https://github.com/t-affeldt/regional_weather/blob/master/LICENSE.md) file.
The entire source code is available on [Github](https://github.com/t-affeldt/regional_weather).

### Particles
- Hail textures: *CC BY-SA (3.0)* made by me
- Snow flake textures: *CC BY-SA (3.0)* by paramat, found in snowdrift mod at https://github.com/paramat/snowdrift
- Snow composite texture: *CC BY-SA (3.0)* by Cap, created from aforementioned snow flakes by paramat (please credit original artist as well)
- Rain textures: *CC BY-SA (3.0)* by Cap (an original design for this mod)

### Block Textures
- Puddle textures: *CC BY-SA (3.0)* by Cap
- Snow layers and ice block using textures from *default* (not included)

### Sounds
- Heavy Rain sounds: *CC0* by Q.K., taken from mymonths at https://github.com/minetest-mods/mymonths/tree/master/sounds
- Light Rain sounds: *CC BY 3.0* by Arctura from https://freesound.org/people/Arctura/sounds/34065/
- Wind sound: *CC BY (3.0)* by InspectorJ from https://freesound.org/people/InspectorJ/sounds/376415/
- Hail sound: *CC0* by ikayuka from https://freesound.org/people/ikayuka/sounds/240742/
- Puddle footstep sound: *CC0* by swordofkings128 from https://freesound.org/people/swordofkings128/sounds/398032/

### HUD Overlays
- Frost HUD: *CC BY-SA (3.0)* by Cap
- Original texture for sand storm HUD: *CC0* from https://freestocktextures.com/texture/dirty-baking-paper,1202.html, edits by me under *CC0* as well

### Assets in screenshots
- Screenshots and editing: *CC BY-SA (3.0)* by me
- Logos and artwork: *CC BY-SA (3.0)* by Cap
- Lato Font (for the Logo): *OFL* by Łukasz Dziedzic from http://www.latofonts.com/lato-free-fonts/
- Source Sans Pro (for the subtitles): *OFL*, see https://fonts.google.com/specimen/Source+Sans+Pro
- Used texture pack: Polygonia (128px edition) *CC BY-SA (4.0)* by Lokrates. See https://forum.minetest.net/viewtopic.php?f=4&t=19043

### Full License Conditions
- [GNU Lesser General Public License version 4](https://github.com/t-affeldt/regional_weather/blob/master/LICENSE.md)
- [Creative Commons Licenses](https://creativecommons.org/licenses/)
- [SIL Open Font License](https://opensource.org/licenses/OFL-1.1)


code:
if not regional_weather.settings.fire then return end
if not minetest.get_modpath("fire") then return end

climate_api.register_abm({
	label			= "extinguish fire at high humidity",
	nodenames = { "fire:basic_flame" },
	neighbors	= { "air" },
	interval	= 10,
	chance		= 2,
	catch_up	= false,

	 conditions	= {
		 min_height		= regional_weather.settings.min_height,
		 max_height		= regional_weather.settings.max_height,
		 min_humidity	= 55,
		 max_heat			= 85,
		 daylight			= 15,
		 indoors			= false
	 },

	 action = function (pos, node, env)
		 minetest.set_node(pos, { name = "air" })
	 end
})


code:
if not regional_weather.settings.ice
or not minetest.get_modpath("default")
or default.node_sound_glass_defaults == nil
then return end

local BLOCK_NAME = "regional_weather:ice"

local S = regional_weather.i18n

minetest.register_node(BLOCK_NAME, {
	description = S("Thin Ice"),
	drawtype = "glasslike",
	tiles = {"(default_ice.png^[colorize:#ffffff:50)^[opacity:200"},
	paramtype = "light",
	groups = {
		cracky = 3,
		cools_lava = 1,
		slippery = 3,
		dig_immediate = 2,
		melts = 1
	},
	freezemelt = "default:river_water_source",
	sounds = default.node_sound_glass_defaults(),
	use_texture_alpha = "blend",
	drop = "",
	on_destruct = function(pos)
		-- asynchronous to avoid destruction loop
		minetest.after(0, function(pos)
			if minetest.get_node(pos).name ~= "air" then return end
			minetest.set_node(pos, { name = "default:river_water_source" })
		end, pos)
	end
})

climate_api.register_abm({
	label			= "freeze river water",
	nodenames = { "default:river_water_source" },
	neighbors	= { "air" },
	interval	= 25,
	chance		= 3,
	catch_up	= false,

	 conditions	= {
		 min_height		= regional_weather.settings.min_height,
		 max_height		= regional_weather.settings.max_height,
		 max_heat			= 25,
		 daylight			= 15,
		 indoors			= false
	 },

	 action = function (pos, node, env)
		 minetest.set_node(pos, { name = BLOCK_NAME })
	 end
})

climate_api.register_abm({
	label			= "unfreeze river water",
	nodenames = { BLOCK_NAME },
	interval	= 25,
	chance		= 4,
	catch_up	= true,

	 conditions	= {
		 min_height		= regional_weather.settings.min_height,
		 max_height		= regional_weather.settings.max_height,
		 min_heat			= 40,
		 daylight			= 15,
		 indoors			= false
	 },

	 action = function (pos, node, env)
		 minetest.set_node(pos, { name = "default:river_water_source" })
	 end
})


code:
if not regional_weather.settings.pedology
or not minetest.get_modpath("pedology")
then return end

climate_api.register_abm({
	label			= "wetten or dry pedology nodes",
	nodenames = { "group:sucky" },
	neighbors = { "air" },
	interval	= 25,
	chance		= 30,
	catch_up	= false,

	conditions	= {
		min_height		= regional_weather.settings.min_height,
		max_height		= regional_weather.settings.max_height,
		min_heat			= 25,
		daylight			= 15,
		indoors				= false,
	},

	action = function (pos, node, env)
		local wetness = minetest.get_item_group(node.name, "wet") or 0
		if wetness < 2 and env.humidity > 55 then
			pedology.wetten(pos)
		elseif wetness > 0 and wetness < 3 and env.humidity < 40 then
			pedology.dry(pos)
		end
	end
})


code:
local BLOCK_PREFIX = "regional_weather:puddle_"
local VARIANT_COUNT = 39
local CHECK_DISTANCE = 4
local MAX_AMOUNT = 3

local GROUND_COVERS = {
	"group:soil",
	"group:stone",
	"group:sand",
	"group:wood",
	"default:permafrost",
	"default:permafrost_with_moss",
	"default:permafrost_with_stones"
}

local S = regional_weather.i18n

-- clean up puddles if disabled
if not regional_weather.settings.puddles then
	-- set all puddle nodes to air
	minetest.register_alias("regional_weather:puddle", "air")
	for i = 1, VARIANT_COUNT do
		for flip = 0, 1 do
			local name = BLOCK_PREFIX .. i
			if flip == 1 then
				name = name .. "_flipped"
			end
			minetest.register_alias(name, "air")
		end
	end

	-- return air instead of a puddle
	function regional_weather.get_random_puddle()
		return { name = "air" }
	end

	-- end puddle execution
	return
end

local node_box = {
	type  = "fixed",
	fixed = { -0.5, -0.5, -0.5, 0.5, -0.49, 0.5 }
}

local apply_water_group
if regional_weather.settings.puddles_water then
	apply_water_group = 1
end

for i = 1, VARIANT_COUNT do
	for flip = 0, 1 do
		local name = BLOCK_PREFIX .. i
		local index = i
		if i < 10 then index = "0" .. i end
		local texture = "weather_puddle_" .. index .. ".png^[opacity:128"
		if flip == 1 then
			name = name .. "_flipped"
			texture = texture .. "^[transformFX"
		end
		minetest.register_node(name, {
			description = S("Puddle"),
			tiles = { texture },
			drawtype = "nodebox",
			pointable = false,
			buildable_to = true,
			floodable = true,
			walkable = false,
			sunlight_propagates = true,
			paramtype = "light",
			paramtype2 = "facedir",
			use_texture_alpha = "blend",
			node_box = node_box,
			groups = {
				not_in_creative_inventory = 1,
				crumbly = 3,
				attached_node = 1,
				slippery = 1,
				flora = 1,
				water = apply_water_group,
				weather_puddle = 1
			},
			drop = "",
			sounds = {
				footstep = {
					name = "weather_puddle",
					gain = 0.8
				}
			}
		})
	end
end

minetest.register_alias("regional_weather:puddle", BLOCK_PREFIX .. "14")

function regional_weather.get_random_puddle()
	local index = math.random(1, VARIANT_COUNT)
	local rotation = math.random(0, 3) * 90
	local flip = math.random(0, 1)
	local name = BLOCK_PREFIX .. index
	if flip == 1 then
		name = name .. "_flipped"
	end
	local param2 = minetest.dir_to_facedir(minetest.yaw_to_dir(rotation))
	return { name = name, param2 = param2 }
end

-- Makes Puddles when raining
climate_api.register_abm({
	label			= "create rain puddles",
	nodenames	= GROUND_COVERS,
	neighbors	= { "air" },
	interval	= 8,
	chance		= 150,
	catch_up	= false,

	 conditions	= {
		 min_height		= regional_weather.settings.min_height,
		 max_height		= regional_weather.settings.max_height,
		 min_humidity	= 55,
		 min_heat			= 30,
		 daylight			= 15,
		 indoors			= false
	 },

	 pos_override = function(pos)
		 return vector.add(pos, { x = 0, y = 1, z = 0 })
	 end,

   action = function (pos, node, env)
		 -- only override air nodes
		 if minetest.get_node(pos).name ~= "air" then return end
		 -- do not place puddle if area is not fully loaded
		 if minetest.find_node_near(pos, CHECK_DISTANCE, "ignore") then return end
		 -- do not place puddle if already enpugh puddles
		 local pos1 = vector.add(pos, { x = -CHECK_DISTANCE, y = -1, z = -CHECK_DISTANCE })
		 local pos2 = vector.add(pos, { x =  CHECK_DISTANCE, y =  1, z =  CHECK_DISTANCE })
		 local preplaced = minetest.find_nodes_in_area(pos1, pos2, "group:weather_puddle")
		 if preplaced ~= nil and #preplaced >= MAX_AMOUNT then return end
		 minetest.set_node(pos, regional_weather.get_random_puddle())
	 end
})

-- Makes puddles dry up when not raining
climate_api.register_abm({
	label = "remove rain puddles",
	nodenames	= { "group:weather_puddle" },
	interval	= 25,
	chance		= 30,
	catch_up	= true,

	action = function (pos, node, env)
		minetest.remove_node(pos)
	end
})


code:
local BLOCK_PREFIX = "regional_weather:snow_cover_"
local CHECK_DISTANCE = 5
local MAX_AMOUNT = 20

local S = regional_weather.i18n

if not minetest.get_modpath("default")
or default.node_sound_snow_defaults == nil
or not regional_weather.settings.snow then
	for i = 1,5 do
		minetest.register_alias(BLOCK_PREFIX .. i, "air")
	end
	return
end

local destruction_handler = function(pos)
	pos.y = pos.y - 1
	if minetest.get_node(pos).name == "default:dirt_with_snow" then
		minetest.set_node(pos, {name = "default:dirt_with_grass"})
	end
end

for i = 1,5 do
	local node_box = {
		type  = "fixed",
		fixed = {-0.5, -0.5, -0.5, 0.5, 0.2*i - 0.5, 0.5}
	}

	minetest.register_node(BLOCK_PREFIX .. i, {
		description = S("Snow Cover"),
		tiles = { "default_snow.png" },
		drawtype = "nodebox",
		buildable_to = i < 3,
		floodable = true,
		walkable = i > 3,
		paramtype = "light",
		node_box = node_box,
		groups = {
			not_in_creative_inventory = 1,
			crumbly = 3,
			falling_node = 1,
			snowy = 1,
			weather_snow_cover = i
		},
		sounds = default.node_sound_snow_defaults(),
		drop = "default:snow " .. math.ceil(i / 2),
		on_construct = function(pos)
			pos.y = pos.y - 1
			if minetest.get_node(pos).name == "default:dirt_with_grass" then
				minetest.set_node(pos, {name = "default:dirt_with_snow"})
			end
		end,
		on_destruct = destruction_handler,
		on_flood = destruction_handler
	})
end

climate_api.register_abm({
	label			= "create snow covers",
	nodenames	= {
		"group:soil",
		"group:leaves",
		"group:stone",
		"default:snowblock",
		"group:coverable_by_snow"
	},
	neighbors	= { "air" },
	interval	= 25,
	chance		= 80,
	catch_up	= false,

	 conditions	= {
		 min_height		= regional_weather.settings.min_height,
		 max_height		= regional_weather.settings.max_height,
		 min_humidity	= 55,
		 max_heat			= 30,
		 not_biome		= {
			"cold_desert",
			"cold_desert_ocean",
			"desert",
			"desert_ocean",
			"sandstone_desert",
			"sandstone_desert_ocean"
		},
		daylight = 15,
		indoors = false
	 },

	 pos_override = function(pos)
		 return vector.add(pos, { x = 0, y = 1, z = 0 })
	 end,

   action = function (pos, node, env)
		 -- only override air nodes
		 if node.name ~= "air" then return end
		 -- do not place snow if area is not fully loaded
		 if minetest.find_node_near(pos, CHECK_DISTANCE, "ignore") then return end
		 -- do not place snow if already enpugh snow
		 local pos1 = vector.add(pos, { x = -CHECK_DISTANCE, y = -1, z = -CHECK_DISTANCE })
		 local pos2 = vector.add(pos, { x =  CHECK_DISTANCE, y =  1, z =  CHECK_DISTANCE })
		 local preplaced = minetest.find_nodes_in_area(pos1, pos2, "group:weather_snow_cover")
		 if preplaced ~= nil and #preplaced >= MAX_AMOUNT then return end
		 minetest.set_node(pos, { name = BLOCK_PREFIX .. "1" })
	 end
})

if regional_weather.settings.snow_griefing then
	climate_api.register_abm({
		label			= "replace flora with snow covers and stack covers higher",
		nodenames	= {
			"group:flora",
			"group:grass",
			"group:plant",
			"group:weather_snow_cover"
		},
		neighbors	= { "air" },
		interval	= 25,
		chance		= 160,
		catch_up	= false,

		 conditions	= {
			 min_height		= regional_weather.settings.min_height,
			 max_height		= regional_weather.settings.max_height,
			 min_humidity	= 55,
			 max_heat			= 30,
			 not_biome		= {
				"cold_desert",
				"cold_desert_ocean",
				"desert",
				"desert_ocean",
				"sandstone_desert",
				"sandstone_desert_ocean"
			},
			daylight = 15,
			indoors = false
		 },

	   action = function (pos, node, env)
			 local value = minetest.get_item_group(node.name, "weather_snow_cover") or 0
			 if value == 0 then
				 -- do not override plants unless marked as buildable_to
				 local def = minetest.registered_nodes[node.name]
				 if def == nil or not def.buildable_to then return end
				 -- do not override plants of the frost_resistance group
				 local resistance = minetest.get_item_group(node.name, "frost_resistance") or 0
				 if resistance > 0 then return end
			 end
			 -- do not place snow if area is not fully loaded
			 if minetest.find_node_near(pos, CHECK_DISTANCE, "ignore") then return end
			 -- do not place snow if already enpugh snow
			 local pos1 = vector.add(pos, { x = -CHECK_DISTANCE, y = -1, z = -CHECK_DISTANCE })
			 local pos2 = vector.add(pos, { x =  CHECK_DISTANCE, y =  1, z =  CHECK_DISTANCE })
			 local preplaced = minetest.find_nodes_in_area(pos1, pos2, "group:weather_snow_cover")
			 if preplaced ~= nil and #preplaced >= MAX_AMOUNT then return end
			 if value < 5 then
				 minetest.set_node(pos, { name = BLOCK_PREFIX .. (value + 1) })
			 end
		 end
	})
end

climate_api.register_abm({
	label			= "melt snow covers",
	nodenames	= { "group:weather_snow_cover" },
	interval	= 25,
	chance		= 85,
	catch_up	= true,

   action = function (pos, node, env)
			local value = minetest.get_item_group(node.name, "weather_snow_cover")
			if value == nil then value = 0 end
			if value > 1 then
				minetest.set_node(pos, { name = BLOCK_PREFIX .. (value - 1) })
			elseif regional_weather.settings.puddles then
				minetest.set_node(pos, regional_weather.get_random_puddle())
			else
				minetest.set_node(pos, { name = "air" })
			end
	 end
})


code:
if not regional_weather.settings.soil then return end
if not minetest.get_modpath("farming") then return end

if farming ~= nil and farming.mod == "redo" then
	climate_api.register_abm({
		label			= "wetten soil at high humidity",
		nodenames = { "farming:soil" },
		interval	= 8,
		chance		= 2,
		catch_up	= false,

		 conditions	= {
			 min_height		= regional_weather.settings.min_height,
			 max_height		= regional_weather.settings.max_height,
			 min_humidity	= 55,
			 min_heat			= 30,
			 daylight			= 15,
			 indoors			= false
		 },

	   action = function (pos, node, env)
			 minetest.set_node(pos, { name = "farming:soil_wet" })
		 end
	})

else
	climate_api.register_abm({
		label			= "wetten fields at high humidity",
		nodenames = { "group:field" },
		interval	= 8,
		chance		= 2,
		catch_up	= false,

		 conditions	= {
			 min_height		= regional_weather.settings.min_height,
			 max_height		= regional_weather.settings.max_height,
			 min_humidity	= 55,
			 min_heat			= 30,
			 daylight			= 15,
			 indoors			= false
		 },

	   action = function (pos, node, env)
			 local node_def = minetest.registered_nodes[node.name] or nil
			 local wet_soil = node_def.soil.wet or nil
			 if wet_soil == nil then return end
			 minetest.set_node(pos, { name = wet_soil })
		 end
	})
end


code:
--[[
# Lightning Effect
Use this effect to cause lightning strikes.
Requires lightning mod in order to function. Uses default lightning configuration.
Expects an integer indicating a chance (between 0 and 1) for lightning to strike (per cycle and player).
]]

if not minetest.get_modpath("lightning") then return end
if regional_weather.settings.lightning == 0 then return end

local EFFECT_NAME = "regional_weather:lightning"

lightning.auto = false

local rng = PcgRandom(82492402425)

-- simplified position chooser by Auke Kok (sofar) taken from lightning mod itself
-- see https://github.com/minetest-mods/lightning/blob/master/init.lua#L53-L91
-- "lightning" is licensed under GNU Lesser Public License 2.1 or any later revision
local function choose_pos(pos)
	pos.x = math.floor(pos.x - (lightning.range_h / 2) + rng:next(1, lightning.range_h))
	pos.y = pos.y + (lightning.range_v / 2)
	pos.z = math.floor(pos.z - (lightning.range_h / 2) + rng:next(1, lightning.range_h))

	local b, pos2 = minetest.line_of_sight(pos, {x = pos.x, y = pos.y - lightning.range_v, z = pos.z}, 1)

	-- nothing but air found
	if b then
		return nil
	end

	local n = minetest.get_node({x = pos2.x, y = pos2.y - 1/2, z = pos2.z})
	if n.name == "air" or n.name == "ignore" then
		return nil
	end

	return pos2
end

local function handle_effect(player_data)
	for playername, data in pairs(player_data) do
		local chance = 0
		for weather, value in pairs(data) do
			if type(value) ~= "number" then
				value = 1/20
			end
			chance = chance + value - (chance * value)
		end
		local random = math.random()
		if random <= chance * regional_weather.settings.lightning then
			local player = minetest.get_player_by_name(playername)
			local ppos = player:get_pos()
			local position = choose_pos(ppos)
			if position ~= nil then
				lightning.strike(position)
			end
		end
	end
end

climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
climate_api.set_effect_cycle(EFFECT_NAME, climate_api.LONG_CYCLE)


code:
--[[
# Player Speed Effect
Use this effect to modify a player's movement speed.
Expects a numeric value that will be multiplied with the current speed physics.
]]

if not regional_weather.settings.player_speed then return end

local EFFECT_NAME = "regional_weather:speed_buff"

local function handle_effect(player_data)
	for playername, data in pairs(player_data) do
		local player = minetest.get_player_by_name(playername)
		local product = 1
		for weather, value in pairs(data) do
			product = product * value
		end
		climate_api.player_physics.add(EFFECT_NAME, player, "speed", product)
	end
end

local function remove_effect(player_data)
	for playername, data in ipairs(player_data) do
		local player = minetest.get_player_by_name(playername)
		climate_api.player_physics.remove(EFFECT_NAME, player, "speed")
	end
end

climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
climate_api.register_effect(EFFECT_NAME, remove_effect, "stop")
climate_api.set_effect_cycle(EFFECT_NAME, climate_api.SHORT_CYCLE)


code:
local name = "regional_weather:ambient"

local conditions = {}

-- see https://en.wikipedia.org/wiki/Cloud_base
local function calc_cloud_height(heat, humidity, dewpoint)
	local base = regional_weather.settings.cloud_height
	-- much lower scale like 20 instead of 1000 fitting for Minetest
	local scale = regional_weather.settings.cloud_scale
	local spread = heat - dewpoint
	local variation = spread / 4.4 * scale * 0.3
	return base + climate_api.utility.rangelim(variation, -scale, scale)
end

local function generate_effects(params)
	local override = {}

	local cloud_height = calc_cloud_height(params.heat, params.humidity, params.dewpoint)
	local wind = climate_api.environment.get_wind({ x = 0, y = cloud_height, z = 0 })

	local skybox = {priority = 10}
	skybox.cloud_data = {
		density = climate_api.utility.rangelim(params.humidity / 100, 0.15, 0.65),
		speed = wind,
		thickness = climate_api.utility.rangelim(params.base_humidity * 0.2, 1, 18),
		height = cloud_height,
		ambient = "#0f0f1050"
	}

	if params.height > -100 and params.humidity > 40 then
		skybox.cloud_data.color  = "#b2a4a4b0"
	end

	if params.height > -100 and params.humidity > 65 then
		skybox.sky_data = {
			type = "regular",
			clouds = true,
			sky_color = {
				day_sky = "#6a828e",
				day_horizon = "#5c7a8a",
				dawn_sky = "#b2b5d7",
				dawn_horizon = "#b7bce1",
				night_sky = "#2373e1",
				night_horizon = "#315d9b"
			}
		}
		skybox.cloud_data.color = "#828e97b5"
		skybox.cloud_data.ambient = "#20212250"
	end

	override["climate_api:skybox"] = skybox

	local movement = params.player:get_player_velocity()
	local movement_direction
	if (vector.length(movement) < 0.1) then
		movement_direction = vector.new(0, 0, 0)
	else
		movement_direction = vector.normalize(movement)
	end
	local vector_product = vector.dot(movement_direction, wind)
	local movement_penalty = climate_api.utility.sigmoid(vector_product, 1.5, 0.15, 0.9) + 0.2
	override["regional_weather:speed_buff"] = movement_penalty
	return override
end

climate_api.register_weather(name, conditions, generate_effects)


code:
local name = "regional_weather:deep_cave"

local conditions = {
	max_daylight	= minetest.LIGHT_MAX,
	max_height		= -100
}

local effects = {}
effects["climate_api:skybox"] = {
	sky_data = {
		type = "plain",
		base_color = { r = 0, g = 0, b = 0 },
		clouds = false
	},
	sun_data = {
		visible = false,
		sunrise_visible = false
	},
	moon_data = { visible = false },
	star_data = { visible = false },
	priority = 100
}

climate_api.register_weather(name, conditions, effects)


code:
local name = "regional_weather:fog"

local conditions = {
	min_height = regional_weather.settings.min_height,
	max_height = regional_weather.settings.max_height,
	min_humidity = 40,
	max_humidity = 50,
	max_windspeed = 2,
	min_heat = 40,
	max_heat = 50
}

local effects = {}

effects["climate_api:skybox"] = {
	sky_data = {
		clouds = true
	},
	cloud_data = {
		density = 1,
		color = "#ffffff80",
		thickness = 40,
		speed = {x=0,y=0,z=0}
	},
	priority = 50
}

local function generate_effects(params)
	local override = {}
	override["climate_api:skybox"] = {
		cloud_data = {
			height = params.player:get_pos().y - 20
		}
	}
	return climate_api.utility.merge_tables(effects, override)
end

climate_api.register_weather(name, conditions, generate_effects)


code:
local name = "regional_weather:fog_heavy"

local conditions = {
	min_height = regional_weather.settings.min_height * 0.9,
	max_height = regional_weather.settings.max_height * 0.9,
	min_humidity = 43,
	max_humidity = 47,
	max_windspeed = 1.5,
	min_heat = 43,
	max_heat = 47
}

local effects = {}

effects["climate_api:hud_overlay"] = {
	file = "weather_hud_fog.png^[opacity:100",
	z_index = -200,
	color_correction = true
}

effects["climate_api:skybox"] = {
	sky_data = {
		type = "plain",
		base_color = "#c0c0c08f"
	},
	cloud_data = {
		color = "#ffffffc0",
	},
	priority = 51
}

climate_api.register_weather(name, conditions, effects)


code:
local name = "regional_weather:hail"

local conditions = {
	min_height			= regional_weather.settings.min_height,
	max_height			= regional_weather.settings.max_height,
	min_heat				= 30,
	max_heat				= 45,
	min_humidity		= 65,
	min_windspeed		= 2.5,
	indoors					= false,
	not_biome				= {
		"cold_desert",
		"cold_desert_ocean",
		"desert",
		"desert_ocean",
		"sandstone_desert",
		"sandstone_desert_ocean"
	}
}

local effects = {}

effects["climate_api:damage"] = {
	rarity = 15,
	value = 3,
	check = {
		type = "raycast",
		height = 7,
		velocity = 20
	}
}

effects["climate_api:sound"] = {
	name = "weather_hail",
	gain = 1
}

effects["regional_weather:lightning"] = 1 / 30

local textures = {}
for i = 1,5 do
	textures[i] = "weather_hail" .. i .. ".png"
end

effects["climate_api:particles"] = {
	boxsize = { x = 18, y = 0, z = 18 },
	v_offset = 7,
	velocity = 20,
	amount = 6,
	expirationtime = 0.7,
	texture = textures,
	glow = 5
}

climate_api.register_weather(name, conditions, effects)


code:
local name = "regional_weather:pollen"

local conditions = {
	min_height			= regional_weather.settings.min_height,
	max_height			= regional_weather.settings.max_height,
	min_heat				= 40,
	min_humidity		= 30,
	max_humidity		= 40,
	max_windspeed		= 2,
	indoors					= false,
	has_biome				= {
		"default",
		"deciduous_forest",
		"deciduous_forest_ocean",
		"deciduous_forest_shore",
		"grassland",
		"grassland_dunes",
		"grassland_ocean",
		"snowy_grassland",
		"snowy_grassland_ocean",
		"grassy",
		"grassy_ocean",
		"grassytwo",
		"grassytwo_ocean",
		"mushroom",
		"mushroom_ocean",
		"plains",
		"plains_ocean",
		"sakura",
		"sakura_ocean"
	}
}

local effects = {}

effects["climate_api:particles"] = {
	boxsize = { x = 24, y = 0, z = 24 },
	vbox = 5,
	v_offset = -1,
	velocity = -0.1,
	acceleration = -0.03,
	expirationtime = 5,
	size = 0.8,
	texture = "weather_pollen.png",
	glow = 2
}

climate_api.register_weather(name, conditions, effects)


code:
local name = "regional_weather:rain"

local conditions = {
	min_height		= regional_weather.settings.min_height,
	max_height		= regional_weather.settings.max_height,
	min_heat			= 35,
	min_humidity	= 50,
	max_humidity	= 65,
	indoors				= false
}

local effects = {}

effects["climate_api:sound"] = {
	name = "weather_rain",
	gain = 1.5
}

effects["climate_api:particles"] = {
	boxsize = { x = 18, y = 2, z = 18 },
	v_offset = 6,
	expirationtime = 1.6,
	size = 2,
	amount = 15,
	velocity = 6,
	acceleration = 0.05,
	texture = "weather_raindrop.png",
	glow = 5
}

climate_api.register_weather(name, conditions, effects)


code:
local name = "regional_weather:rain_heavy"

local conditions = {
	min_height		= regional_weather.settings.min_height,
	max_height		= regional_weather.settings.max_height,
	min_heat			= 40,
	min_humidity	= 65,
	indoors				= false
}

local effects = {}

effects["climate_api:skybox"] = {
	cloud_data = {
		color = "#5e676eb5"
	},
	priority = 11
}

effects["climate_api:sound"] = {
	name = "weather_rain_heavy",
	gain = 1
}

effects["regional_weather:lightning"] = 1 / 20

effects["climate_api:particles"] = {
	boxsize = { x = 18, y = 0, z = 18 },
	v_offset = 7,
	velocity = 7,
	amount = 17,
	expirationtime = 1.2,
	minsize = 25,
	maxsize = 35,
	texture = {
		"weather_rain.png",
		"weather_rain.png",
		"weather_rain_medium.png"
	},
	glow = 5
}

climate_api.register_weather(name, conditions, effects)


code:
local name = "regional_weather:sandstorm"

local conditions = {
	min_height = regional_weather.settings.min_height,
	max_height = regional_weather.settings.max_height,
	min_heat				= 50,
	max_humidity		= 25,
	min_windspeed		= 4.5,
	has_biome				= {
		"cold_desert",
		"cold_desert_ocean",
		"desert",
		"desert_ocean",
		"sandstone_desert",
		"sandstone_desert_ocean"
	}
}

local effects = {}

effects["climate_api:hud_overlay"] = {
	file = "weather_hud_sand.png",
	z_index = -100,
	color_correction = true
}

effects["climate_api:damage"] = {
	rarity = 3,
	value = 1,
	check = {
		type = "raycast",
		height = 0,
		velocity = 0.3
	}
}

effects["climate_api:particles"] = {
	boxsize = { x = 8, y = 4.5, z = 8 },
	velocity = 0.6,
	acceleration = -0.2,
	amount = 12,
	expirationtime = 0.7,
	size = 25,
	texture = {
		"weather_sandstorm.png",
		"weather_sandstorm.png^[transformFY",
		"weather_sandstorm.png^[transformR180",
		"weather_sandstorm.png^[transformFYR180"
	}
}

effects["climate_api:skybox"] = {
	sky_data = {
		type = "plain",
		clouds = true,
	},
	cloud_data = {
		density = 1,
		color = "#f7e4bfc0",
		thickness = 40,
		speed = {x=0,y=0,z=0}
	},
	priority = 60
}

local function generate_effects(params)
	local override = {}
	local light = math.max(params.light / 15, 0.2)
	local color = {r = 247 * light, g = 228 * light, b = 191 * light, a = 256}
	override["climate_api:skybox"] = {
		sky_data = {
			base_color = color
		},
		cloud_data = {
			height = params.player:get_pos().y - 20
		}
	}
	override = climate_api.utility.merge_tables(effects, override)
	if params.daylight < 15 then
		local result = {}
		result["climate_api:skybox"] = override["climate_api:skybox"]
		return result
	end
	return override
end

climate_api.register_weather(name, conditions, generate_effects)


code:
local name = "regional_weather:snow"

local conditions = {
	min_height = regional_weather.settings.min_height,
	max_height = regional_weather.settings.max_height,
	max_heat				= 35,
	min_humidity		= 50,
	max_humidity		= 65,
	indoors					= false,
	not_biome				= {
		"cold_desert",
		"cold_desert_ocean",
		"desert",
		"desert_ocean",
		"sandstone_desert",
		"sandstone_desert_ocean"
	}
}

local effects = {}

local textures = {}
for i = 1,12,1 do
	textures[i] = "weather_snowflake" .. i .. ".png"
end

effects["climate_api:particles"] = {
	boxsize = { x = 24, y = 6, z = 24 },
	v_offset = 2,
	amount = 4,
	expirationtime = 7,
	velocity = 0.85,
	acceleration = -0.06,
	texture = textures,
	glow = 6
}

climate_api.register_weather(name, conditions, effects)


code:
local name = "regional_weather:snow_heavy"

local conditions = {
	min_height = regional_weather.settings.min_height,
	max_height = regional_weather.settings.max_height,
	max_heat				= 30,
	min_humidity		= 65,
	indoors					= false,
	not_biome				= {
		"cold_desert",
		"cold_desert_ocean",
		"desert",
		"desert_ocean",
		"sandstone_desert",
		"sandstone_desert_ocean"
	}
}

local effects = {}

effects["climate_api:skybox"] = {
	cloud_data = {
		color = "#5e676eb5"
	},
	priority = 11
}

effects["climate_api:hud_overlay"] = {
	file = "weather_hud_frost.png",
	z_index = -100,
	color_correction = true
}

effects["climate_api:particles"] = {
	boxsize = { x = 14, y = 3, z = 14 },
	v_offset = 3,
	expirationtime = 7.5,
	size = 15,
	amount = 6,
	velocity = 0.75,
	texture = "weather_snow.png",
	glow = 6
}

climate_api.register_weather(name, conditions, effects)


code:
local name = "regional_weather:storm"

local conditions = {
	min_height 		= regional_weather.settings.min_height,
	max_height 		= regional_weather.settings.max_height,
	min_windspeed	= 3,
	indoors				= false,
}

local effects = {}

effects["climate_api:sound"] = {
	name = "weather_wind"
}

local function generate_effects(params)
	local avg_windspeed = 5
	local intensity = params.windspeed / avg_windspeed
	local override = {}

	override["climate_api:sound"]  = {
		gain = math.min(intensity, 1.2)
	}

	return climate_api.utility.merge_tables(effects, override)
end

climate_api.register_weather(name, conditions, generate_effects)


code:
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)

local function get_setting_bool(name, default, is_global)
	local prefix = ""
	if not is_global then prefix = "regional_weather_" end
	local value = minetest.settings:get_bool(prefix .. name)
	if type(value) == "nil" then value = default end
	return minetest.is_yes(value)
end

local function get_setting_number(name, default, is_global)
	local prefix = ""
	if not is_global then prefix = "regional_weather_" end
	local value = minetest.settings:get(prefix .. name)
	if type(value) == "nil" then value = default end
	return tonumber(value)
end

regional_weather = {}
regional_weather.settings = {}
regional_weather.settings.player_speed	= get_setting_bool("player_speed", true)
regional_weather.settings.snow					= get_setting_bool("snow_layers", true)
regional_weather.settings.snow_griefing	= get_setting_bool("snow_griefing", true)
regional_weather.settings.puddles				= get_setting_bool("puddles", true)
regional_weather.settings.puddles_water	= get_setting_bool("puddles_water", false)
regional_weather.settings.soil					= get_setting_bool("soil", true)
regional_weather.settings.fire					= get_setting_bool("fire", true)
regional_weather.settings.ice						= get_setting_bool("ice", true)
regional_weather.settings.pedology			= get_setting_bool("pedology", true)
regional_weather.settings.lightning			= get_setting_number("lightning", 1)
regional_weather.settings.max_height		= get_setting_number("max_height", 120)
regional_weather.settings.min_height		= get_setting_number("min_height", -50)
regional_weather.settings.cloud_height	= get_setting_number("cloud_height", 120)
regional_weather.settings.cloud_scale		= get_setting_number("cloud_scale", 40)

local S = minetest.get_translator("regional_weather")
regional_weather.i18n = S

-- warn about clouds being overriden by MTG weather
if climate_mod.settings.skybox
and minetest.get_modpath("weather")
and get_setting_bool("enable_weather", true, true) then
	minetest.log("warning", "[Regional Weather] " .. S("Disable MTG weather for the best experience. Check the forum for more information."))
end

-- import individual weather types
dofile(modpath.."/ca_weathers/ambient.lua")
dofile(modpath.."/ca_weathers/deep_cave.lua")
dofile(modpath.."/ca_weathers/fog.lua")
dofile(modpath.."/ca_weathers/fog_heavy.lua")
dofile(modpath.."/ca_weathers/hail.lua")
dofile(modpath.."/ca_weathers/pollen.lua")
dofile(modpath.."/ca_weathers/rain.lua")
dofile(modpath.."/ca_weathers/rain_heavy.lua")
dofile(modpath.."/ca_weathers/sandstorm.lua")
dofile(modpath.."/ca_weathers/snow.lua")
dofile(modpath.."/ca_weathers/snow_heavy.lua")
dofile(modpath.."/ca_weathers/storm.lua")

-- register environment effects
dofile(modpath.."/ca_effects/lightning.lua")
dofile(modpath.."/ca_effects/speed_buff.lua")

-- register ABM cycles and custom nodes
dofile(modpath .. "/abms/puddle.lua")
dofile(modpath .. "/abms/snow_cover.lua")
dofile(modpath .. "/abms/fire.lua")
dofile(modpath .. "/abms/ice.lua")
dofile(modpath .. "/abms/pedology.lua")
dofile(modpath .. "/abms/soil.lua")


code:
# textdomain:regional_weather
Disable MTG weather for the best experience. Check the forum for more information.=Deaktiviere MTG weather für die beste Spielerfahrung. Mehr Informationen im Forum.
Thin Ice=Dünnes Eis
Puddle=Pfütze
Snow Cover=Schnee


code:
# textdomain:regional_weather
Disable MTG weather for the best experience=
Thin Ice=
Puddle=
Snow Cover=


code:
name = regional_weather
title = Regional Weather
author = TestificateMods
depends = climate_api
optional_depends = default, lightning, farming, fire, pedology
description = """
Not every biome is the same and neither should their weather be.
Regional Weather controls it's effects with the local climate in mind.

Experience the humid air of the rain forest and harsh desert sandstorms.
"""


code:
[Features]

# If set to true, wind will boost or penalize player movements based on direction.
regional_weather_player_speed (Change movement speed based on wind) bool true

# If set to true, snow layers will stack up during snowy weather.
regional_weather_snow_layers (Place snow layers) bool true

# If set to true, snow layers will destroy crops
regional_weather_snow_griefing (Destructive snow layers) bool true

# If set to true, river water sources will freeze at low temperatures and melt when it gets warmer again.
# This process does not affect regular ice blocks because it adds its own temporary ones.
regional_weather_ice (Freeze river water) bool true

# If set to true, water puddles will form during rain or when snow layers have melted.
regional_weather_puddles (Place rain puddles) bool true

# If set to true, puddles will be marked as water and hydrate farmland.
# Not compatible with some ambient sound or mob mods
regional_weather_puddles_water (Hydrate farmland near puddles) bool false

# If set to true, rain will cause dry farmland to turn wet.
regional_weather_soil (Hydrate farmland during rain) bool true

# If set to true, fires will be extinguished during rain showers.
regional_weather_fire (Extinguish fire) bool true

# If set to true, rain will wetten or dry nodes from pedology mod.
regional_weather_pedology (Wetten pedology nodes) bool true

# Multiplier for lightning strike chances
# Requires lightning mod to be installed
regional_weather_lightning (Lightning chance modifier) float 1 0 20

[World Configuration]

# No visual effects will be applied below this height.
# This will prevent unwanted visuals within large underground caves.
regional_weather_min_height (Minimum height of weather effects) int -50

# No visual effects will be applied above this height.
# This value defaults to normal cloud height (120 nodes above sea level).
regional_weather_max_height (Maximum height of weather effects) int 120

# Average height of cloud bases
regional_weather_cloud_height (Cloud height) int 120

# Maxmial variation of cloud height from base value
regional_weather_cloud_scale (Cloud height variation) int 40


code:
# Auto detect text files and perform LF normalization
* text=auto

# Custom for Visual Studio
*.cs     diff=csharp

# Standard to msysgit
*.doc	 diff=astextplain
*.DOC	 diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot  diff=astextplain
*.DOT  diff=astextplain
*.pdf  diff=astextplain
*.PDF	 diff=astextplain
*.rtf	 diff=astextplain
*.RTF	 diff=astextplain


code:
# Windows image file caches
Thumbs.db
ehthumbs.db

# Folder config file
Desktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msm
*.msp

# Windows shortcuts
*.lnk

# =========================
# Operating System Files
# =========================

# OSX
# =========================

.DS_Store
.AppleDouble
.LSOverride

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk


code:
This mod implements a simple approach to making the various liquid node types (water, lava) behave in a more realistic manner. In a nutshell, it sets an Active Block Modifier running for liquid nodes that implements the following behavior:

* If there is a "flowing" or air node below a "source" liquid block, swap the liquid block with the node below it (ie, the liquid drops)
* Else swap with a random flowing or air node beside it.

This causes "source" blocks for liquids to randomly shuffle around when they don't completely fill a horizontal layer, and causes them to drain rapidly down holes or flow rapidly down hillsides. Normal "flowing" behaviour is unchanged so even though this mod is just moving cubes around the surface of the water looks reasonably smooth. The ABM only runs for blocks that are adjacent to flowing nodes and does very few calculations so it's also reasonably lightweight - I'm essentially borrowing the engine's built-in liquid flow detection to determine when my own code needs to run.

Each type of liquid can have this behaviour enabled independently of each other. By default lava is configured to flow more slowly than water, but this can be changed in the mod's settings.

## Damp Clay Counters Ocean Loss

If basic water is set as dynamic, it loses its "renewability" - water nodes don't replicate like they normally do. If you are concerned about the global water level dropping over time as water pours down into undersea caverns, or just generally don't like the idea of water being a finite resource (even with an ocean's worth to start with), this mod includes an optional feature that turns natural clay deposits into "springs". Clay deposits get an Active Block Modifier that checks if there's air or shallow water above them and refills them with newly spawned water source blocks.

If this clay is dug out by players and reconstituted into clay blocks elsewhere, the "spring" behaviour won't occur for these - it only happens for clay spawned by mapgen while this feature is enabled. (In technical terms, when clay springs are enabled the map generator replaces generated clay with an otherwise identical "damp clay" node type that drops regular clay when dug. The ABM only affects this "damp clay" node type.)

There's also a "spring" block that's available only via the creative menu or /give command. This block has an ABM that ensures there's always a single block of water generated directly above it, regardless of where the block is placed.

Note that other than this clay spring feature and the override of water's "renewability" this mod doesn't affect any default node definitions, so any other mods dealing with liquids should still function as they did before. If you disable any of the liquid types they will just "freeze" in whatever position they were in at the time.

## "Flow-through" Blocks

There are many blocks in the game that one would think should not serve as an impenetrable barrier to water flow, but which do. For example: metal bars, grass, leaves. The "flow-through" feature of this mod makes them permeable, allowing water_source and lava_source blocks to "teleport" from one side of a flow-through block to the other. Liquid source blocks will only teleport through a single layer of flow-through node and only in the cardinal directions.

Other mods can mark blocks as flow-through by adding them to the "flow_through" group.

## Lava/Water Interaction

Due to the way default lava/water interaction works a single block of lava_source would be able to wander endlessly around on the surface of the ocean turning the uppermost layer of water into stone. Similarly, a single block of water_source could wander around on the surface of a lava pool turning the uppermost layer into obsidian. This is an undesirable outcome so the dynamic_liquid mod includes a more sophisticated replacement to the default lava-cooling ABM.

lava_flowing nodes are now eliminated entirely by nodes marked cools_lava, they do not leave behind default:stone. This means that infinite stone generation is no longer possible using arrangements of lava and water.

lava_source nodes are turned into obsidian only by water_source nodes; water_flowing nodes are eliminated by lava_source nodes without affecting them. The water_source node is destroyed in the process. This means that if you want to cool a large amount of lava you're going to need a correspondingly large amount of water.

For modders, you can now fine-tune the effects of blocks on lava (and vice versa) using the following groups:

* "group:dynamic_cools_lava_flowing" - causes lava_flowing nodes to vanish. The old "group:cools_lava" has this effect.
* "group:dynamic_lava_flowing_destroys" - causes lava_flowing nodes to destroy nodes belonging to this group.
* "group:dynamic_cools_lava_source" - causes lava_source nodes to turn into obsidian. The old "group:cools_lava" does *not* have this effect.
* "group:dynamic_lava_source_destroys" - causes lava_source nodes to destroy nodes belonging to this group.


code:
if not minetest.get_modpath("default") then return end

local new_lava_cooling = minetest.settings:get_bool("dynamic_liquid_new_lava_cooling", true)
if not new_lava_cooling then return end

local falling_obsidian = minetest.settings:get_bool("dynamic_liquid_falling_obsidian", false)

-- The existing cool_lava ABM is hard-coded to respond to water nodes
-- and overriding node groups doesn't appear to work:
-- https://github.com/minetest/minetest/issues/5518
-- So modifying the lava-cooling system requires some unfortunate
-- workarounds. Most notable is the "dynamic_cools_lava" group;
-- the "cools_lava" group can't be removed from flowing water for ABM
-- purposes so this redundancy is required.

-- Mods can hook into this system by adding the group
-- "dynamic_cools_lava_flowing" and/or "dynamic_cools_lava_source"
-- to nodes that should cool lava and
-- "dynamic_lava_flowing_destroys" and/or "dynamic_lava_source_destroys"
-- to nodes that should be destroyed by proximity to lava.

local particles = minetest.settings:get_bool("enable_particles", true)

local steam = function(pos)
	if particles then
	minetest.add_particlespawner({
		amount = 6,
		time = 1,
		minpos = pos,
		maxpos = pos,
		minvel = {x=-2, y=0, z=-2},
		maxvel = {x=2, y=1, z=2},
		minacc = {x=0, y=2, z=0},
		maxacc = {x=0, y=2, z=0},
		minexptime = 1,
		maxexptime = 4,
		minsize = 16,
		maxsize = 16,
		collisiondetection = false,
		vertical = false,
		texture = "smoke_puff.png",
	})
	end
end

default.cool_lava = function(pos, node)
	-- no-op disables default cooling ABM
end

-------------------------------------------------------------------------------------------------

local dynamic_cools_lava_flowing = {"group:dynamic_cools_lava_flowing", "group:cools_lava"}

-- Flowing lava will turn these blocks into steam.
local dynamic_lava_flowing_destroys = {
	"group:dynamic_lava_flowing_destroys",
	"default:water_flowing",
	"default:river_water_flowing",
	"default:snow",
	"default:snowblock"
}

local all_flowing_nodes = {unpack(dynamic_cools_lava_flowing)}
for i = 1, #dynamic_lava_flowing_destroys do
    all_flowing_nodes[#dynamic_cools_lava_flowing + i] = dynamic_lava_flowing_destroys[i]
end

local cool_lava_flowing = function(pos, node)
	local cooler_adjacent = minetest.find_node_near(pos, 1, dynamic_cools_lava_flowing)

	if cooler_adjacent ~= nil then
		-- pulling nearby sources into position is necessary to break certain classes of
		-- flow "deadlock". Weird, but what're you gonna do.
		local nearby_source = minetest.find_node_near(pos, 1, "default:lava_source")
		if nearby_source then
			minetest.set_node(pos, {name="default:lava_source"})
			minetest.set_node(nearby_source, {name="air"})
			steam(nearby_source)
		else
			minetest.set_node(pos, {name = "air"})
			steam(pos)
		end
	end

	local evaporate_list = minetest.find_nodes_in_area(
		vector.add(pos,{x=-1, y=-1, z=-1}),
		vector.add(pos,{x=1, y=1, z=1}),
		dynamic_lava_flowing_destroys
	)
	for _, loc in pairs(evaporate_list) do
		minetest.set_node(loc, {name="air"})
		steam(loc)
	end

	minetest.sound_play("default_cool_lava",
		{pos = pos, max_hear_distance = 16, gain = 0.25})
end

minetest.register_abm({
	label = "Lava flowing cooling",
	nodenames = {"default:lava_flowing"},
	neighbors = all_flowing_nodes,
	interval = 1,
	chance = 1,
	catch_up = false,
	action = function(...)
		cool_lava_flowing(...)
	end,
})

-------------------------------------------------------------------------------------------------

local dynamic_cools_lava_source = {"group:dynamic_cools_lava_source"}
for name, node_def in pairs(minetest.registered_nodes) do
	-- We don't want "flowing" nodes to cool lava source blocks, otherwise when water falls onto a large pool of lava there's
	-- way too many blocks turned to obsidian.
	if minetest.get_item_group(name, "cools_lava") > 0 and name ~= "default:water_flowing" and name ~= "default:river_water_flowing" then
		table.insert(dynamic_cools_lava_source, name)
	end
end

-- lava source blocks will turn these blocks into steam.
local dynamic_lava_source_destroys = {
	"group:dynamic_lava_source_destroys",
	"default:water_source",
	"default:river_water_source",
	"default:water_flowing",
	"default:river_water_flowing",
	"default:ice",
	"default:snow",
	"default:snowblock"
}

local all_source_nodes = {unpack(dynamic_cools_lava_source)}
for i = 1, #dynamic_lava_source_destroys do
    all_source_nodes[#dynamic_cools_lava_source + i] = dynamic_lava_source_destroys[i]
end

local is_pos_in_list = function(pos, list)
	for _, loc in pairs(list) do
		if vector.equals(pos, loc) then return true end
	end
	return false
end

local function shuffle_array(a)
    local rand = math.random
    local iterations = #a
    local j

    for i = iterations, 2, -1 do
        j = rand(i)
        a[i], a[j] = a[j], a[i]
    end
end

local cool_lava_source = function(pos, node)
	local cooler_list = minetest.find_nodes_in_area(
		vector.add(pos,{x=-1, y=-1, z=-1}),
		vector.add(pos,{x=1, y=1, z=1}),
		dynamic_cools_lava_source)
	local evaporate_list = minetest.find_nodes_in_area(
		vector.add(pos,{x=-1, y=-1, z=-1}),
		vector.add(pos,{x=1, y=1, z=1}),
		dynamic_lava_source_destroys
	)

	shuffle_array(cooler_list)

	local obsidian_location = nil
	for _, loc in pairs(cooler_list) do
		if is_pos_in_list(loc, evaporate_list) then
			if loc.y < pos.y then
				if loc.z == pos.z and loc.x == pos.x then
					obsidian_location = loc -- best outcome, directly below us. End loop immediately.
					break
				else
					obsidian_location = loc -- next-best outcome, in the tier below us.
				end
			elseif loc.y == pos.y and obsidian_location == nil then
				obsidian_location = loc -- On the same level as us, acceptable if nothing else comes along.
			end
		end
	end
	if obsidian_location == nil and #cooler_list > 0 then
		obsidian_location = pos -- there's an adjacent cooler node, but it's above us. Turn into obsidian in place.
	end

	for _, loc in pairs(evaporate_list) do
		minetest.set_node(loc, {name="air"})
		steam(loc)
	end

	if obsidian_location ~= nil then
		minetest.set_node(pos, {name = "air"})
		minetest.set_node(obsidian_location, {name = "default:obsidian"})
		if minetest.spawn_falling_node and falling_obsidian then -- TODO cutting-edge dev function, so check if it exists for the time being. Remove check when 0.4.16 is released.
			minetest.spawn_falling_node(obsidian_location)
		end
	elseif #evaporate_list > 0 then
		-- Again, this weird bit is necessary for breaking certain types of flow deadlock
		local loc = evaporate_list[math.random(1,#evaporate_list)]
		if loc.y <= pos.y then
			minetest.set_node(pos, {name = "air"})
			minetest.set_node(loc, {name = "default:lava_source"})
		end
	end

	minetest.sound_play("default_cool_lava",
		{pos = pos, max_hear_distance = 16, gain = 0.25})
end

minetest.register_abm({
	label = "Lava source cooling",
	nodenames = {"default:lava_source"},
	neighbors = all_source_nodes,
	interval = 1,
	chance = 1,
	catch_up = false,
	action = function(...)
		cool_lava_source(...)
	end,
})


code:
-- Fallback functions for when `intllib` is not installed.
-- Code released under Unlicense <http://unlicense.org>.

-- Get the latest version of this file at:
--   https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua

local function format(str, ...)
	local args = { ... }
	local function repl(escape, open, num, close)
		if escape == "" then
			local replacement = tostring(args[tonumber(num)])
			if open == "" then
				replacement = replacement..close
			end
			return replacement
		else
			return "@"..open..num..close
		end
	end
	return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
end

local gettext, ngettext
if minetest.get_modpath("intllib") then
	if intllib.make_gettext_pair then
		-- New method using gettext.
		gettext, ngettext = intllib.make_gettext_pair()
	else
		-- Old method using text files.
		gettext = intllib.Getter()
	end
end

-- Fill in missing functions.

gettext = gettext or function(msgid, ...)
	return format(msgid, ...)
end

ngettext = ngettext or function(msgid, msgid_plural, n, ...)
	return format(n==1 and msgid or msgid_plural, ...)
end

return gettext, ngettext


code:
# Dynamic liquid Minetest mod.
# Copyright (C) 2017
# This file is distributed under the same license as the dynamic_liquid package.
# FaceDeer
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-12 12:55-0700\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: init.lua:141
msgid "Damp Clay"
msgstr "Feuchten Lehm"

#: init.lua:197
msgid "Spring"
msgstr "Quelle"

#: init.lua:198
msgid ""
"A natural spring that generates an endless stream of water source blocks"
msgstr ""

#: init.lua:199
msgid ""
"Generates one source block of water directly on top of itself once per "
"second, provided the space is clear. If this natural spring is dug out the "
"flow stops and it is turned into ordinary cobble."
msgstr ""


code:
# Spanish translations for PACKAGE package
# Traducciones al español para el paquete PACKAGE.
# Copyright (C) 2017 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Diego Martínez <kaeza@kaeza-desktop>, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-12 12:55-0700\n"
"PO-Revision-Date: 2017-02-24 00:13-0300\n"
"Last-Translator: Diego Martínez <kaeza@kaeza-desktop>\n"
"Language-Team: Spanish\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#: init.lua:141
msgid "Damp Clay"
msgstr "Arcilla húmeda"

#: init.lua:197
msgid "Spring"
msgstr "Manantial"

#: init.lua:198
msgid ""
"A natural spring that generates an endless stream of water source blocks"
msgstr ""
"Un manantial natural que genera un flujo sin fín de bloques fuente de agua"

#: init.lua:199
msgid ""
"Generates one source block of water directly on top of itself once per "
"second, provided the space is clear. If this natural spring is dug out the "
"flow stops and it is turned into ordinary cobble."
msgstr ""
"Genera un bloque fuente de agua directamente encima de sí mismo una vez por "
"segundo, en tanto el espacio esté libre. Si éste manantial natural es "
"excavado el flujo se detiene y se convierte en adoquines comúnes."


code:
# Dynamic liquid Minetest mod.
# Copyright (C) 2017
# This file is distributed under the same license as the dynamic_liquid package.
# FaceDeer
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-12 12:55-0700\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: init.lua:141
msgid "Damp Clay"
msgstr "Argile humide"

#: init.lua:197
msgid "Spring"
msgstr "Source"

#: init.lua:198
msgid ""
"A natural spring that generates an endless stream of water source blocks"
msgstr ""

#: init.lua:199
msgid ""
"Generates one source block of water directly on top of itself once per "
"second, provided the space is clear. If this natural spring is dug out the "
"flow stops and it is turned into ordinary cobble."
msgstr ""


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-12 12:55-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: init.lua:141
msgid "Damp Clay"
msgstr ""

#: init.lua:197
msgid "Spring"
msgstr ""

#: init.lua:198
msgid ""
"A natural spring that generates an endless stream of water source blocks"
msgstr ""

#: init.lua:199
msgid ""
"Generates one source block of water directly on top of itself once per "
"second, provided the space is clear. If this natural spring is dug out the "
"flow stops and it is turned into ordinary cobble."
msgstr ""


code:
name = dynamic_liquid
description = Flowing dynamic liquids and ocean-maintenance springs.
depends =
optional_depends = default, intllib, doc, xpanes, carts, signs
author = FaceDeer
title = Dynamic Liquid


code:
#Makes water limited (it doesn't spawn new source blocks any more) and causes
#water blocks to move dynamically
dynamic_liquid_water (Dynamic water) bool true

#Causes river water blocks to move dynamically. Use caution when using this
#with the "valleys" mapgen, the streams generated by that mapgen have elevation
#changes and can make a mess if their water moves downhill uncontrolled.
dynamic_liquid_river_water (Dynamic river water) bool false

#Causes lava blocks to move dynamically
dynamic_liquid_lava (Dynamic lava) bool true

#Floatlands are "leaky", so if you wish to prevent their lakes from draining
#set this value to somewhere below the elevation the floatlands are generated
#at.
dynamic_liquid_disable_flow_above (Disable flow above this altitude) int 31000

#Causes natural clay deposits to act as water sources, seeping new water blocks
#into the space above them.
#Also adds a "spring" block to the creative inventory to serve as an infinite water
#source in creative mode.
dynamic_liquid_springs (Springs) bool true

#Allows dynamic liquids to "teleport" through nodes that should allow liquids to
#pass through. This includes the groups:
#leaves, sapling, grass, dry_grass, flora, rail, flower
#As well as select other nodes from the default game's definitions. Other mods
#can set nodes as flow-through by adding them to these groups or to the group
#"flow_through"
dynamic_liquid_flow_through (Flow-through) bool true

#During map generation, this mod can attempt to "pre-fill" underwater cave
#entrances by replacing air directly below water with additional water blocks.
#This process is only attempted below "sea level" and is not perfect - it will
#only fill down to the bottom of the map block being generated and it won't
#fill out openings to the sides. But it should hopefully reduce the magnitude
#of the whirlpools that form over cave entrances and the load on the active
#block modifier in these situations.
dynamic_liquid_mapgen_prefill (Mapgen water prefill) bool true

#Dynamic liquid movement causes trouble with the default lava cooling system.
#When set to true, this replaces the default lava cooling system with one
#that will not result in oceans being paved over by a single wandering
#lava block or lava seas being paved by a single wandering water block.
dynamic_liquid_new_lava_cooling (Revised lava cooling) bool true

#When this is enabled, obsidian blocks spawned by lava cooling will fall from
#their origin point like sand or gravel does. Otherwise, obsidian will still
#behave the same - this occurs only when it's first created.
dynamic_liquid_falling_obsidian (Falling obsidian) bool false

#When true, placing a block where a registered liquid source currently is
#will make a simple attempt to displace the liquid source somewhere else
#rather than destroying it.
dynamic_liquid_displace_liquid (Displace liquid) bool true

[Flow Rates]

#Sets the probability of water flow per block per second.
#That is, if this is set to 5 then there's a 1/5 chance per second that a water block
#will check if it should move. Increase this value to make water flow slower.
#This value only has an effect if dynamic water is set to true
dynamic_liquid_water_flow_propability (Water flow probability) int 1 1 100

#Sets the probability of river water flow per block per second.
#That is, if this is set to 5 then there's a 1/5 chance per second that a river water
#block will check if it should move. Increase this value to make river water flow
#slower.
#This value only has an effect if dynamic river water is set to true
dynamic_liquid_river_water_flow_propability (River water flow probability) int 1 1 100

#Sets the probability of lava flow per block per second.
#That is, if this is set to 5 then there's a 1/5 chance per second that a lava block
#will check if it should move. Increase this value to make lava flow slower.
#This value only has an effect if dynamic lava is set to true
dynamic_liquid_lava_flow_propability (Lava flow probability) int 5 1 100


code:
# fire_plus

## API

* `fire_plus.burnplayer(player)`
	Sets `player` on fire
	* player - player object

* `fire_plus.get_burn_dmg(player)`
	Returns how much damage `player` should take from fire
	default is 1
	* `player` - player object

* `fire_plus.tnt_explode_radius`
	How close to a TNT node a player has to be to detonate it

* `fire_plus.ignition_nodes`
	Nodes that set player on fire. Example:
	* `table.insert(fire_plus.ignition_nodes, "fire")`
		All nodes with `fire` in their itemstring will set a player on fire

* `fire_plus.put_outs`
	Nodes that put out burning players. Example:
	* `table.insert(fire_plus.ignition_nodes, "water")`
		All nodes with `water` in their itemstring will put out a burning player

* `Fire tools`
	Allows modders to make tools set any players they hit on fire.
	Just set `tool_capabilities.damage_groups.burn = 1`


code:
local armor_mod = minetest.get_modpath("3d_armor")

-- Replace min/max table with random value from min to max
local function get_val(val)
	if type(val) == "table" then
		val = math.random(val.min or 1, val.max or 5)
	end

	return val
end

function fire_plus.burn_player(player, burns, damage, not_initial)
	if not player then
		minetest.log("warning", "[Fire Plus] (burn_player): player is nil")
	end

	local name = player
	if type(player) == "string" then
		player = minetest.get_player_by_name(player)
	end

	if not player or player:get_hp() <= 0 then
		fire_plus.extinguish_player(name)
		return
	end

	name = player:get_player_name()

	-- 3d_armor fire protection puts out flames
	if armor_mod and armor.def[name].fire > 1 then
		fire_plus.extinguish_player(name)
	end

	-- Fire was extinguished
	if not fire_plus.burning[name] and not_initial then
		return
	end

	if not fire_plus.burning[name] then
		burns = get_val(burns)

		fire_plus.burning[name] = {
			burns_left = burns,
			hud_id = player:hud_add({
				hud_elem_type = "image",
				position  = {x = 0.5, y = 0.95},
				offset    = {x = 0, y = 0},
				text      = "fire_basic_flame.png",
				alignment = -1,
				scale     = {x = 100, y = 32},
				number    = 0xFFFFFF,
			}),
			sound_id = minetest.sound_play("fire_fire", {
				to_player = name,
				gain = 1.0,
				loop = true,
			}),
			particlespawner_id = minetest.add_particlespawner({
				amount = 10,
				time = 0,
				minpos = vector.new(-0.3, 0.5, -0.3),
				maxpos = vector.new( 0.3,   2,  0.3),
				minvel = {x = -1, y = 0, z = -1},
				maxvel = {x =  1, y = 1, z =  1},
				minacc = {x =  0, y = 2, z =  0},
				maxacc = {x =  0, y = 3, z =  0},
				minexptime = 0.5,
				maxexptime = 1,
				minsize = 3,
				maxsize = 3,
				texture = "fire_basic_flame.png",
				collisiondetection = true,
				glow = minetest.LIGHT_MAX,
				attached = player,
			})
		}
	else
		player:set_hp(player:get_hp() - get_val(damage), {type = "set_hp", fire_plus = true})

		if minetest.get_modpath("tnt") then
			local tntpos = minetest.find_node_near(player:get_pos(), fire_plus.tnt_explode_radius, {"tnt:tnt"}, true)

			if tntpos then
				tnt.boom(tntpos, {radius = fire_plus.tnt_explode_radius, damage_radius = fire_plus.tnt_explode_radius})
			end
		end

		if not_initial then
			fire_plus.burning[name].burns_left = fire_plus.burning[name].burns_left - 1
		else
			fire_plus.burning[name].burns_left = burns
		end
	end

	if fire_plus.burning[name].burns_left > 0 then
		minetest.after(fire_plus.burn_interval, function()
			fire_plus.burn_player(name, burns, damage, true)
		end)
	else
		fire_plus.extinguish_player(name)
	end
end

function fire_plus.extinguish_player(player)
	local name = player

	if player then
		if type(name) ~= "string" then
			name = player:get_player_name()
		else
			player = minetest.get_player_by_name(name)
		end
	end

	if not fire_plus.burning[name] then return end

	if player then
		player:hud_remove(fire_plus.burning[name].hud_id)
		minetest.sound_fade(fire_plus.burning[name].sound_id, 1, 0)
	end

	minetest.delete_particlespawner(fire_plus.burning[name].particlespawner_id)

	fire_plus.burning[name] = nil
end


code:
fire_plus = {
	burn_interval = 1,
	tnt_explode_radius = 1.5,
	burning = {--[[
		["playername"] = {
			burns_left = <number>,
			hud_id     = <hud id>,
			sound_id   = <sound id>,
			particlespawner_id = <particlespawner id>
		}
	]]},
	ignition_nodes = {
		--["nodename"] = {burns = <table with min/max or number>, damage = <table with min/max or number>}
		["default:lava"] = {burns = 2, damage = 4},
		["fire:"]        = {burns = 4, damage = 2},
	},
	extinguishers = {
		"default:water",
		"default:river_water",
		"default:snow"
	}
}

dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/api.lua")

local time = 0
minetest.register_globalstep(function(dtime)
	time = time + dtime

	if time < 0.1 then
		return
	else
		time = 0
	end

	-- Put out players in extinguisher nodes
	for _, player in pairs(minetest.get_connected_players()) do

		local name = player:get_player_name()

		if fire_plus.burning[name] then

			local nodename = minetest.get_node(player:get_pos()).name
			local nodename_head = minetest.get_node(vector.add(player:get_pos(),
					vector.new(0, 1, 0))).name

			for _, extinguisher in pairs(fire_plus.extinguishers) do

				if nodename:find(extinguisher) or nodename_head:find(extinguisher) then

					minetest.sound_play("fire_extinguish_flame", {
						to_player = name,
						gain = 1.0,
					})

					fire_plus.extinguish_player(name)

					return
				end
			end
		end
	end
end)

-- Ignite players in ignition nodes
minetest.register_on_player_hpchange(function(player, _, reason)
	if reason.type == "node_damage" and reason.node then
		for igniter, def in pairs(fire_plus.ignition_nodes) do
			if reason.node:find(igniter) then
				fire_plus.burn_player(player, def.burns or 4, def.damage or 2)
				break
			end
		end
	end
end)

minetest.register_on_punchplayer(function(player, hitter, _, toolcaps, _, dmg)
	if hitter and hitter:is_player() and toolcaps.damage_groups.burns and player and player:get_hp() - dmg > 0 then
		fire_plus.burn_player(player, toolcaps.damage_groups.burn_time or 4, toolcaps.damage_groups.burns)
	end

	return false
end)

minetest.register_on_respawnplayer(function(player)
	local name = player:get_player_name()

	if fire_plus.burning[name] then
		fire_plus.extinguish_player(name)
	end
end)


code:
name = fire_plus
description = Some cool fire interactions.
depends = fire
optional_depends = default, tnt, 3d_armor
author = LoneWolfHT
title = Fire Plus


code:
default?
flowers?
farming?


code:
floodables = {
  { group = "torch", drop = "default:torch", sound = "floodables_torch", gain = 0.8 },
  { group = "flora", sound = "floodables_grass", gain = 0.2 },
  { group = "wheat", drop = "farming:seed_wheat", sound = "floodables_grass", gain = 0.2 },
  { name = "default:junglegrass", sound = "floodables_grass", gain = 0.2 },
  { group = "grass", drop = "default:grass_1", sound = "floodables_grass", gain = 0.2 },
  { group = "dry_grass", drop = "default:dry_grass_1", sound = "floodables_grass", gain = 0.2 },
--  { name = "default:dirt_with_grass", erode = true },
}

for _,c in ipairs(floodables) do
  if c.name ~=nil then
    minetest.override_item(c.name, {
      floodable = true,
      on_flood = function(pos, oldnode, newnode)
        minetest.remove_node(pos)
        if c.sound ~= nil then
          minetest.sound_play({ name = c.sound, gain = c.gain }, { pos = pos, max_hear_distance = 16 })
        end
        if c.drop == nil or c.drop ~= false then c.drop = c.name end
        minetest.add_item(pos, {name = c.drop})
        if c.erode == true then
          minetest.remove_node( { x = pos.x, y = pos.y-1, z = pos.z } )
        end
      end
    })
  elseif c.group ~=nil then
    for _,v in pairs(minetest.registered_nodes) do
    	if minetest.get_item_group(v.name, c.group) ~= 0 then
        minetest.override_item(v.name, {
          floodable = true,
          on_flood = function(pos, oldnode, newnode)
            minetest.remove_node(pos)
            if c.sound ~= nil then
              minetest.sound_play({ name = c.sound, gain = c.gain }, { pos = pos, max_hear_distance = 16 })
            end
            if c.drop == nil or c.drop == true then c.drop = v.name end
            minetest.add_item(pos, {name = c.drop})
            if c.erode == true then
              minetest.remove_node( { x = pos.x, y = pos.y-1, z = pos.z } )
            end
          end
        })
    	end
    end
  end
end


code:
name = floodables
description = Liquid uprooting, liquid erosion.
depends =
optional_depends = default, farming, flowers
author = tacotexmex
title = Floodables


code:
- The `master` branch should be stable to end-users at all times. It should target latest Minetest point release.
- Put rewrites and new features in branches.
- Conform with setting defaults so that end-user upgrades doesn't change expected in-game behavior. Discuss default changes in an issue if one really need to change.


code:
# item_drop
By [PilzAdam](https://github.com/PilzAdam),
[texmex](https://github.com/tacotexmex/), [hybriddog](https://github.com/hybriddog/).

## Description
A highly configurable mod providing item magnet and in-world node drops

## Licensing
LGPLv2.1/CC BY-SA 3.0. Particle code from WCILA mod by Aurailus, originally licensed MIT.

## Notes
item_drop can be played with Minetest 0.4.16 or above. It was originally
developed by [PilzAdam](https://github.com/PilzAdam/item_drop).

## List of features
* All settings may be configured from within the game itself.
  (Settings tab > Advanced settings > Mods > item_drop)
* Drops nodes as in-world items on dig if `item_drop.enable_item_drop` is
  `true` (true by default) It does nothing in creative mode.
* Puts dropped items to the player's inventory if `item_drop.enable_item_pickup`
  is `true` (true by default)
  * Multiple items are picked in a quick succession instead of all at once which
    is indicated by the pickup sound.
  * It uses a node radius set in `item_drop.pickup_radius` (default 0.75),
    if items are within this radius around the player's belt, they're picked.
  * If `item_drop.pickup_age` is something positive, items dropped by players
    are ignored for this time to avoid instantly picking up when dropping.
  * If `item_drop.pickup_age` is `-1`, items are only picked when they don't
    move, it's another fix for instant item picking.
  * If `item_drop.magnet_radius` is bigger than `item_drop.pickup_radius`,
    items between these radii are flying to the player for
    `item_drop.magnet_time` seconds, after this time, they're picked or stop
    flying.
  * Enable manual item pickups by mouse only if `item_drop.mouse_pickup` is
    `true` (true by default)
* Plays a sound when the items are picked up with the gain level set to
  `item_drop.pickup_sound_gain` (default 0.2)
* Requires a key to be pressed in order to pick items if
  `item_drop.enable_pickup_key` is `true` (true by default)
  * The keytypes to choose from by setting `item_pickup_keytype` are:
    * Use key (`Use`)
    * Sneak key (`Sneak`)
    * Left and Right keys combined (`LeftAndRight`)
    * Right mouse button (`RMB`)
    * Sneak key and right mouse button combined (`SneakAndRMB`)
  * If `item_drop.pickup_keyinvert` is `true`, items are
    collected when the key is not pressed instead of when it's pressed.
* Displays a particle of the picked item above the player if
  `item_drop.pickup_particle` is `true` (true by default)

## Known issues

## Bug reports and suggestions
You can report bugs or suggest ideas by
[filing an issue](http://github.com/minetest-mods/item_drop/issues/new).

## Links
* [Download ZIP](https://github.com/minetest-mods/item_drop/archive/master.zip)
* [Source](https://github.com/minetest-mods/item_drop/)
* [Forum thread](https://forum.minetest.net/viewtopic.php?t=16913)


code: A highly configurable mod providing item magnet and in-world node drops


code:
local load_time_start = minetest.get_us_time()

-- Functions which can be overridden by mods
item_drop = {
	-- This function is executed before picking up an item or making it fly to
	-- the player. If it does not return true, the item is ignored.
	-- It is also executed before collecting the item after it flew to
	-- the player and did not reach him/her for magnet_time seconds.
	can_pickup = function(entity, player)
		if entity.item_drop_picked then
			-- Ignore items where picking has already failed
			return false
		end
		return true
	end,

	-- before_collect and after_collect are executed before and after an item
	-- is collected by a player
	before_collect = function(entity, pos, player)
	end,
	after_collect = function(entity, pos, player)
		entity.item_drop_picked = true
	end,
}

local function legacy_setting_getbool(name_new, name_old, default)
	local v = minetest.settings:get_bool(name_new)
	if v == nil then
		v = minetest.settings:get_bool(name_new)
	end
	if default then
		return v ~= false
	end
	return v
end

local function legacy_setting_getnumber(name_new, name_old, default)
	return tonumber(minetest.settings:get(name_new))
		or tonumber(minetest.settings:get(name_old))
		or default
end

if legacy_setting_getbool("item_drop.enable_item_pickup",
		"enable_item_pickup", true) then
	local pickup_gain = legacy_setting_getnumber("item_drop.pickup_sound_gain",
		"item_pickup_gain", 0.2)
	local pickup_particle =
		minetest.settings:get_bool("item_drop.pickup_particle", true)
	local pickup_radius = legacy_setting_getnumber("item_drop.pickup_radius",
		"item_pickup_radius", 0.75)
	local magnet_radius = tonumber(
		minetest.settings:get("item_drop.magnet_radius")) or -1
	local magnet_time = tonumber(
		minetest.settings:get("item_drop.magnet_time")) or 5.0
	local pickup_age = tonumber(
		minetest.settings:get("item_drop.pickup_age")) or 0.5
	local key_triggered = legacy_setting_getbool("item_drop.enable_pickup_key",
		"enable_item_pickup_key", true)
	local key_invert = minetest.settings:get_bool(
		"item_drop.pickup_keyinvert") ~= false
	local keytype
	if key_triggered then
		keytype = minetest.settings:get("item_drop.pickup_keytype") or
		minetest.settings:get("item_pickup_keytype") or "Use"
		-- disable pickup age if picking is explicitly enabled by the player
		if not key_invert then
			pickup_age = math.min(pickup_age, 0)
		end
	end
	local mouse_pickup = minetest.settings:get_bool(
		"item_drop.mouse_pickup") ~= false
	if not mouse_pickup then
		minetest.registered_entities["__builtin:item"].pointable = false
	end

	local magnet_mode = magnet_radius > pickup_radius
	local zero_velocity_mode = pickup_age == -1
	if magnet_mode
	and zero_velocity_mode then
		error"zero velocity mode can't be used together with magnet mode"
	end

	-- tells whether an inventorycube should be shown as pickup_particle or not
	-- for known drawtypes
	local inventorycube_drawtypes = {
		normal = true,
		allfaces = true,
		allfaces_optional = true,
		glasslike = true,
		glasslike_framed = true,
		glasslike_framed_optional = true,
		liquid = true,
		flowingliquid = true,
	}

	-- adds the item to the inventory and removes the object
	local function collect_item(ent, pos, player)
		item_drop.before_collect(ent, pos, player)
		minetest.sound_play("item_drop_pickup", {
			pos = pos,
			gain = pickup_gain,
		})
		if pickup_particle then
			local item = minetest.registered_nodes[
				ent.itemstring:gsub("(.*)%s.*$", "%1")]
			local image = ""
			if item and item.tiles and item.tiles[1] then
				if inventorycube_drawtypes[item.drawtype] then
					local tiles = item.tiles

					local top = tiles[1]
					if type(top) == "table" then
						top = top.name
					end
					local left = tiles[3] or top
					if type(left) == "table" then
						left = left.name
					end
					local right = tiles[5] or left
					if type(right) == "table" then
						right = right.name
					end

					image = minetest.inventorycube(top, left, right)
				else
					image = item.inventory_image or item.tiles[1]
				end
				minetest.add_particle({
					pos = {x = pos.x, y = pos.y + 1.5, z = pos.z},
					velocity = {x = 0, y = 1, z = 0},
					acceleration = {x = 0, y = -4, z = 0},
					expirationtime = 0.2,
					size = 3,--math.random() + 0.5,
					vertical = false,
					texture = image,
				})
			end
		end
		ent:on_punch(player)
		item_drop.after_collect(ent, pos, player)
	end

	-- opt_get_ent gets the object's luaentity if it can be collected
	local opt_get_ent
	if zero_velocity_mode then
		function opt_get_ent(object)
			if object:is_player()
			or not vector.equals(object:get_velocity(), {x=0, y=0, z=0}) then
				return
			end
			local ent = object:get_luaentity()
			if not ent
			or ent.name ~= "__builtin:item"
			or ent.itemstring == "" then
				return
			end
			return ent
		end
	else
		function opt_get_ent(object)
			if object:is_player() then
				return
			end
			local ent = object:get_luaentity()
			if not ent
			or ent.name ~= "__builtin:item"
			or (ent.dropped_by and ent.age < pickup_age)
			or ent.itemstring == "" then
				return
			end
			return ent
		end
	end

	local afterflight
	if magnet_mode then
		-- take item or reset velocity after flying a second
		function afterflight(object, inv, player)
			-- TODO: test what happens if player left the game
			local ent = opt_get_ent(object)
			if not ent then
				return
			end
			local item = ItemStack(ent.itemstring)
			if inv
			and inv:room_for_item("main", item)
			and item_drop.can_pickup(ent, player) then
				collect_item(ent, object:get_pos(), player)
			else
				-- the acceleration will be reset by the object's on_step
				object:set_velocity({x=0,y=0,z=0})
				ent.is_magnet_item = false
			end
		end

		-- disable velocity and acceleration changes of items flying to players
		minetest.after(0, function()
			local ObjectRef
			local blocked_methods = {"set_acceleration", "set_velocity",
				"setacceleration", "setvelocity"}
			local itemdef = minetest.registered_entities["__builtin:item"]
			local old_on_step = itemdef.on_step
			local function do_nothing() end
			function itemdef.on_step(self, dtime)
				if not self.is_magnet_item then
					return old_on_step(self, dtime)
				end
				ObjectRef = ObjectRef or getmetatable(self.object)
				local old_funcs = {}
				for i = 1, #blocked_methods do
					local method = blocked_methods[i]
					old_funcs[method] = ObjectRef[method]
					ObjectRef[method] = do_nothing
				end
				old_on_step(self, dtime)
				for i = 1, #blocked_methods do
					local method = blocked_methods[i]
					ObjectRef[method] = old_funcs[method]
				end
			end
		end)
	end

	-- set keytype to the key name if possible
	if keytype == "Use" then
		keytype = "aux1"
	elseif keytype == "Sneak" then
		keytype = "sneak"
	elseif keytype == "LeftAndRight" then -- LeftAndRight combination
		keytype = 0
	elseif keytype == "SneakAndRMB" then -- SneakAndRMB combination
		keytype = 1
	end

	-- tests if the player has the keys pressed to enable item picking
	local function has_keys_pressed(player)
		if not key_triggered then
			return true
		end

		local control = player:get_player_control()
		local keys_pressed
		if keytype == 0 then -- LeftAndRight combination
			keys_pressed = control.left and control.right
		elseif keytype == 1 then -- SneakAndRMB combination
			keys_pressed = control.sneak and control.RMB
		else
			keys_pressed = control[keytype]
		end

		return keys_pressed ~= key_invert
	end

	-- called for each player to possibly collect an item, returns true if so
	local function pickupfunc(player)
		if not has_keys_pressed(player)
		or not minetest.get_player_privs(player:get_player_name()).interact
		or player:get_hp() <= 0 then
			return
		end

		local pos = player:get_pos()
		pos.y = pos.y+0.5
		local inv

		local objectlist = minetest.get_objects_inside_radius(pos,
			magnet_mode and magnet_radius or pickup_radius)
		for i = 1,#objectlist do
			local object = objectlist[i]
			local ent = opt_get_ent(object)
			if ent
			and item_drop.can_pickup(ent, player) then
				if not inv then
					inv = player:get_inventory()
					if not inv then
						minetest.log("error", "[item_drop] Couldn't " ..
							"get inventory")
						return
					end
				end
				local item = ItemStack(ent.itemstring)
				if inv:room_for_item("main", item) then
					local flying_item
					local pos2
					if magnet_mode then
						pos2 = object:get_pos()
						flying_item = vector.distance(pos, pos2) > pickup_radius
					end
					if not flying_item then
						-- The item is near enough to pick it
						collect_item(ent, pos, player)
						-- Collect one item at a time to avoid the loud pop
						return true
					end
					-- The item is not too far a way but near enough to be
					-- magnetised, make it fly to the player
					local vel = vector.multiply(vector.subtract(pos, pos2), 3)
					vel.y = vel.y + 0.6
					object:set_velocity(vel)
					if not ent.is_magnet_item then
						ent.object:set_acceleration({x=0, y=0, z=0})
						ent.is_magnet_item = true

						minetest.after(magnet_time, afterflight,
							object, inv, player)
					end
				end
			end
		end
	end

	local function pickup_step()
		local got_item
		local players = minetest.get_connected_players()
		for i = 1,#players do
			got_item = got_item or pickupfunc(players[i])
		end
		-- lower step if takeable item(s) were found
		local time
		if got_item then
			time = 0.02
		else
			time = 0.2
		end
		minetest.after(time, pickup_step)
	end
	minetest.after(3.0, pickup_step)
end

if legacy_setting_getbool("item_drop.enable_item_drop", "enable_item_drop", true)
and not minetest.settings:get_bool("creative_mode") then
	-- Workaround to test if an item metadata (ItemStackMetaRef) is empty
	local function itemmeta_is_empty(meta)
		local t = meta:to_table()
		for k, v in pairs(t) do
			if k ~= "fields" then
				return false
			end
			assert(type(v) == "table")
			if next(v) ~= nil then
				return false
			end
		end
		return true
	end

	-- Tests if the item has special information such as metadata
	local function can_split_item(item)
		return item:get_wear() == 0 and itemmeta_is_empty(item:get_meta())
	end

	local function spawn_items(pos, items_to_spawn)
		for i = 1,#items_to_spawn do
			local obj = minetest.add_item(pos, items_to_spawn[i])
			if not obj then
				error("Couldn't spawn item " .. name .. ", drops: "
					.. dump(drops))
			end

			local vel = obj:get_velocity()
			local x = math.random(-5, 4)
			if x >= 0 then
				x = x+1
			end
			vel.x = 1 / x
			local z = math.random(-5, 4)
			if z >= 0 then
				z = z+1
			end
			vel.z = 1 / z
			obj:set_velocity(vel)
		end
	end

	function minetest.handle_node_drops(pos, drops)
		for i = 1,#drops do
			local item = drops[i]
			if type(item) == "string" then
				-- The string is not necessarily only the item name,
				-- so always convert it to ItemStack
				item = ItemStack(item)
			end
			local count = item:get_count()
			local name = item:get_name()

			-- Sometimes nothing should be dropped
			if name == ""
			or not minetest.registered_items[name] then
				count = 0
			end

			if count > 0 then
				-- Split items if possible
				local items_to_spawn = {item}
				if can_split_item(item) then
					for i = 1,count do
						items_to_spawn[i] = name
					end
				end

				spawn_items(pos, items_to_spawn)
			end
		end
	end
end

local time = (minetest.get_us_time() - load_time_start) / 1000000
local msg = "[item_drop] loaded after ca. " .. time .. " seconds."
if time > 0.01 then
	print(msg)
else
	minetest.log("info", msg)
end


code:
name = item_drop
description = A highly configurable mod providing item magnet and in-world node drops


code:
#Pick up items automatically
item_drop.enable_item_pickup (Enable item pickups) bool true

#Drop items in-world on dig, does nothing in creative mode
item_drop.enable_item_drop (Enable item drops) bool true

#Use a key to pick up items
item_drop.enable_pickup_key (Use pickup key) bool true

#Collect items when the key is not pressed instead of when it is pressed
item_drop.pickup_keyinvert (Invert pickup key) bool true

#What keytype to use as pickup key
item_drop.pickup_keytype (Pickup keytype) enum Use Use,Sneak,LeftAndRight,RMB,SneakAndRMB

#The volume of the pickup sound
item_drop.pickup_sound_gain (Pickup sound gain) float 0.4

#Display a particle of the item picked up above the player
item_drop.pickup_particle (Pickup particle) bool true

#Player pickup radius, the maximum distance from which items can be collected
item_drop.pickup_radius (Pickup radius) float 0.75

#Magnet radius, items between pickup_radius and this begin flying to the player
#Set it to -1 (or something else smaller than pickup_radius) to disable item
#flying
item_drop.magnet_radius (Magnet radius) float -1

#Item flight duration, items flying for more than this time are added to the
#player's inventory
item_drop.magnet_time (Magnet time) float 5.0

#Time delay in seconds after autopicking an item if it's dropped by a player
item_drop.pickup_age (Pickup age) float 0.5

#Enable manual item pickups by mouse
item_drop.mouse_pickup (Mouse pickup) bool true