Repixture/mods/rp_default/functions.lua

603 lines
19 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 water_level = tonumber(minetest.get_mapgen_setting("water_level"))
local S = minetest.get_translator("rp_default")
-- If a sapling fails to grow, check the sapling again after this many seconds
local SAPLING_RECHECK_TIME_MIN = 60
local SAPLING_RECHECK_TIME_MAX = 70
-- Maximum growth height of cactus, normally
local CACTUS_MAX_HEIGHT = 4
-- Maximum growth height of cactus, fertilized
local CACTUS_MAX_HEIGHT_PLUS = 6
-- Maximum growth height of papyrus, normally
local PAPYRUS_MAX_HEIGHT = 3
-- Maximum growth height of papyrus, fertilized
local PAPYRUS_MAX_HEIGHT_PLUS = 4
-- Bonus height for papyrus when growing on swamp dirt
local PAPYRUS_SWAMP_HEIGHT_BONUS = 1
--
-- Functions/ABMs
--
-- Chest naming via signs
function default.write_name(pos, text)
-- TODO: Allow container naming later
--[[
-- Check above, if allowed
if minetest.settings:get_bool("signs_allow_name_above") then
local above = {x = pos.x, y = pos.y + 1, z = pos.z}
local abovedef = nil
if minetest.registered_nodes[minetest.get_node(above).name] then
abovedef = minetest.registered_nodes[minetest.get_node(above).name]
end
if abovedef and abovedef.write_name ~= nil then
abovedef.write_name(above, text)
end
end
-- Then below
local below = {x = pos.x, y = pos.y - 1, z = pos.z}
local belowdef = nil
if minetest.registered_nodes[minetest.get_node(below).name] then
belowdef = minetest.registered_nodes[minetest.get_node(below).name]
end
if belowdef and belowdef.write_name ~= nil then
belowdef.write_name(below, text)
end
]]
end
-- Saplings growing and placing
function default.place_sapling(itemstack, placer, pointed_thing)
-- Boilerplate to handle pointed node handlers
local handled, handled_itemstack = util.on_place_pointed_node_handler(itemstack, placer, pointed_thing)
if handled then
return handled_itemstack
end
-- Find position to place sapling at
local place_in, place_floor = util.pointed_thing_to_place_pos(pointed_thing)
if place_in == nil then
return itemstack
end
local floornode = minetest.get_node(place_floor)
-- Check protection
if minetest.is_protected(place_in, placer:get_player_name()) and
not minetest.check_player_privs(placer, "protection_bypass") then
minetest.record_protection_violation(pos, placer:get_player_name())
return itemstack
end
-- Floor must be soil
if minetest.get_item_group(floornode.name, "soil") == 0 then
return itemstack
end
-- Place sapling
minetest.set_node(place_in, {name = itemstack:get_name()})
-- Reduce item count
if not minetest.is_creative_enabled(placer:get_player_name()) then
itemstack:take_item()
end
return itemstack
end
local sapling_data = {
["rp_default:sapling"] = {
grows_to = "apple",
grow_time_min = 300,
grow_time_max = 480,
},
["rp_default:sapling_oak"] = {
grows_to = "oak",
grow_time_min = 700,
grow_time_max = 960,
},
["rp_default:sapling_birch"] = {
grows_to = "birch",
grow_time_min = 480,
grow_time_max = 780,
},
["rp_default:sapling_dry_bush"] = {
grows_to = "dry_bush",
grow_time_min = 180,
grow_time_max = 400
}
}
local tree_data = {
["apple"] = {
schem = "default_appletree.mts",
offset = vector.new(-2, -1, -2),
space = {
{ vector.new(0,0,0), vector.new(0,2,0) },
{ vector.new(-2,3,-2), vector.new(2,5,2) },
},
},
["oak"] = {
schem = "default_oaktree.mts",
offset = vector.new(-2, -1, -2),
space = {
{ vector.new(0,0,0), vector.new(0,2,0) },
{ vector.new(-1,3,-1), vector.new(1,5,1) },
},
},
["birch"] = {
schem = "default_squaretree.mts",
replacements = {
["rp_default:leaves"] = "rp_default:leaves_birch",
["rp_default:tree"] = "rp_default:tree_birch",
["rp_default:apple"] = "air"
},
offset = vector.new(-1, -1, -1),
space = {
{ vector.new(0,0,0), vector.new(0,1,0) },
{ vector.new(-1,2,-1), vector.new(1,4,1) },
},
},
["dry_bush"] = {
schem = "default_dry_bush.mts",
offset = vector.new(-1, -1, -1),
space = {
{ vector.new(0,0,0), vector.new(0,1,0) },
},
},
}
function default.check_sapling_space(pos, variety)
local tdata = tree_data[variety]
if not tdata then
return false
end
local space = tdata.space
for i=1, #space do
local min, max = space[i][1], space[i][2]
min = vector.add(pos, min)
max = vector.add(pos, max)
-- Every node of the volume needs to be air,
-- so we calculate the volume first
local required_airs = (max.x - min.x + 1) * (max.y - min.y + 1) * (max.z - min.z + 1)
-- If pos is inside the volume, dont count it (cut its the sapling)
if pos.x >= min.x and pos.y >= min.y and pos.z >= min.z and pos.x <= max.x and pos.y <= max.y and pos.z <= max.z then
required_airs = required_airs - 1
end
local _, counts = minetest.find_nodes_in_area(min, max, {"air"}, false)
local counted_airs = counts.air
if counted_airs < required_airs then
return false
end
end
return true
end
-- Start the sapling grow timer of the sapling at pos.
-- Returns true on success or false if it was not a sapling.
function default.begin_growing_sapling(pos)
local node = minetest.get_node(pos)
local sdata = sapling_data[node.name]
if not sdata then
return false
end
local min, max = sdata.grow_time_min, sdata.grow_time_max
minetest.get_node_timer(pos):start(math.random(min, max))
return true
end
-- Grow a sapling at pos
function default.grow_sapling(pos)
local function grow(variety)
local tdata = tree_data[variety]
if not tdata then
minetest.log("error", "[rp_default] Unknown sapling variety in default.grow_sapling!")
return
end
local opos = vector.add(pos, tdata.offset)
local replacements = tdata.replacements or {}
minetest.place_schematic(
opos,
minetest.get_modpath("rp_default") .. "/schematics/" .. tdata.schem,
"0", replacements, false)
end
local node = minetest.get_node(pos)
local sdata = sapling_data[node.name]
if not sdata then
return false
end
local variety = sdata.grows_to
local enough_space = default.check_sapling_space(pos, variety)
if not enough_space then
minetest.get_node_timer(pos):start(math.random(SAPLING_RECHECK_TIME_MIN, SAPLING_RECHECK_TIME_MAX))
return false
end
minetest.remove_node(pos)
minetest.after(0, grow, variety)
minetest.log("action", "[rp_default] Sapling of type '" .. variety .. "' grows at " ..
minetest.pos_to_string(pos))
return true
end
-- Make preexisting trees restart the growing process
minetest.register_lbm(
{
label = "Grow legacy trees",
name = "rp_default:grow_legacy_trees",
nodenames = {"rp_default:sapling", "rp_default:sapling_oak", "rp_default:sapling_birch"},
action = function(pos, node)
default.begin_growing_sapling(pos)
end
}
)
-- Update nodes after the rename orgy after 1.5.3
minetest.register_lbm(
{
label = "Update signs",
name = "rp_default:update_signs",
nodenames = {"rp_default:sign"},
action = function(pos, node)
local meta = minetest.get_meta(pos)
local text = meta:get_string("text")
meta:set_string("infotext", S('"@1"', text))
end
}
)
minetest.register_lbm(
{
label = "Update bookshelves",
name = "rp_default:update_bookshelves",
nodenames = {"rp_default:bookshelf"},
action = function(pos, node)
local def = minetest.registered_nodes[node.name]
def.on_construct(pos)
end
}
)
minetest.register_lbm(
{
label = "Update chests",
name = "rp_default:update_chests",
nodenames = {"rp_default:chest"},
action = function(pos, node)
local def = minetest.registered_nodes[node.name]
def.on_construct(pos)
end
}
)
-- Vertical plants
-- Leaf decay
default.leafdecay_trunk_cache = {}
default.leafdecay_enable_cache = true
-- Spread the load of finding trunks
default.leafdecay_trunk_find_allow_accumulator = 0
minetest.register_globalstep(function(dtime)
local finds_per_second = 5000
default.leafdecay_trunk_find_allow_accumulator =
math.floor(dtime * finds_per_second)
end)
default.after_place_leaves = function(pos, placer, itemstack, pointed_thing)
local node = minetest.get_node(pos)
node.param2 = 1
minetest.set_node(pos, node)
end
minetest.register_abm( -- leaf decay
{
label = "Leaf decay",
nodenames = {"group:leafdecay"},
neighbors = {"air", "group:liquid"},
-- A low interval and a high inverse chance spreads the load
interval = 2,
chance = 3,
action = function(p0, node, _, _)
local do_preserve = false
local d = minetest.registered_nodes[node.name].groups.leafdecay
if not d or d == 0 then
return
end
local n0 = minetest.get_node(p0)
if n0.param2 ~= 0 then
return
end
local p0_hash = nil
if default.leafdecay_enable_cache then
p0_hash = minetest.hash_node_position(p0)
local trunkp = default.leafdecay_trunk_cache[p0_hash]
if trunkp then
local n = minetest.get_node(trunkp)
local reg = minetest.registered_nodes[n.name]
-- Assume ignore is a trunk, to make the thing work at the border of the active area
if n.name == "ignore" or (reg and reg.groups.tree and reg.groups.tree ~= 0) then
return
end
-- Cache is invalid
table.remove(default.leafdecay_trunk_cache, p0_hash)
end
end
if default.leafdecay_trunk_find_allow_accumulator <= 0 then
return
end
default.leafdecay_trunk_find_allow_accumulator =
default.leafdecay_trunk_find_allow_accumulator - 1
-- Assume ignore is a trunk, to make the thing work at the border of the active area
local p1 = minetest.find_node_near(p0, d, {"ignore", "group:tree"})
if p1 then
do_preserve = true
if default.leafdecay_enable_cache then
-- Cache the trunk
default.leafdecay_trunk_cache[p0_hash] = p1
end
end
if not do_preserve then
-- Drop stuff other than the node itself
local itemstacks = minetest.get_node_drops(n0.name)
for _, itemname in ipairs(itemstacks) do
if minetest.get_item_group(n0.name, "leafdecay_drop") ~= 0 or itemname ~= n0.name then
local p_drop = {
x = p0.x - 0.5 + math.random(),
y = p0.y - 0.5 + math.random(),
z = p0.z - 0.5 + math.random(),
}
minetest.add_item(p_drop, itemname)
end
end
-- Remove node
minetest.remove_node(p0)
end
end
})
local is_dry_biome = function(biomename)
return biomename == "Charparral" or biomename == "Savanna" or biomename == "Savanna Ocean" or biomename == "Desert" or biomename == "Wasteland"
end
minetest.register_abm( -- dirt and grass footsteps becomes dirt with grass if uncovered
{
label = "Grow grass on dirt",
nodenames = {"rp_default:dirt", "rp_default:dirt_with_grass_footsteps", "rp_default:swamp_dirt"},
interval = 2,
chance = 40,
action = function(pos, node)
local above = {x=pos.x, y=pos.y+1, z=pos.z}
local name = minetest.get_node(above).name
local partialblock = minetest.get_item_group(name, "path") ~= 0 or minetest.get_item_group(name, "slab") ~= 0 or minetest.get_item_group(name, "stair") ~= 0
local nodedef = minetest.registered_nodes[name]
if nodedef and (not partialblock) and (nodedef.sunlight_propagates or nodedef.paramtype == "light") and nodedef.liquidtype == "none" and (minetest.get_node_light(above) or 0) >= 8 then
local biomedata = minetest.get_biome_data(pos)
local biomename = minetest.get_biome_name(biomedata.biome)
if node.name == "rp_default:swamp_dirt" then
if biomename == "Swamp" then
minetest.set_node(pos, {name = "rp_default:dirt_with_swamp_grass"})
end
else
if is_dry_biome(biomename) then
minetest.set_node(pos, {name = "rp_default:dirt_with_dry_grass"})
else
minetest.set_node(pos, {name = "rp_default:dirt_with_grass"})
end
end
end
end
})
minetest.register_abm( -- dirt with grass becomes dirt if covered
{
label = "Remove grass on covered dirt",
nodenames = {"group:grass_cover"},
interval = 2,
chance = 10,
action = function(pos, node)
local above = {x=pos.x, y=pos.y+1, z=pos.z}
local name = minetest.get_node(above).name
local partialblock = minetest.get_item_group(name, "path") ~= 0 or minetest.get_item_group(name, "slab") ~= 0 or minetest.get_item_group(name, "stair") ~= 0
local nodedef = minetest.registered_nodes[name]
if nodedef and (name ~= "ignore" and (partialblock) or (not ((nodedef.sunlight_propagates or nodedef.paramtype == "light") and nodedef.liquidtype == "none"))) then
if node.name == "rp_default:dirt_with_swamp_grass" then
minetest.set_node(pos, {name = "rp_default:swamp_dirt"})
else
minetest.set_node(pos, {name = "rp_default:dirt"})
end
end
end
})
minetest.register_abm({
label = "Grass expansion",
nodenames = {"group:grass"},
neighbors = {"group:grass_cover"},
interval = 20,
chance = 160,
action = function(pos, node)
pos.y = pos.y - 1
local under = minetest.get_node(pos)
pos.y = pos.y + 1
local required_under
if minetest.get_item_group(node.name, "normal_grass") ~= 0 then
required_under = "rp_default:dirt_with_grass"
elseif minetest.get_item_group(node.name, "dry_grass") ~= 0 then
required_under = "rp_default:dirt_with_dry_grass"
elseif minetest.get_item_group(node.name, "swamp_grass") ~= 0 then
required_under = "rp_default:dirt_with_swamp_grass"
else
return
end
if under.name ~= required_under then
return
end
-- Lower chance to spread dry grass
if node.name == "rp_default:dry_grass" and math.random(1,2) == 1 then
return
end
local pos0 = vector.subtract(pos, 4)
local pos1 = vector.add(pos, 4)
-- Testing shows that a threshold of 3 results in an appropriate maximum
-- density of approximately 7 nodes per 9x9 area.
if #minetest.find_nodes_in_area(pos0, pos1, {"group:grass", "rp_default:fern"}) > 3 then
return
end
local soils = minetest.find_nodes_in_area_under_air( pos0, pos1, "group:grass_cover")
local num_soils = #soils
if num_soils >= 1 then
for si = 1, math.min(3, num_soils) do
local soil = soils[math.random(num_soils)]
local soil_above = {x = soil.x, y = soil.y + 1, z = soil.z}
minetest.set_node(soil_above, {name = node.name})
end
end
end
})
minetest.register_abm({
label = "Growing clams",
nodenames = {"rp_default:sand", "rp_default:gravel"},
neighbors = {"rp_default:water_source"},
interval = 20,
chance = 160,
action = function(pos, node)
if pos.y ~= water_level then
return
end
local pos0 = vector.add(pos, {x=-5, y=0, z=-5})
local pos1 = vector.add(pos, {x=5, y=2, z=5})
if #minetest.find_nodes_in_area(pos0, pos1, "rp_default:clam") >= 1 then
return
end
pos0 = vector.add(pos, {x=-2, y=0, z=-2})
pos1 = vector.add(pos, {x=2, y=0, z=2})
local soils = minetest.find_nodes_in_area_under_air( pos0, pos1, {"rp_default:sand", "rp_default:gravel"})
local num_soils = #soils
if num_soils >= 1 then
for si = 1, math.min(3, num_soils) do
local soil = soils[math.random(num_soils)]
local soil_above = {x = soil.x, y = soil.y + 1, z = soil.z}
minetest.set_node(soil_above, {name = "rp_default:clam"})
end
end
end
})
minetest.register_abm( -- cactus grows
{
label = "Growing cacti",
nodenames = {"rp_default:cactus"},
neighbors = {"group:sand"},
interval = 20,
chance = 10,
action = function(pos, node)
pos.y = pos.y-1
local name = minetest.get_node(pos).name
if minetest.get_item_group(name, "sand") ~= 0 then
local fertilized = minetest.get_item_group(name, "plantable_fertilizer") == 1
pos.y = pos.y+1
local height = 0
local maxh
-- Determine maximum height. Bonus height on fertilized node
if fertilized then
maxh = CACTUS_MAX_HEIGHT_PLUS
else
maxh = CACTUS_MAX_HEIGHT
end
while minetest.get_node(pos).name == "rp_default:cactus" and height < maxh do
height = height+1
pos.y = pos.y+1
end
if height < maxh then
if minetest.get_node(pos).name == "air" then
minetest.set_node(pos, {name="rp_default:cactus"})
end
end
end
end,
})
minetest.register_abm( -- papyrus grows
{
label = "Growing papyrus",
nodenames = {"rp_default:papyrus"},
neighbors = {"group:plantable_sandy", "group:plantable_soil"},
interval = 20,
chance = 10,
action = function(pos, node)
-- Papyrus grows upwards, up to a limit.
-- The maximum height is on fertilized swamp dirt.
pos.y = pos.y-1
local name = minetest.get_node(pos).name
if minetest.find_node_near(pos, 3, {"group:water"}) == nil then
return
end
pos.y = pos.y+1
local height = 0
local fertilized = minetest.get_item_group(name, "plantable_fertilizer") == 1
local maxh
-- Maximum height is higher on fertilized
if fertilized then
maxh = PAPYRUS_MAX_HEIGHT_PLUS
else
maxh = PAPYRUS_MAX_HEIGHT
end
local is_swampy = minetest.get_item_group(name, "swamp_dirt") == 1
-- Bonus max. height on swamp dirt
if is_swampy then
maxh = maxh + PAPYRUS_SWAMP_HEIGHT_BONUS
end
while minetest.get_node(pos).name == "rp_default:papyrus" and height < maxh do
height = height+1
pos.y = pos.y+1
end
if height < maxh then
if minetest.get_node(pos).name == "air" then
minetest.set_node(pos, {name="rp_default:papyrus"})
end
end
end,
})
minetest.register_abm( -- weak torchs burn out and die after ~3 minutes
{
label = "Burning out weak torches",
nodenames = {"rp_default:torch_weak", "rp_default:torch_weak_wall"},
interval = 3,
chance = 60,
action = function(pos, node)
minetest.set_node(pos, {name = "rp_default:torch_dead", param = node.param, param2 = node.param2})
end
})