Add mcl_util.trace_node(), rewrite bamboo growth code to fix bone meal growth

This commit is contained in:
teknomunk 2024-08-22 06:58:39 -05:00
parent 4eda77acd1
commit 66b5a369f1
2 changed files with 84 additions and 150 deletions

@ -1131,3 +1131,25 @@ if not vector.in_area then
(pos.z >= min.z) and (pos.z <= max.z)
end
end
-- Traces along a line of nodes vertically to find the next possition that isn't an allowed node
---@param pos The position to start tracing from
---@param dir The direction to trace in (1 is up, -1 is down)
---@param allowed_nodes A table of node names to trace along
---@param limit The maximum number of steps to make. Defaults to 16 if nil or missing
---@return Three return values:
--- the position of the next node that isn't allowed or nil if no such node was found,
--- the distance from the start where that node was found,
--- the node table if a node was found
function mcl_util.trace_nodes(pos, dir, allowed_nodes, limit)
if not dir or dir == 0 or #allowed_nodes == 0 then return nil, 0, nil end
limit = limit or 16
for i = 1,limit do
pos = vector.offset(pos, 0, dir, 0)
local node = minetest.get_node(pos)
if table.indexof(allowed_nodes, node.name) == -1 then return pos, i, node end
end
return nil, limit, nil
end

