VoxeLibre/mods/ITEMS/mcl_core/functions.lua
2024-09-20 17:11:19 +02:00

1509 lines
50 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local modpath = minetest.get_modpath(minetest.get_current_modname())
local mg_name = minetest.get_mapgen_setting("mg_name")
local random = math.random
local sqrt = math.sqrt
local floor = math.floor
local ceil = math.ceil
local abs = math.abs
local max = math.max
local min = math.min
local vector_new = vector.new
local vector_zero = vector.zero
local vector_offset = vector.offset
local vector_copy = vector.copy
local vector_equals = vector.equals
local vector_add = vector.add
local vector_subtract = vector.subtract
local vector_distance = vector.distance
local OAK_TREE_ID = 1
local DARK_OAK_TREE_ID = 2
local SPRUCE_TREE_ID = 3
local ACACIA_TREE_ID = 4
local JUNGLE_TREE_ID = 5
local BIRCH_TREE_ID = 6
minetest.register_abm({
label = "Lava cooling",
nodenames = {"group:lava"},
neighbors = {"group:water"},
interval = 1,
chance = 1,
min_y = mcl_vars.mg_end_min,
action = function(pos, node, active_object_count, active_object_count_wider)
local water = minetest.find_nodes_in_area(vector_offset(pos, -1, -1, -1), vector_offset(pos, 1, 1, 1), "group:water")
local lavatype = minetest.registered_nodes[node.name].liquidtype
for w=1, #water do
--local waternode = minetest.get_node(water[w])
--local watertype = minetest.registered_nodes[waternode.name].liquidtype
-- Lava on top of water: Water turns into stone
if water[w].y < pos.y and water[w].x == pos.x and water[w].z == pos.z then
minetest.set_node(water[w], {name="mcl_core:stone"})
minetest.sound_play("fire_extinguish_flame", {pos = water[w], gain = 0.25, max_hear_distance = 16}, true)
-- Flowing lava vs water on same level: Lava turns into cobblestone
elseif lavatype == "flowing" and water[w].y == pos.y and (water[w].x == pos.x or water[w].z == pos.z) then
minetest.set_node(pos, {name="mcl_core:cobble"})
minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16}, true)
-- Lava source vs flowing water above or horizontally neighbored: Lava turns into obsidian
elseif lavatype == "source" and
((water[w].y > pos.y and water[w].x == pos.x and water[w].z == pos.z) or
(water[w].y == pos.y and (water[w].x == pos.x or water[w].z == pos.z))) then
minetest.set_node(pos, {name="mcl_core:obsidian"})
minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16}, true)
-- water above flowing lava: Lava turns into cobblestone
elseif lavatype == "flowing" and water[w].y > pos.y and water[w].x == pos.x and water[w].z == pos.z then
minetest.set_node(pos, {name="mcl_core:cobble"})
minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16}, true)
end
end
end,
})
--
-- Papyrus and cactus growing
--
function grow_cactus(pos, node)
pos.y = pos.y - 1 -- below
if minetest.get_item_group(minetest.get_node(pos).name, "sand") == 0 then return end
pos.y = pos.y + 2 -- above
local above = minetest.get_node(pos).name
if above == "air" then
minetest.set_node(pos, {name="mcl_core:cactus"})
return
end
if above ~= "mcl_core:cactus" then return end
pos.y = pos.y + 1 -- at max height 3
if minetest.get_node(pos).name == "air" then
minetest.set_node(pos, {name="mcl_core:cactus"})
end
end
function grow_reeds(pos, node)
pos.y = pos.y - 1 -- below
if minetest.get_item_group(minetest.get_node(pos).name, "soil_sugarcane") == 0 then return end
pos.y = pos.y + 2 -- above
local above = minetest.get_node(pos).name
if above == "air" then
pos.y = pos.y - 1 -- original position, check for water
if minetest.find_node_near(pos, 1, {"group:water", "group:frosted_ice"}) == nil then return end
pos.y = pos.y + 1 -- above
minetest.set_node(pos, {name="mcl_core:reeds"})
return
end
if above ~= "mcl_core:reeds" then return end
pos.y = pos.y + 1 -- at max height 3
if minetest.get_node(pos).name == "air" then
pos.y = pos.y - 2 -- original position, check for water
if minetest.find_node_near(pos, 1, {"group:water", "group:frosted_ice"}) == nil then return end
pos.y = pos.y + 2 -- above
minetest.set_node(pos, {name="mcl_core:reeds"})
end
end
-- ABMs
local function drop_attached_node(p)
local nn = minetest.get_node(p).name
if nn == "air" or nn == "ignore" then return end
minetest.remove_node(p)
for _, item in pairs(minetest.get_node_drops(nn, "")) do
if item ~= "" then
minetest.add_item(vector_offset(p, random() * 0.5 - 0.25, random() * 0.5 - 0.25, random() * 0.5 - 0.25), item)
end
end
end
-- Helper function for node actions for liquid flow
local function liquid_flow_action(pos, group, action)
local function check_detach(pos, xp, yp, zp)
local n = minetest.get_node_or_nil(vector_offset(pos, xp, yp, zp))
local d = n and minetest.registered_nodes[n.name]
if not d then return false end
--[[ Check if we want to perform the liquid action.
* 1: Item must be in liquid group
* 2a: If target node is below liquid, always succeed
* 2b: If target node is horizontal to liquid: succeed if source, otherwise check param2 for horizontal flow direction ]]
local range = d.liquid_range or 8
if minetest.get_item_group(n.name, group) ~= 0 and
(yp > 0 or
(yp == 0 and (d.liquidtype == "source" or (n.param2 > 8-range and n.param2 < 9)))) then
action(pos)
end
end
check_detach(pos, -1, 0, 0)
check_detach(pos, 1, 0, 0)
check_detach(pos, 0, 0, -1)
check_detach(pos, 0, 0, 1)
check_detach(pos, 0, 1, 0)
end
-- Drop some nodes next to flowing water, if it would flow into the node
minetest.register_abm({
label = "Wash away dig_by_water nodes by water flow",
nodenames = {"group:dig_by_water"},
neighbors = {"group:water"},
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
liquid_flow_action(pos, "water", function(pos)
drop_attached_node(pos)
minetest.remove_node(pos)
end)
end,
})
-- Destroy some nodes next to flowing lava, if it would flow into the node
minetest.register_abm({
label = "Destroy destroy_by_lava_flow nodes by lava flow",
nodenames = {"group:destroy_by_lava_flow"},
neighbors = {"group:lava"},
interval = 1,
chance = 5,
action = function(pos, node, active_object_count, active_object_count_wider)
liquid_flow_action(pos, "lava", function(pos)
minetest.remove_node(pos)
minetest.sound_play("builtin_item_lava", {pos = pos, gain = 0.25, max_hear_distance = 16}, true)
minetest.check_for_falling(pos)
end)
end,
})
-- Cactus mechanisms
minetest.register_abm({
label = "Cactus growth",
nodenames = {"mcl_core:cactus"},
neighbors = {"group:sand"},
interval = 25,
chance = 40,
action = grow_cactus
})
local function is_walkable(pos)
local ndef = minetest.registered_nodes[minetest.get_node(pos).name]
return ndef and ndef.walkable
end
minetest.register_abm({
label = "Cactus mechanisms",
nodenames = {"mcl_core:cactus"},
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
for _, object in pairs(minetest.get_objects_inside_radius(pos, 0.9)) do
local entity = object:get_luaentity()
if entity and entity.name == "__builtin:item" then
object:remove()
end
end
if is_walkable(vector_offset(pos, 1, 0, 0))
or is_walkable(vector_offset(pos, -1, 0, 0))
or is_walkable(vector_offset(pos, 0, 0, 1))
or is_walkable(vector_offset(pos, 0, 0, -1)) then
local lpos = vector_copy(pos)
local dx, dy
while true do
local node = minetest.get_node(lpos)
if not node or node.name ~= "mcl_core:cactus" then break end
-- minetest.dig_node ignores protected nodes and causes infinite drop (#4628)
minetest.remove_node(lpos)
dx = dx or ((random(0,1)-0.5) * sqrt(random())) * 1.5
dy = dy or ((random(0,1)-0.5) * sqrt(random())) * 1.5
local obj = minetest.add_item(vector_offset(lpos, dx, 0.25, dy), "mcl_core:cactus")
obj:set_velocity(vector_new(dx, 1, dy))
lpos.y = lpos.y + 1
end
end
end,
})
minetest.register_abm({
label = "Sugar canes growth",
nodenames = {"mcl_core:reeds"},
neighbors = {"group:soil_sugarcane"},
interval = 25,
chance = 40,
action = grow_reeds
})
--
-- Sugar canes drop
--
minetest.register_on_dignode(function(pos, node)
local name = "mcl_core:reeds"
local np = vector_offset(pos, 0, 1, 0)
while minetest.get_node(np).name == name do
minetest.remove_node(np)
minetest.add_item(np, name)
np.y = np.y + 1
end
end)
-- Check if a node stops a tree from growing. Torches, plants, wood, tree,
-- leaves and dirt does not affect tree growth.
local function node_stops_growth(node)
if node.name == "air" then return false end
local def = minetest.registered_nodes[node.name]
local groups = def and def.groups
if not groups then return true end
return not (groups.leaves or groups.wood or groups.tree or groups.plant or groups.dirt or groups.torch or groups.bark)
end
-- Check if a tree can grow at position. The width is the width to check
-- around the tree. A width of 3 and height of 5 will check a 3x3 area, 5
-- nodes above the sapling. If any walkable node other than dirt, wood or
-- leaves occurs in those blocks the tree cannot grow.
local function check_growth_width(pos, width, height)
-- Huge tree (with even width to check) will check one more node in
-- positive x and y directions.
local neg_space, pos_space = floor((width - 1) * 0.5), ceil((width - 1) * 0.5)
for x = -neg_space, pos_space do
for z = -neg_space, pos_space do
for y = 1, height do
if node_stops_growth(minetest.get_node(vector_offset(pos, x, y, z))) then
return false
end
end
end
end
return true
end
mcl_core.check_growth_width = check_growth_width
-- Check if a tree with id can grow at a position. Options is a table of flags
-- for varieties of trees. The 'two_by_two' option is used to check if there is
-- room to generate huge trees for spruce and jungle. The 'balloon' option is
-- used to check if there is room to generate a balloon tree for oak.
local function check_tree_growth(pos, tree_id, options)
local two_by_two = options and options.two_by_two
local balloon = options and options.balloon
if tree_id == OAK_TREE_ID then
if balloon then
return check_growth_width(pos, 7, 11)
else
return check_growth_width(pos, 3, 5)
end
elseif tree_id == BIRCH_TREE_ID then
return check_growth_width(pos, 3, 6)
elseif tree_id == SPRUCE_TREE_ID then
if two_by_two then
return check_growth_width(pos, 6, 20)
else
return check_growth_width(pos, 5, 11)
end
elseif tree_id == JUNGLE_TREE_ID then
if two_by_two then
return check_growth_width(pos, 8, 23)
else
return check_growth_width(pos, 3, 8)
end
elseif tree_id == ACACIA_TREE_ID then
return check_growth_width(pos, 7, 8)
elseif tree_id == DARK_OAK_TREE_ID and two_by_two then
return check_growth_width(pos, 4, 7)
end
return false
end
-- Generates a tree with a type. Options is a table of flags for varieties of
-- trees. The 'two_by_two' option is used by jungle and spruce trees to
-- generate huge trees. The 'balloon' option is used by oak to generate a balloon
-- oak tree.
function mcl_core.generate_tree(pos, tree_type, options)
if not minetest.get_node_light(pos) then return end
local two_by_two = options and options.two_by_two
local balloon = options and options.balloon
if tree_type == nil or tree_type == OAK_TREE_ID then
if mg_name == "v6" then
mcl_core.generate_v6_oak_tree(pos)
else
if balloon then
mcl_core.generate_balloon_oak_tree(pos)
else
mcl_core.generate_oak_tree(pos)
end
end
elseif tree_type == DARK_OAK_TREE_ID then
mcl_core.generate_dark_oak_tree(pos)
elseif tree_type == SPRUCE_TREE_ID then
if two_by_two then
mcl_core.generate_huge_spruce_tree(pos)
else
if mg_name == "v6" then
mcl_core.generate_v6_spruce_tree(pos)
else
mcl_core.generate_spruce_tree(pos)
end
end
elseif tree_type == ACACIA_TREE_ID then
mcl_core.generate_acacia_tree(pos)
elseif tree_type == JUNGLE_TREE_ID then
if two_by_two then
mcl_core.generate_huge_jungle_tree(pos)
else
if mg_name == "v6" then
mcl_core.generate_v6_jungle_tree(pos)
else
mcl_core.generate_jungle_tree(pos)
end
end
elseif tree_type == BIRCH_TREE_ID then
mcl_core.generate_birch_tree(pos)
end
mcl_core.update_sapling_foliage_colors(pos)
end
-- Classic oak in v6 style
function mcl_core.generate_v6_oak_tree(p)
local pos = vector_copy(p)
for dy = 1, 4 do
pos.y = p.y + dy
if minetest.get_node(pos).name ~= "air" then return end
end
local trunk = {name = "mcl_core:tree" }
for dy = 0, 4 do
pos.y = p.y + dy
if minetest.get_node(pos).name == "air" then minetest.add_node(pos, trunk) end
end
local leaves = { name = "mcl_core:leaves" }
for dx = -2, 2 do
for dz = -2, 2 do
for dy = 3, 6 do
pos.x, pos.y, pos.z = p.x + dx, p.y + dy, p.z + dz
if dy == 6 then
if dx == 0 and dz == 0 and minetest.get_node(pos).name == "air" and random(1, 5) <= 4 then
minetest.add_node(pos, leaves)
end
elseif abs(dx) ~= 2 and abs(dz) ~= 2 then
if minetest.get_node(pos).name == "air" then
minetest.add_node(pos, leaves)
end
elseif abs(dx) ~= 2 or abs(dz) ~= 2 then
if minetest.get_node(pos).name == "air" and random(1, 5) <= 4 then
minetest.add_node(pos, leaves)
end
end
end
end
end
end
-- Ballon Oak
function mcl_core.generate_balloon_oak_tree(pos)
if random(1, 12) == 1 then
-- Small balloon oak
minetest.place_schematic(vector_offset(pos, -2, -1, -2),
modpath .. "/schematics/mcl_core_oak_balloon.mts",
"random", nil, false)
return
end
-- Large balloon oak
local t = random(1, 4)
local path = modpath .. "/schematics/mcl_core_oak_large_"..t..".mts"
if t == 1 or t == 3 then
minetest.place_schematic(vector_offset(pos, -3, -1, -3), path, "random", nil, false)
elseif t == 2 or t == 4 then
minetest.place_schematic(vector_offset(pos, -4, -1, -4), path, "random", nil, false)
end
end
-- Oak
local path_oak_tree = modpath.."/schematics/mcl_core_oak_classic.mts"
function mcl_core.generate_oak_tree(pos)
minetest.place_schematic(vector_offset(pos, -2, -1, -2 ), path_oak_tree, "random", nil, false)
end
-- Birch
function mcl_core.generate_birch_tree(pos)
minetest.place_schematic(vector_offset(pos, -2, -1, -2), modpath .. "/schematics/mcl_core_birch.mts", "random", nil, false)
end
-- BEGIN of spruce tree generation functions --
-- Copied from Minetest Game 0.4.15 from the pine tree (default.generate_pine_tree)
-- Pine tree (=spruce tree in MCL2) from mg mapgen mod, design by sfan5, pointy top added by paramat
local function add_spruce_leaves(data, vi, c_air, c_ignore, c_snow, c_spruce_leaves)
local node_id = data[vi]
if node_id == c_air or node_id == c_ignore or node_id == c_snow then
data[vi] = c_spruce_leaves
end
end
function mcl_core.generate_v6_spruce_tree(pos)
local x, y, z = pos.x, pos.y, pos.z
local maxy = y + random(9, 13) -- Trunk top
local c_air = minetest.get_content_id("air")
local c_ignore = minetest.get_content_id("ignore")
local c_spruce_tree = minetest.get_content_id("mcl_core:sprucetree")
local c_spruce_leaves = minetest.get_content_id("mcl_core:spruceleaves")
local c_snow = minetest.get_content_id("mcl_core:snow")
local vm = minetest.get_voxel_manip()
local minp, maxp = vm:read_from_map(vector_offset(pos, -3, 0, -3), vector_offset(pos, 3, maxy - y + 3, 3))
local a = VoxelArea:new(minp, maxp)
local data = vm:get_data()
-- Upper branches layer
local dev = 3
for yy = maxy - 1, maxy + 1 do
for zz = z - dev, z + dev do
local vi = a:index(x - dev, yy, zz)
for xx = x - dev, x + dev do
if random() < 0.95 - dev * 0.05 then
add_spruce_leaves(data, vi, c_air, c_ignore, c_snow, c_spruce_leaves)
end
vi = vi + 1
end
end
dev = dev - 1
end
-- Centre top nodes
add_spruce_leaves(data, a:index(x, maxy + 1, z), c_air, c_ignore, c_snow, c_spruce_leaves)
add_spruce_leaves(data, a:index(x, maxy + 2, z), c_air, c_ignore, c_snow, c_spruce_leaves)
-- Lower branches layer
local my = 0
for i = 1, 20 do -- Random 2x2 squares of leaves
local xi = x + random(-3, 2)
local yy = maxy + random(-6, -5)
local zi = z + random(-3, 2)
if yy > my then my = yy end
for zz = zi, zi + 1 do
local vi = a:index(xi, yy, zz)
for xx = xi, xi + 1 do
add_spruce_leaves(data, vi, c_air, c_ignore, c_snow, c_spruce_leaves)
vi = vi + 1
end
end
end
dev = 2
for yy = my + 1, my + 2 do
for zz = z - dev, z + dev do
local vi = a:index(x - dev, yy, zz)
for xx = x - dev, x + dev do
if random() < 0.95 - dev * 0.05 then
add_spruce_leaves(data, vi, c_air, c_ignore, c_snow, c_spruce_leaves)
end
vi = vi + 1
end
end
dev = dev - 1
end
-- Trunk
-- Force-place lowest trunk node to replace sapling
data[a:index(x, y, z)] = c_spruce_tree
for yy = y + 1, maxy do
local vi = a:index(x, yy, z)
local node_id = data[vi]
if node_id == c_air or node_id == c_ignore or node_id == c_spruce_leaves or node_id == c_snow then
data[vi] = c_spruce_tree
end
end
vm:set_data(data)
vm:write_to_map()
end
function mcl_core.generate_spruce_tree(pos)
minetest.place_schematic(vector_offset(pos, -3, -1, -3),
modpath .. "/schematics/mcl_core_spruce_"..random(1, 3)..".mts", "0", nil, false)
end
local function find_necorner(p)
local n = minetest.get_node_or_nil(vector_offset(p, 0, 1, 1))
local e = minetest.get_node_or_nil(vector_offset(p, 1, 1, 0))
if n and n.name == "mcl_core:sprucetree" then
p = vector_offset(p, 0, 0, 1)
end
if e and e.name == "mcl_core:sprucetree" then
p = vector_offset(p, 1, 0, 0)
end
return p
end
local function generate_spruce_podzol(ps)
local pos = find_necorner(ps)
local pos1, pos2 = vector_offset(pos, -6, -6, -6), vector_offset(pos, 6, 6, 6)
local nn = minetest.find_nodes_in_area_under_air(pos1, pos2, {"group:dirt"})
for k,v in pairs(nn) do
if not (abs(pos.x - v.x) == 6 and abs(pos.z - v.z) == 6) and random(vector_distance(pos,v)) < 4 then --leave out the corners
minetest.set_node(v, {name="mcl_core:podzol"})
end
end
end
function mcl_core.generate_huge_spruce_tree(pos)
local r1, r2 = random(1, 2), random(1, 4)
local path, offset
if r1 <= 2 then
-- Mega Spruce Taiga (full canopy)
path = modpath.."/schematics/mcl_core_spruce_huge_"..r2..".mts"
offset = vector_offset(pos, -4, -1, -5)
else
-- Mega Taiga (leaves only at top)
if r2 == 1 or r2 == 3 then
offset = vector_offset(pos, -3, -1, -4)
else
offset = vector_offset(pos, -4, -1, -5)
end
path = modpath.."/schematics/mcl_core_spruce_huge_up_"..r2..".mts"
end
minetest.place_schematic(offset, path, "0", nil, false)
generate_spruce_podzol(pos)
end
-- END of spruce tree functions --
-- Acacia tree (multiple variants)
function mcl_core.generate_acacia_tree(pos)
local r = random(1, 7)
local path, offset = modpath.."/schematics/mcl_core_acacia_"..r..".mts", nil
if r == 1 or r == 5 then
offset = vector_offset(pos, -5, -1, -5)
elseif r == 2 or r == 3 then
offset = vector_offset(pos, -4, -1, -4)
elseif r == 4 or r == 6 or r == 7 then
offset = vector_offset(pos, -3, -1, -3)
end
minetest.place_schematic(offset, path, "random", nil, false)
end
-- Generate dark oak tree with 2×2 trunk.
-- With pos being the lower X and the higher Z value of the trunk
function mcl_core.generate_dark_oak_tree(pos)
minetest.place_schematic(vector_offset(pos, -3, -1, -4), modpath.."/schematics/mcl_core_dark_oak.mts", "random", nil, false)
end
-- Helper function for jungle tree, form Minetest Game 0.4.15
local function add_trunk_and_leaves(data, a, pos, tree_cid, leaves_cid, height, size, iters)
local x, y, z = pos.x, pos.y, pos.z
local c_air = minetest.CONTENT_AIR
local c_ignore = minetest.CONTENT_IGNORE
-- Trunk
data[a:index(x, y, z)] = tree_cid -- Force-place lowest trunk node to replace sapling
for yy = y + 1, y + height - 1 do
local vi = a:index(x, yy, z)
local node_id = data[vi]
if node_id == c_air or node_id == c_ignore or node_id == leaves_cid then
data[vi] = tree_cid
end
end
-- Force leaves near the trunk
for z_dist = -1, 1 do
for y_dist = -size, 1 do
local vi = a:index(x - 1, y + height + y_dist, z + z_dist)
for x_dist = -1, 1 do
if data[vi] == c_air or data[vi] == c_ignore then
data[vi] = leaves_cid
end
vi = vi + 1
end
end
end
-- Randomly add leaves in 2x2x2 clusters.
for i = 1, iters do
local clust_x = x + random(-size, size - 1)
local clust_y = y + height + random(-size, 0)
local clust_z = z + random(-size, size - 1)
for xi = 0, 1 do
for yi = 0, 1 do
for zi = 0, 1 do
local vi = a:index(clust_x + xi, clust_y + yi, clust_z + zi)
if data[vi] == c_air or data[vi] == c_ignore then
data[vi] = leaves_cid
end
end
end
end
end
end
-- Old jungle tree grow function from Minetest Game 0.4.15, imitating v6 jungle trees
function mcl_core.generate_v6_jungle_tree(pos)
--[[
NOTE: Jungletree-placing code is currently duplicated in the engine
and in games that have saplings; both are deprecated but not
replaced yet
--]]
local x, y, z = pos.x, pos.y, pos.z
local height = random(8, 12)
local c_air = minetest.get_content_id("air")
local c_ignore = minetest.get_content_id("ignore")
local c_jungletree = minetest.get_content_id("mcl_core:jungletree")
local c_jungleleaves = minetest.get_content_id("mcl_core:jungleleaves")
local vm = minetest.get_voxel_manip()
local minp, maxp = vm:read_from_map(vector_offset(pos, -3, -1, -3), vector_offset(pos, 3, 1, 3))
local a = VoxelArea:new(minp, maxp)
local data = vm:get_data()
add_trunk_and_leaves(data, a, pos, c_jungletree, c_jungleleaves, height, 3, 30)
-- Roots
for z_dist = -1, 1 do
local vi_1 = a:index(x - 1, y - 1, z + z_dist)
local vi_2 = a:index(x - 1, y, z + z_dist)
for x_dist = -1, 1 do
if random(1, 3) >= 2 then
if data[vi_1] == c_air or data[vi_1] == c_ignore then
data[vi_1] = c_jungletree
elseif data[vi_2] == c_air or data[vi_2] == c_ignore then
data[vi_2] = c_jungletree
end
end
vi_1 = vi_1 + 1
vi_2 = vi_2 + 1
end
end
vm:set_data(data)
vm:write_to_map()
end
function mcl_core.generate_jungle_tree(pos)
minetest.place_schematic(vector_offset(pos, -2, -1, -2), modpath.."/schematics/mcl_core_jungle_tree.mts", "random", nil, false)
end
-- Generate huge jungle tree with 2×2 trunk.
-- With pos being the lower X and the higher Z value of the trunk.
function mcl_core.generate_huge_jungle_tree(pos)
minetest.place_schematic(vector_offset(pos, -6, -1, -7),
modpath.."/schematics/mcl_core_jungle_tree_huge_"..random(1, 2)..".mts",
"random", nil, false)
end
local grass_spread_randomizer = PseudoRandom(minetest.get_mapgen_setting("seed"))
-- Return appropriate grass block node for pos
function mcl_core.get_grass_block_type(pos, requested_grass_block_name)
return {name = requested_grass_block_name or minetest.get_node(pos).name, param2 = mcl_util.get_palette_indexes_from_pos(pos).grass_palette_index}
end
-- Return appropriate foliage block node for pos
function mcl_core.get_foliage_block_type(pos)
return {name = minetest.get_node(pos).name, param2 = mcl_util.get_palette_indexes_from_pos(pos).foliage_palette_index}
end
-- Return appropriate water block node for pos
function mcl_core.get_water_block_type(pos)
return {name = minetest.get_node(pos).name, param2 = mcl_util.get_palette_indexes_from_pos(pos).water_palette_index}
end
------------------------------
-- Spread grass blocks and mycelium on neighbor dirt
------------------------------
minetest.register_abm({
label = "Grass block and mycelium spread",
nodenames = {"mcl_core:dirt"},
neighbors = {"air", "group:grass_block_no_snow", "mcl_core:mycelium"},
interval = 30,
chance = 20,
catch_up = false,
action = function(pos)
if pos == nil then return end
local above = vector_offset(pos, 0, 1, 0)
local abovenode = minetest.get_node(above)
if minetest.get_item_group(abovenode.name, "liquid") ~= 0 or minetest.get_item_group(abovenode.name, "opaque") == 1 then
-- Never grow directly below liquids or opaque blocks
return
end
local light_self = minetest.get_node_light(above)
if not light_self then return end
--[[ Try to find a spreading dirt-type block (e.g. grass block or mycelium)
within a 3×5×3 area, with the source block being on the 2nd-topmost layer. ]]
local nodes = minetest.find_nodes_in_area(vector_offset(pos, -1, -1, -1), vector_offset(pos, 1, 3, 1), "group:spreading_dirt_type")
-- Nothing found ? Bail out!
if #nodes <= 0 then return end
local p2 = nodes[grass_spread_randomizer:next(1, #nodes)]
-- Found it! Now check light levels!
local source_above = vector_offset(p2, 0, 1, 0)
local light_source = minetest.get_node_light(source_above)
if not light_source then return end
if light_self >= 4 and light_source >= 9 then
-- All checks passed! Let's spread the grass/mycelium!
local n2 = minetest.get_node(p2)
if minetest.get_item_group(n2.name, "grass_block") ~= 0 then
n2 = mcl_core.get_grass_block_type(pos, "mcl_core:dirt_with_grass")
end
minetest.set_node(pos, {name=n2.name})
-- If this was mycelium, uproot plant above
if n2.name == "mcl_core:mycelium" then
local tad = minetest.registered_nodes[minetest.get_node(above).name]
if tad and tad.groups and tad.groups.non_mycelium_plant then
minetest.dig_node(above)
end
end
end
end
})
-- Grass/mycelium death in darkness
minetest.register_abm({
label = "Grass block / mycelium in darkness",
nodenames = {"group:spreading_dirt_type"},
interval = 8,
chance = 50,
catch_up = false,
action = function(pos, node)
local above = minetest.get_node(vector_offset(pos, 0, 1, 0)).name
-- Kill grass/mycelium when below opaque block or liquid
if above ~= "ignore" and (minetest.get_item_group(above, "opaque") == 1 or minetest.get_item_group(above, "liquid") ~= 0) then
minetest.set_node(pos, {name = "mcl_core:dirt"})
end
end
})
-- Turn Grass Path and similar nodes to Dirt if a solid node is placed above it
minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing)
if minetest.get_item_group(newnode.name, "solid") ~= 0 or minetest.get_item_group(newnode.name, "dirtifier") ~= 0 then
local below = vector_offset(pos, 0, -1, 0)
local belownode = minetest.get_node(below)
if minetest.get_item_group(belownode.name, "dirtifies_below_solid") == 1 then
minetest.set_node(below, {name="mcl_core:dirt"})
end
end
end)
minetest.register_abm({
label = "Turn grass path below solid block into dirt",
nodenames = {"mcl_core:grass_path"},
neighbors = {"group:solid"},
interval = 8,
chance = 50,
action = function(pos, node)
local above = minetest.get_node(vector_offset(pos, 0, 1, 0)).name
if above == "ignore" then return end
local nodedef = minetest.registered_nodes[above]
if nodedef and (nodedef.groups and nodedef.groups.solid) then
minetest.set_node(pos, {name = "mcl_core:dirt"})
end
end,
})
local SAVANNA_INDEX = 1
minetest.register_lbm({
label = "Replace legacy dry grass",
name = "mcl_core:replace_legacy_dry_grass_0_65_0",
nodenames = {"mcl_core:dirt_with_dry_grass", "mcl_core:dirt_with_dry_grass_snow"},
run_at_every_load = true,
action = function(pos, node)
node.name = node.name == "mcl_core:dirt_with_dry_grass_snow" and "mcl_core:dirt_with_grass_snow" or "mcl_core:dirt_with_grass"
-- use savanna palette index to simulate dry grass.
node.param2 = node.param2 or SAVANNA_INDEX
minetest.set_node(pos, node)
end,
})
--------------------------
-- Try generate tree ---
--------------------------
local treelight = 9
local function sapling_grow_action(tree_id, soil_needed, one_by_one, two_by_two, sapling)
return function(pos)
local meta = minetest.get_meta(pos)
if meta:get("grown") then return end
-- Checks if the sapling at pos has enough light and the correct soil
local light = minetest.get_node_light(pos)
if not light then return end
local low_light = light < treelight
local delta = 1
local current_game_time = minetest.get_day_count() + minetest.get_timeofday()
local last_game_time = tonumber(meta:get_string("last_gametime"))
meta:set_string("last_gametime", tostring(current_game_time))
if last_game_time then
delta = current_game_time - last_game_time
elseif low_light then
return
end
if low_light then
if delta < 1.2 then return end
if minetest.get_node_light(pos, 0.5) < treelight then return end
end
-- TODO: delta is [days] missed in inactive area. Currently we just add it to stage, which is far from a perfect calculation...
local soilnode = minetest.get_node(vector_offset(pos, 0, -1, 0))
local soiltype = minetest.get_item_group(soilnode.name, "soil_sapling")
if soiltype < soil_needed then return end
-- Increase and check growth stage
local meta = minetest.get_meta(pos)
local stage = meta:get_int("stage")
if stage == nil then stage = 0 end
stage = stage + max(1, floor(delta))
if stage >= 3 then
meta:set_string("grown", "true")
-- This sapling grows in a special way when there are 4 saplings in a 2×2 pattern
if two_by_two then
-- Check 8 surrounding saplings and try to find a 2×2 pattern
local function is_sapling(pos, sapling)
return minetest.get_node(pos).name == sapling
end
-- clockwise from +x
local p2 = vector_offset(pos, 1, 0, 0)
local p3 = vector_offset(pos, 1, 0, -1)
local p4 = vector_offset(pos, 0, 0, -1)
local p5 = vector_offset(pos, -1, 0, -1)
local p6 = vector_offset(pos, -1, 0, 0)
local p7 = vector_offset(pos, -1, 0, 1)
local p8 = vector_offset(pos, 0, 0, 1)
local p9 = vector_offset(pos, 1, 0, 1)
local s2 = is_sapling(p2, sapling)
local s3 = is_sapling(p3, sapling)
local s4 = is_sapling(p4, sapling)
local s5 = is_sapling(p5, sapling)
local s6 = is_sapling(p6, sapling)
local s7 = is_sapling(p7, sapling)
local s8 = is_sapling(p8, sapling)
local s9 = is_sapling(p9, sapling)
-- In a 9×9 field there are 4 possible 2×2 squares. We check them all.
if s2 and s3 and s4 and check_tree_growth(pos, tree_id, { two_by_two = true }) then
-- Success: Remove saplings and place tree
minetest.remove_node(pos)
minetest.remove_node(p2)
minetest.remove_node(p3)
minetest.remove_node(p4)
mcl_core.generate_tree(pos, tree_id, { two_by_two = true })
return
elseif s4 and s5 and s6 and check_tree_growth(p6, tree_id, { two_by_two = true }) then
minetest.remove_node(pos)
minetest.remove_node(p4)
minetest.remove_node(p5)
minetest.remove_node(p6)
mcl_core.generate_tree(p6, tree_id, { two_by_two = true })
return
elseif s6 and s7 and s8 and check_tree_growth(p7, tree_id, { two_by_two = true }) then
minetest.remove_node(pos)
minetest.remove_node(p6)
minetest.remove_node(p7)
minetest.remove_node(p8)
mcl_core.generate_tree(p7, tree_id, { two_by_two = true })
return
elseif s8 and s9 and s2 and check_tree_growth(p8, tree_id, { two_by_two = true }) then
minetest.remove_node(pos)
minetest.remove_node(p8)
minetest.remove_node(p9)
minetest.remove_node(p2)
mcl_core.generate_tree(p8, tree_id, { two_by_two = true })
return
end
end
if one_by_one and tree_id == OAK_TREE_ID then
-- There is a chance that this tree wants to grow as a balloon oak
if random(1, 12) == 1 then
-- Check if there is room for that
if check_tree_growth(pos, tree_id, { balloon = true }) then
minetest.set_node(pos, {name="air"})
mcl_core.generate_tree(pos, tree_id, { balloon = true })
return
end
end
end
-- If this sapling can grow alone
if one_by_one and check_tree_growth(pos, tree_id) then
-- Single sapling
minetest.set_node(pos, {name="air"})
mcl_core.generate_tree(pos, tree_id)
return
end
else
meta:set_int("stage", stage)
end
end
end
local grow_oak = sapling_grow_action(OAK_TREE_ID, 1, true, false)
local grow_dark_oak = sapling_grow_action(DARK_OAK_TREE_ID, 2, false, true, "mcl_core:darksapling")
local grow_jungle_tree = sapling_grow_action(JUNGLE_TREE_ID, 1, true, true, "mcl_core:junglesapling")
local grow_acacia = sapling_grow_action(ACACIA_TREE_ID, 2, true, false)
local grow_spruce = sapling_grow_action(SPRUCE_TREE_ID, 1, true, true, "mcl_core:sprucesapling")
local grow_birch = sapling_grow_action(BIRCH_TREE_ID, 1, true, false)
function mcl_core.update_sapling_foliage_colors(pos)
local foliage = minetest.find_nodes_in_area(
vector_offset(pos, -8, 0, -8), vector_offset(pos, 8, 30, 8),
{"group:foliage_palette", "group:foliage_palette_wallmounted"})
for _, fpos in pairs(foliage) do
minetest.set_node(fpos, minetest.get_node(fpos))
end
end
--- Attempts to grow the sapling at the specified position
-- pos: Position
-- node: Node table of the node at this position, from minetest.get_node
-- Returns true on success and false on failure
function mcl_core.grow_sapling(pos, node)
if node.name == "mcl_core:sapling" then
grow_oak(pos)
elseif node.name == "mcl_core:darksapling" then
grow_dark_oak(pos)
elseif node.name == "mcl_core:junglesapling" then
grow_jungle_tree(pos)
elseif node.name == "mcl_core:acaciasapling" then
grow_acacia(pos)
elseif node.name == "mcl_core:sprucesapling" then
grow_spruce(pos)
elseif node.name == "mcl_core:birchsapling" then
grow_birch(pos)
else
return false
end
return true
end
-- TODO: Use better tree models for everything
-- TODO: Support 2×2 saplings
-- Oak tree
minetest.register_abm({
label = "Oak tree growth",
nodenames = {"mcl_core:sapling"},
neighbors = {"group:soil_sapling"},
interval = 25,
chance = 2,
action = grow_oak
})
minetest.register_lbm({
label = "Add growth for unloaded oak tree",
name = "mcl_core:lbm_oak",
nodenames = {"mcl_core:sapling"},
run_at_every_load = true,
action = grow_oak
})
-- Dark oak tree
minetest.register_abm({
label = "Dark oak tree growth",
nodenames = {"mcl_core:darksapling"},
neighbors = {"group:soil_sapling"},
interval = 25,
chance = 2,
action = grow_dark_oak
})
minetest.register_lbm({
label = "Add growth for unloaded dark oak tree",
name = "mcl_core:lbm_dark_oak",
nodenames = {"mcl_core:darksapling"},
run_at_every_load = true,
action = grow_dark_oak
})
-- Jungle Tree
minetest.register_abm({
label = "Jungle tree growth",
nodenames = {"mcl_core:junglesapling"},
neighbors = {"group:soil_sapling"},
interval = 25,
chance = 2,
action = grow_jungle_tree
})
minetest.register_lbm({
label = "Add growth for unloaded jungle tree",
name = "mcl_core:lbm_jungle_tree",
nodenames = {"mcl_core:junglesapling"},
run_at_every_load = true,
action = grow_jungle_tree
})
-- Spruce tree
minetest.register_abm({
label = "Spruce tree growth",
nodenames = {"mcl_core:sprucesapling"},
neighbors = {"group:soil_sapling"},
interval = 25,
chance = 2,
action = grow_spruce
})
minetest.register_lbm({
label = "Add growth for unloaded spruce tree",
name = "mcl_core:lbm_spruce",
nodenames = {"mcl_core:sprucesapling"},
run_at_every_load = true,
action = grow_spruce
})
-- Birch tree
minetest.register_abm({
label = "Birch tree growth",
nodenames = {"mcl_core:birchsapling"},
neighbors = {"group:soil_sapling"},
interval = 25,
chance = 2,
action = grow_birch
})
minetest.register_lbm({
label = "Add growth for unloaded birch tree",
name = "mcl_core:lbm_birch",
nodenames = {"mcl_core:birchsapling"},
run_at_every_load = true,
action = grow_birch
})
-- Acacia tree
minetest.register_abm({
label = "Acacia tree growth",
nodenames = {"mcl_core:acaciasapling"},
neighbors = {"group:soil_sapling"},
interval = 20,
chance = 2,
action = grow_acacia
})
minetest.register_lbm({
label = "Add growth for unloaded acacia tree",
name = "mcl_core:lbm_acacia",
nodenames = {"mcl_core:acaciasapling"},
run_at_every_load = true,
action = grow_acacia
})
local function leafdecay_particles(pos, node)
minetest.add_particlespawner({
amount = random(10, 20),
time = 0.1,
minpos = vector_offset(pos, -0.4, -0.4, -0.4),
maxpos = vector_offset(pos, 0.4, 0.4, 0.4),
minvel = vector_new(-0.2, -0.2, -0.2),
maxvel = vector_new(0.2, 0.1, 0.2),
minacc = vector_new(0, -9.81, 0),
maxacc = vector_new(0, -9.81, 0),
minexptime = 0.1,
maxexptime = 0.5,
minsize = 0.5,
maxsize = 1.5,
collisiondetection = true,
vertical = false,
node = node,
})
end
local function vinedecay_particles(pos, node)
local dir = minetest.wallmounted_to_dir(node.param2)
local minpos, maxpos
if dir.x < 0 then
minpos = vector_offset(pos, -0.45, -0.4, -0.5)
maxpos = vector_offset(pos, -0.4, 0.4, 0.5)
elseif dir.x > 0 then
minpos = vector_offset(pos, 0.4, -0.4, -0.5)
maxpos = vector_offset(pos, 0.45, 0.4, 0.5)
elseif dir.z < 0 then
minpos = vector_offset(pos, -0.5, -0.4, -0.45)
maxpos = vector_offset(pos, 0.5, 0.4, -0.4)
elseif dir.z > 0 then
minpos = vector_offset(pos, -0.5, -0.4, 0.4)
maxpos = vector_offset(pos, 0.5, 0.4, 0.45)
else
return
end
minetest.add_particlespawner({
amount = random(8, 16),
time = 0.1,
minpos = minpos,
maxpos = maxpos,
minvel = vector_new(-0.2, -0.2, -0.2),
maxvel = vector_new( 0.2, 0.1, 0.2),
minacc = vector_new(0, -9.81, 0),
maxacc = vector_new(0, -9.81, 0),
minexptime = 0.1,
maxexptime = 0.5,
minsize = 0.5,
maxsize = 1.0,
collisiondetection = true,
vertical = false,
node = node,
})
end
-----------------
-- Vine growth --
-----------------
-- Add vines below pos (if empty)
local function vine_spread_down(origin, node)
if random(1, 2) == 1 then return end
local target = vector_offset(origin, 0, -1, 0)
if minetest.get_node(target).name == "air" then
minetest.add_node(target, {name = "mcl_core:vine", param2 = node.param2})
end
end
-- Add vines above pos if it is backed up
local function vine_spread_up(origin, node)
if random(1, 2) == 1 then return end
local vines_in_area = minetest.find_nodes_in_area(vector_offset(origin, -4, -1, -4), vector_offset(origin, 4, 1, 4), "mcl_core:vine")
-- Less than 4 other vines blocks around the ticked vines block (remember the ticked block is counted by above function as well)
if #vines_in_area >= 5 then return end
local target = vector_offset(origin, 0, 1, 0)
if minetest.get_node(target).name ~= "air" then return end
local backupnodename = minetest.get_node(vector_subtract(target, minetest.wallmounted_to_dir(node.param2))).name
-- Check if the block above is supported
if mcl_core.supports_vines(backupnodename) then
minetest.add_node(target, {name = "mcl_core:vine", param2 = node.param2})
end
end
local function vine_spread_horizontal(origin, dir, node)
local vines_in_area = minetest.find_nodes_in_area(vector_offset(origin, -4, -1, -4), vector_offset(origin, 4, 1, 4), "mcl_core:vine")
if #vines_in_area >= 5 then return end
-- Less than 4 other vines blocks around the ticked vines block (remember the ticked block is counted by above function as well)
local target = vector_add(origin, dir)
-- Spread horizontally, but not into support direction
local backup_dir = minetest.wallmounted_to_dir(node.param2)
if backup_dir.x == dir.x and backup_dir.y == dir.y then return end
local target_node = minetest.get_node(target)
if target_node.name ~= "air" then return end
local backupnodename = minetest.get_node(vector_add(target, backup_dir)).name
if mcl_core.supports_vines(backupnodename) then
minetest.add_node(target, {name = "mcl_core:vine", param2 = node.param2})
end
end
minetest.register_abm({
label = "Vine growth",
nodenames = {"mcl_core:vine"},
interval = 47,
chance = 4,
action = function(pos, node, active_object_count, active_object_count_wider)
-- First of all, check if we are even supported, otherwise, decay.
if not mcl_core.check_vines_supported(pos, node) then
minetest.remove_node(pos)
vinedecay_particles(pos, node)
minetest.check_for_falling(pos)
return
end
local d = random(1, 6)
if d == 1 then
vine_spread_horizontal(pos, vector_new( 1, 0, 0), node)
elseif d == 2 then
vine_spread_horizontal(pos, vector_new(-1, 0, 0), node)
elseif d == 3 then
vine_spread_horizontal(pos, vector_new( 0, 0, 1), node)
elseif d == 4 then
vine_spread_horizontal(pos, vector_new( 0, 0, -1), node)
elseif d == 5 then
vine_spread_up(pos, node)
else
vine_spread_down(pos, node)
end
end
})
-- Returns true of the node supports vines
function mcl_core.supports_vines(nodename)
local def = minetest.registered_nodes[nodename]
-- Rules: 1) walkable 2) full cube
return def and def.walkable and
(def.node_box == nil or def.node_box.type == "regular") and
(def.collision_box == nil or def.collision_box.type == "regular")
end
-- Leaf Decay
--
-- Whenever a tree trunk node is removed, all `group:leaves` nodes in a radius
-- of 6 blocks are checked from the trunk node's `after_destruct` handler.
-- Any such nodes within that radius that has no trunk node present within a
-- distance of 6 blocks is replaced with a `group:orphan_leaves` node.
--
-- The `group:orphan_leaves` nodes are gradually decayed in this ABM.
minetest.register_abm({
label = "Leaf decay",
nodenames = {"group:orphan_leaves"},
interval = 5,
chance = 10,
action = function(pos, node)
-- Spawn item entities for any of the leaf's drops
local itemstacks = minetest.get_node_drops(node.name)
for _, itemname in pairs(itemstacks) do
minetest.add_item(vector_offset(pos, random() - 0.5, random() - 0.5, random() - 0.5), itemname)
end
-- Remove the decayed node
minetest.remove_node(pos)
leafdecay_particles(pos, node)
minetest.check_for_falling(pos)
-- Kill depending vines immediately to skip the vines decay delay
local function clean_vines(spos)
local maybe_vine = minetest.get_node(spos)
if maybe_vine.name == "mcl_core:vine" and (not mcl_core.check_vines_supported(spos, maybe_vine)) then
minetest.remove_node(spos)
vinedecay_particles(spos, maybe_vine)
minetest.check_for_falling(spos)
end
end
clean_vines(vector_offset(pos, 0, 0, -1))
clean_vines(vector_offset(pos, 0, 0, 1))
clean_vines(vector_offset(pos, -1, 0, 0))
clean_vines(vector_offset(pos, 1, 0, 0))
clean_vines(vector_offset(pos, 0, -1, 0))
end
})
-- Remove vines which are not supported by anything, similar to leaf decay.
--[[ TODO: Vines are supposed to die immediately when they supporting block is destroyed.
But doing this in Minetest would be too complicated / hacky. This vines decay is a simple
way to make sure that all floating vines are destroyed eventually. ]]
minetest.register_abm({
label = "Vines decay",
nodenames = {"mcl_core:vine"},
neighbors = {"air"},
-- A low interval and a high inverse chance spreads the load
interval = 4,
chance = 8,
action = function(pos, node)
if not mcl_core.check_vines_supported(pos, node) then
minetest.remove_node(pos)
vinedecay_particles(pos, node)
minetest.check_for_falling(pos)
end
end
})
-- Melt snow
minetest.register_abm({
label = "Top snow and ice melting",
nodenames = {"mcl_core:snow", "mcl_core:ice"},
interval = 16,
chance = 8,
action = function(pos, node)
if minetest.get_node_light(pos, 0) >= 12 then
if node.name == "mcl_core:ice" then
mcl_core.melt_ice(pos)
else
minetest.remove_node(pos)
end
end
end
})
-- Freeze water
minetest.register_abm({
label = "Freeze water in cold areas",
nodenames = {"mcl_core:water_source", "mclx_core:river_water_source"},
interval = 32,
chance = 8,
action = function(pos, node)
if mcl_weather.has_snow(pos)
and minetest.get_natural_light(vector_offset(pos, 0, 1, 0), 0.5) == minetest.LIGHT_MAX + 1
and minetest.get_artificial_light(minetest.get_node(pos).param1) < 10 then
node.name = "mcl_core:ice"
minetest.swap_node(pos, node)
end
end
})
--[[ Call this for vines nodes only.
Given the pos and node of a vines node, this returns true if the vines are supported
and false if the vines are currently floating.
Vines are considered “supported” if they face a walkable+solid block or “hang” from a vines node above. ]]
function mcl_core.check_vines_supported(pos, node)
local dir = minetest.wallmounted_to_dir(node.param2)
local node_neighbor = minetest.get_node(vector_add(pos, dir))
-- Check if vines are attached to a solid block, assume "ignore" is good.
if node_neighbor.name == "ignore" or mcl_core.supports_vines(node_neighbor.name) then return true end
if dir.y == 0 then
-- Vines are not attached, now we check if the vines are “hanging” below another vines block
-- of equal orientation.
local node2 = minetest.get_node(vector_offset(pos, 0, 1, 0))
if node2.name == "ignore" or (node2.name == "mcl_core:vine" and node2.param2 == node.param2) then
return true
end
end
return false
end
-- Melt ice at pos. mcl_core:ice MUST be at pos if you call this!
function mcl_core.melt_ice(pos)
-- Create a water source if ice is destroyed and there was something below it
local below = vector_offset(pos, 0, -1, 0)
local dim = mcl_worlds.pos_to_dimension(below)
local belownode = minetest.get_node(below)
if dim == "nether" or belownode.name == "air" or belownode.name == "ignore" or belownode.name == "mcl_core:void" then
minetest.remove_node(pos)
else
minetest.set_node(pos, {name="mcl_core:water_source"})
end
minetest.check_single_for_falling(vector_offset(pos, -1, 0, 0))
minetest.check_single_for_falling(vector_offset(pos, 1, 0, 0))
minetest.check_single_for_falling(vector_offset(pos, 0, -1, 0))
minetest.check_single_for_falling(vector_offset(pos, 0, 1, 0))
minetest.check_single_for_falling(vector_offset(pos, 0, 0, -1))
minetest.check_single_for_falling(vector_offset(pos, 0, 0, 1))
end
---- FUNCTIONS FOR SNOWED NODES ----
-- These are nodes which change their appearence when they are below a snow cover
-- and turn back into “normal” when the snow cover is removed.
-- Registers a snowed variant of a node (e.g. grass block, podzol, mycelium).
-- * itemstring_snowed: Itemstring of the snowed node to add
-- * itemstring_clear: Itemstring of the original “clear” node without snow
-- * tiles: Optional custom tiles
-- * sounds: Optional custom sounds
-- * clear_colorization: Optional. If true, will clear all paramtype2="color" related node def. fields
-- * desc: Item description
--
-- The snowable nodes also MUST have _mcl_snowed defined to contain the name
-- of the snowed node.
function mcl_core.register_snowed_node(itemstring_snowed, itemstring_clear, tiles, sounds, clear_colorization, desc, grass_palette)
local def = table.copy(minetest.registered_nodes[itemstring_clear])
-- Just some group clearing
def.description = desc
def._doc_items_longdesc = nil
def._doc_items_usagehelp = nil
def._doc_items_create_entry = false
def.groups.not_in_creative_inventory = 1
def.groups.grass_palette = grass_palette
if def.groups.grass_block == 1 then
def.groups.grass_block_no_snow = nil
def.groups.grass_block_snow = 1
end
-- Enderman must never take this because this block is supposed to be always buried below snow.
def.groups.enderman_takable = nil
-- Snowed blocks never spread
def.groups.spreading_dirt_type = nil
-- Add the clear node to the item definition for easy lookup
def._mcl_snowless = itemstring_clear
-- Note: _mcl_snowed must be added to the clear node manually!
def.tiles = tiles or {"default_snow.png", "default_dirt.png", {name="mcl_core_grass_side_snowed.png", tileable_vertical=false}}
if clear_colorization then
def.paramtype2 = nil
def.palette = nil
def.palette_index = nil
def.color = nil
def.overlay_tiles = nil
end
def.sounds = sounds or mcl_sounds.node_sound_dirt_defaults({footstep = mcl_sounds.node_sound_snow_defaults().footstep})
def._mcl_silk_touch_drop = {itemstring_clear}
-- Register stuff
minetest.register_node(itemstring_snowed, def)
if def.description and minetest.get_modpath("doc") then
doc.add_entry_alias("nodes", itemstring_clear, "nodes", itemstring_snowed)
end
end
-- Reverts a snowed dirtlike node at pos to its original snow-less form.
-- This function assumes there is no snow cover node above. This function
-- MUST NOT be called if there is a snow cover node above pos.
function mcl_core.clear_snow_dirt(pos, node)
local def = minetest.registered_nodes[node.name]
if def and def._mcl_snowless then
minetest.swap_node(pos, {name = def._mcl_snowless, param2 = node.param2})
end
end
---- [[[[[ Functions for snowable nodes (nodes that can become snowed). ]]]]] ----
-- Always add these for snowable nodes.
-- on_construct
-- Makes constructed snowable node snowed if placed below a snow cover node.
function mcl_core.on_snowable_construct(pos)
local above = minetest.get_node(vector_offset(pos, 0, 1, 0)).name
-- Make snowed if needed
if minetest.get_item_group(above.name, "snow_cover") == 1 then
local node = minetest.get_node(pos)
local def = minetest.registered_nodes[node.name]
if def and def._mcl_snowed then
minetest.swap_node(pos, {name = def._mcl_snowed, param2 = node.param2})
end
end
end
---- [[[[[ Functions for snow cover nodes. ]]]]] ----
-- A snow cover node is a node which turns a snowed dirtlike --
-- node into its snowed form while it is placed above.
-- MCL2's snow cover nodes are Top Snow (mcl_core:snow) and Snow (mcl_core:snowblock).
-- Always add the following functions to snow cover nodes:
-- on_construct
-- Makes snowable node below snowed.
function mcl_core.on_snow_construct(pos)
local below = vector_offset(pos, 0, -1, 0)
local node = minetest.get_node(below)
local def = minetest.registered_nodes[node.name]
if def and def._mcl_snowed then
minetest.swap_node(below, {name = def._mcl_snowed, param2 = node.param2})
end
end
-- after_destruct
-- Clears snowed dirtlike node below.
function mcl_core.after_snow_destruct(pos)
if minetest.get_item_group(minetest.get_node(pos).name, "snow_cover") == 1 then return end
local below = vector_offset(pos, 0, -1, 0)
mcl_core.clear_snow_dirt(below, minetest.get_node(below))
end
-- Obsidian crying
local crobby_particle = {
velocity = vector_zero(),
acceleration = vector_zero(),
texture = "mcl_core_crying_obsidian_tear.png",
collisiondetection = false,
collision_removal = false,
}
minetest.register_abm({
label = "Obsidian cries",
nodenames = {"mcl_core:crying_obsidian"},
interval = 5,
chance = 10,
action = function(pos, node)
minetest.after(0.1 + random() * 1.4, function()
local pt = table.copy(crobby_particle)
pt.size = 1.3 + random() * 1.2
pt.expirationtime = 0.5 + random()
pt.pos = vector_offset(pos, random() - 0.5, -0.51, random() - 0.5)
minetest.add_particle(pt)
minetest.after(pt.expirationtime, function()
pt.acceleration = vector_new(0, -9, 0)
pt.collisiondetection = true
pt.expirationtime = 1.2 + random() * 3.3
minetest.add_particle(pt)
end)
end)
end
})