@ -94,172 +94,84 @@ end
--]]
function mcl_bamboo.grow_bamboo(pos, bonemeal_applied)
local log = mcl_bamboo.mcl_log
local node_above = minetest.get_node(vector.offset(pos, 0, 1, 0))
mcl_bamboo.mcl_log("Grow bamboo called; bonemeal: " .. tostring(bonemeal_applied))
log("Grow bamboo called; bonemeal: " .. tostring(bonemeal_applied))
if not bonemeal_applied and mcl_bamboo.is_bamboo(node_above.name) ~= false then
return false -- short circuit this function if we're trying to grow (std) the bamboo and it's not the top shoot.
end
if minetest.get_node_light(pos) < 8 then
return false
if not bonemeal_applied then
-- Only allow natural growth at the top of the bamboo
if mcl_bamboo.is_bamboo(node_above.name) ~= false then return false end
-- Don't perform natual growth in low light
if minetest.get_node_light(pos) < 8 then return false end
end
-- variables used in more than one spot.
local first_shoot
local chk_pos
-- Determine the location of soil
local soil_pos
local node_name = ""
local dist = 0
local node_below
-- -------------------
soil_pos,a,b = mcl_util.trace_nodes(pos, -1, mcl_bamboo.bamboo_index, BAMBOO_MAX_HEIGHT - 1)
mcl_bamboo.mcl_log("Grow bamboo; checking for soil: ")
-- the soil node below the bamboo.
for py = -1, BAMBOO_SOIL_DIST, -1 do
chk_pos = vector.offset(pos, 0, py, 0)
node_name = minetest.get_node(chk_pos).name
if mcl_bamboo.is_dirt(node_name) then
soil_pos = chk_pos
break
-- No soil found, return false so that bonemeal isn't used
if not soil_pos then return false end
log("Grow bamboo; soil found. ")
-- Find the first bamboo shoot and retrieve data about it
local first_shoot = vector.offset(soil_pos, 0, 1, 0)
local first_shoot_meta = minetest.get_meta(first_shoot)
-- Get or initialize bamboo height
local height = (first_shoot_meta and first_shoot_meta:get_int("height", -1)) or -1
if height == -1 then
height = rand(BAM_MAX_HEIGHT_STPCHK + 1, BAM_MAX_HEIGHT_TOP + 1)
first_shoot_meta:set_int("height", height)
end
if mcl_bamboo.is_bamboo(node_name) == false then
break
end
end
-- requires knowing where the soil node is.
if soil_pos == nil then
return false -- returning false means don't use up the bonemeal.
log("Grow bamboo; height: " .. height)
-- Locate the bamboo tip
local bamboo_tip,actual_height,bamboo_tip_node = mcl_util.trace_nodes(first_shoot, 1, mcl_bamboo.bamboo_index, height - 1)
log("Current height: "..tostring(actual_height))
-- Short circuit growth if the bamboo is already finished growing
if not bamboo_tip or not actual_height or actual_height >= height then
log("Bamboo is already as large as it can grow")
return false
end
mcl_bamboo.mcl_log("Grow bamboo; soil found. ")
local grow_amount = rand(1, GROW_DOUBLE_CHANCE)
grow_amount = rand(1, GROW_DOUBLE_CHANCE)
grow_amount = rand(1, GROW_DOUBLE_CHANCE) -- because yeah, not truly random, or even a good prng.
grow_amount = rand(1, GROW_DOUBLE_CHANCE)
local init_height = rand(BAM_MAX_HEIGHT_STPCHK + 1, BAM_MAX_HEIGHT_TOP + 1)
mcl_bamboo.mcl_log("Grow bamboo; random height: " .. init_height)
-- Now that we are actually going to add nodes, initialize some more information
local first_shoot_node_name = minetest.get_node(first_shoot).name
node_name = ""
-- update: add randomized max height to first node's meta data.
first_shoot = vector.offset(soil_pos, 0, 1, 0)
local meta = minetest.get_meta(first_shoot)
node_below = minetest.get_node(first_shoot).name
mcl_bamboo.mcl_log("Grow bamboo; checking height meta ")
-- check the meta data for the first node, to see how high to make the stalk.
if not meta then
-- if no metadata, set the metadata!!!
meta:set_int("height", init_height)
end
local height = meta:get_int("height", -1)
mcl_bamboo.mcl_log("Grow bamboo; meta-height: " .. height)
if height <= 10 then
height = init_height
meta:set_int("height", init_height)
end
mcl_bamboo.mcl_log("Grow bamboo; height: " .. height)
-- Bonemeal: Grows the bamboo by 1-2 stems. (per the minecraft wiki.)
-- If applying bonemeal, randomly grow two segments instead of one
local grow_amount = 1
if bonemeal_applied then
-- handle applying bonemeal.
for py = 1, BAM_MAX_HEIGHT_TOP do
-- find the top node of bamboo.
chk_pos = vector.offset(pos, 0, py, 0)
node_name = minetest.get_node(chk_pos).name
dist = vector.distance(soil_pos, chk_pos)
if mcl_bamboo.is_bamboo(node_name) == false or node_name == BAMBOO_ENDCAP_NAME then
break
local rng = PcgRandom(minetest.hash_node_position(pos) + minetest.get_us_time())
if rng:next(1, GROW_DOUBLE_CHANGE) == 1 then
grow_amount = 2
end
end
log("Growing up to "..grow_amount.." segments")
mcl_bamboo.mcl_log("Grow bamboo; dist: " .. dist)
if node_name == BAMBOO_ENDCAP_NAME then
-- prevent overgrowth
return false
-- Perform bamboo growth
for i = 1,grow_amount do
-- Check for air to grow into
local bamboo_tip_node = minetest.get_node(bamboo_tip)
if not bamboo_tip_node or bamboo_tip_node.name ~= "air" then
-- Something is blocking growth, stop and signal that use bonemeal has been used if at least on segment has grown
return i ~= 1
end
-- check to see if we have a full stalk of bamboo.
if dist >= height - 1 then
if dist == height - 1 then
-- equals top of the stalk before the cap
if node_name == "air" then
mcl_bamboo.mcl_log("Grow bamboo; Placing endcap")
minetest.set_node(vector.offset(chk_pos, 0, 1, 0), { name = BAMBOO_ENDCAP_NAME })
return true -- returning true means use up the bonemeal.
if actual_height + 1 == height then
-- This is the end cap
minetest.set_node(bamboo_tip, { name = BAMBOO_ENDCAP_NAME })
return true
else
return false
end
else
-- okay, we're higher than the end cap, fail out.
return false -- returning false means don't use up the bonemeal.
end
-- This isn't the end cap, add a bamboo segment
minetest.set_node(bamboo_tip, { name = first_shoot_node_name })
actual_height = actual_height + 1
end
-- and now, the meat of the section... add bamboo to the stalk.
-- at this point, we should be lower than the generated maximum height. ~ about height -2 or lower.
if dist <= height - 2 then
if node_name == "air" then
-- here we can check to see if we can do up to 2 bamboo shoots onto the stalk
mcl_bamboo.mcl_log("Grow bamboo; Placing bamboo.")
minetest.set_node(chk_pos, { name = node_below })
-- handle growing a second node.
if grow_amount == 2 then
chk_pos = vector.offset(chk_pos, 0, 1, 0)
if minetest.get_node(chk_pos).name == "air" then
mcl_bamboo.mcl_log("Grow bamboo; OOOH! It's twofer day!")
minetest.set_node(chk_pos, { name = node_below })
end
end
return true -- exit out with a success. We've added 1-2 nodes, per the wiki.
end
end
bamboo_tip = vector.offset(bamboo_tip, 0, 1, 0)
end
-- Non-Bonemeal growth.
for py = 1, BAM_MAX_HEIGHT_TOP do
-- Find the topmost node above the stalk, and check it for "air"
chk_pos = vector.offset(pos, 0, py, 0)
node_below = minetest.get_node(pos).name
node_name = minetest.get_node(chk_pos).name
dist = vector.distance(soil_pos, chk_pos)
if node_name ~= "air" and mcl_bamboo.is_bamboo(node_name) == false then
break
end
-- stop growing check. ie, handle endcap placement.
if dist >= height - 1 then
local above_node_name = minetest.get_node(vector.offset(chk_pos, 0, 1, 0)).name
if node_name == "air" and above_node_name == "air" then
if height - 1 == dist then
mcl_bamboo.mcl_log("Grow bamboo; Placing endcap")
minetest.set_node(chk_pos, { name = BAMBOO_ENDCAP_NAME })
end
end
break
end
-- handle regular node placement.
-- find the air node above the top shoot. place a node. And then, if short enough,
-- check for second node placement.
if node_name == "air" then
mcl_bamboo.mcl_log("Grow bamboo; dist: " .. dist)
mcl_bamboo.mcl_log("Grow bamboo; Placing bamboo.")
minetest.set_node(chk_pos, { name = node_below })
-- handle growing a second node. (1 in 32 chance.)
if grow_amount == 2 and dist <= height - 2 then
chk_pos = vector.offset(chk_pos, 0, 1, 0)
if minetest.get_node(chk_pos).name == "air" then
mcl_bamboo.mcl_log("Grow bamboo; OOOH! It's twofer day!")
minetest.set_node(chk_pos, { name = node_below })
end
end
break
end
end
return true
end
-- Add Groups function, courtesy of Warr1024.