mirror of
https://codeberg.org/tenplus1/farming.git
synced 2025-06-29 23:01:00 +02:00
NEW growing routine added
This commit is contained in:
385
init.lua
385
init.lua
@ -1,6 +1,7 @@
|
||||
--[[
|
||||
Minetest Farming Redo Mod 1.14 (11th May 2015)
|
||||
Minetest Farming Redo Mod 1.20 (20th May 2015)
|
||||
by TenPlus1
|
||||
NEW growing routine by prestidigitator
|
||||
]]
|
||||
|
||||
farming = {}
|
||||
@ -8,6 +9,39 @@ farming.mod = "redo"
|
||||
farming.path = minetest.get_modpath("farming")
|
||||
farming.hoe_on_use = default.hoe_on_use
|
||||
|
||||
farming.DEBUG = false
|
||||
-- farming.DEBUG = {} -- Uncomment to turn on profiling code/functions
|
||||
|
||||
local DEBUG_abm_runs = 0
|
||||
local DEBUG_abm_time = 0
|
||||
local DEBUG_timer_runs = 0
|
||||
local DEBUG_timer_time = 0
|
||||
if farming.DEBUG then
|
||||
function farming.DEBUG.reset_times()
|
||||
DEBUG_abm_runs = 0
|
||||
DEBUG_abm_time = 0
|
||||
DEBUG_timer_runs = 0
|
||||
DEBUG_timer_time = 0
|
||||
end
|
||||
|
||||
function farming.DEBUG.report_times()
|
||||
local abm_n = DEBUG_abm_runs
|
||||
local abm_dt = DEBUG_abm_time
|
||||
local abm_avg = (abm_n > 0 and abm_dt / abm_n) or 0
|
||||
local timer_n = DEBUG_timer_runs
|
||||
local timer_dt = DEBUG_timer_time
|
||||
local timer_avg = (timer_n > 0 and timer_dt / timer_n) or 0
|
||||
local dt = abm_dt + timer_dt
|
||||
print("ABM ran for "..abm_dt.."µs over "..abm_n.." runs: "..
|
||||
abm_avg.."µs/run")
|
||||
print("Timer ran for "..timer_dt.."µs over "..timer_n.." runs: "..
|
||||
timer_avg.."µs/run")
|
||||
print("Total farming time: "..dt.."µs")
|
||||
end
|
||||
else
|
||||
end
|
||||
|
||||
local statistics = dofile(farming.path.."/statistics.lua")
|
||||
dofile(farming.path.."/soil.lua")
|
||||
dofile(farming.path.."/hoes.lua")
|
||||
dofile(farming.path.."/grass.lua")
|
||||
@ -31,6 +65,319 @@ dofile(farming.path.."/donut.lua")
|
||||
dofile(farming.path.."/mapgen.lua")
|
||||
dofile(farming.path.."/compatibility.lua") -- Farming Plus compatibility
|
||||
|
||||
-- Utility Functions
|
||||
|
||||
local time_speed = tonumber(minetest.setting_get("time_speed")) or 72
|
||||
local SECS_PER_CYCLE = (time_speed > 0 and 24 * 60 * 60/time_speed) or nil
|
||||
|
||||
local function clamp(x, min, max)
|
||||
return (x < min and min) or (x > max and max) or x
|
||||
end
|
||||
|
||||
local function in_range(x, min, max)
|
||||
return min <= x and x <= max
|
||||
end
|
||||
|
||||
--- Tests the amount of day or night time between two times.
|
||||
--
|
||||
-- @param t_game
|
||||
-- The current time, as reported by mintest.get_gametime().
|
||||
-- @param t_day
|
||||
-- The current time, as reported by mintest.get_timeofday().
|
||||
-- @param dt
|
||||
-- The amount of elapsed time.
|
||||
-- @param count_day
|
||||
-- If true, count elapsed day time. Otherwise, count elapsed night time.
|
||||
-- @return
|
||||
-- The amount of day or night time that has elapsed.
|
||||
--
|
||||
local function day_or_night_time(t_game, t_day, dt, count_day)
|
||||
local t1_day = t_day - dt / SECS_PER_CYCLE
|
||||
|
||||
local t1_c, t2_c -- t1_c < t2_c and t2_c always in [0, 1)
|
||||
if count_day then
|
||||
if t_day < 0.25 then
|
||||
t1_c = t1_day + 0.75 -- Relative to sunup, yesterday
|
||||
t2_c = t_day + 0.75
|
||||
else
|
||||
t1_c = t1_day - 0.25 -- Relative to sunup, today
|
||||
t2_c = t_day - 0.25
|
||||
end
|
||||
else
|
||||
if t_day < 0.75 then
|
||||
t1_c = t1_day + 0.25 -- Relative to sundown, yesterday
|
||||
t2_c = t_day + 0.25
|
||||
else
|
||||
t1_c = t1_day - 0.75 -- Relative to sundown, today
|
||||
t2_c = t_day - 0.75
|
||||
end
|
||||
end
|
||||
|
||||
local dt_c = clamp(t2_c, 0, 0.5) - clamp(t1_c, 0, 0.5) -- this cycle
|
||||
if t1_c < -0.5 then
|
||||
local nc = math.floor(-t1_c)
|
||||
t1_c = t1_c + nc
|
||||
dt_c = dt_c + 0.5 * nc + clamp(-t1_c - 0.5, 0, 0.5)
|
||||
end
|
||||
|
||||
return dt_c * SECS_PER_CYCLE
|
||||
end
|
||||
|
||||
--- Tests the amount of elapsed day time.
|
||||
--
|
||||
-- @param dt
|
||||
-- The amount of elapsed time.
|
||||
-- @return
|
||||
-- The amount of day time that has elapsed.
|
||||
--
|
||||
local function day_time(dt)
|
||||
return day_or_night_time(minetest.get_gametime(), minetest.get_timeofday(), dt, true)
|
||||
end
|
||||
|
||||
--- Tests the amount of elapsed night time.
|
||||
--
|
||||
-- @param dt
|
||||
-- The amount of elapsed time.
|
||||
-- @return
|
||||
-- The amount of night time that has elapsed.
|
||||
--
|
||||
local function night_time(time_game, time_day, dt, count_day)
|
||||
return day_or_night_time(minetest.get_gametime(), minetest.get_timeofday(), dt, false)
|
||||
end
|
||||
|
||||
|
||||
-- Growth Logic
|
||||
|
||||
local STAGE_LENGTH_AVG = 160.0
|
||||
local STAGE_LENGTH_DEV = STAGE_LENGTH_AVG / 6
|
||||
local MIN_LIGHT = 13
|
||||
local MAX_LIGHT = 1000
|
||||
|
||||
--- Determines plant name and stage from node.
|
||||
--
|
||||
-- Separates node name on the last underscore (_).
|
||||
--
|
||||
-- @param node
|
||||
-- Node or position table, or node name.
|
||||
-- @return
|
||||
-- List (plant_name, stage), or nothing (nil) if node isn't loaded
|
||||
--
|
||||
local function plant_name_stage(node)
|
||||
local name
|
||||
if type(node) == 'table' then
|
||||
if node.name then
|
||||
name = node.name
|
||||
elseif node.x and node.y and node.z then
|
||||
node = minetest.get_node_or_nil(node)
|
||||
name = node and node.name
|
||||
end
|
||||
else
|
||||
name = tostring(node)
|
||||
end
|
||||
if not name or name == "ignore" then return nil end
|
||||
|
||||
local sep_pos = name:find("_[^_]+$")
|
||||
if sep_pos and sep_pos > 1 then
|
||||
local stage = tonumber(name:sub(sep_pos + 1))
|
||||
if stage and stage >= 0 then
|
||||
return name:sub(1, sep_pos - 1), stage
|
||||
end
|
||||
end
|
||||
|
||||
return name, 0
|
||||
end
|
||||
|
||||
--- Map from node name to
|
||||
-- { plant_name = ..., name = ..., stage = n, stages_left = { node_name, ... } }
|
||||
local plant_stages = {}
|
||||
farming.plant_stages = plant_stages
|
||||
|
||||
--- Registers the stages of growth of a (possible plant) node.
|
||||
--
|
||||
-- @param node
|
||||
-- Node or position table, or node name.
|
||||
-- @return
|
||||
-- The (possibly zero) number of stages of growth the plant will go through
|
||||
-- before being fully grown, or nil if not a plant.
|
||||
--
|
||||
local register_plant_node
|
||||
-- Recursive helper
|
||||
local function reg_plant_stages(plant_name, stage, force_last)
|
||||
local node_name = plant_name and plant_name .. "_" .. stage
|
||||
local node_def = node_name and minetest.registered_nodes[node_name]
|
||||
if not node_def then return nil end
|
||||
|
||||
local stages = plant_stages[node_name]
|
||||
if stages then return stages end
|
||||
|
||||
if minetest.get_item_group(node_name, "growing") > 0 then
|
||||
local ns = reg_plant_stages(plant_name, stage + 1, true)
|
||||
|
||||
local stages_left = (ns and { ns.name, unpack(ns.stages_left) }) or {}
|
||||
stages = { plant_name = plant_name, name = node_name, stage = stage, stages_left = stages_left }
|
||||
|
||||
if #stages_left > 0 then
|
||||
local old_constr = node_def.on_construct
|
||||
local old_destr = node_def.on_destruct
|
||||
minetest.override_item(node_name,
|
||||
{
|
||||
on_construct = function(pos)
|
||||
if old_constr then old_constr(pos) end
|
||||
farming.handle_growth(pos)
|
||||
end,
|
||||
|
||||
on_destruct = function(pos)
|
||||
minetest.get_node_timer(pos):stop()
|
||||
if old_destr then old_destr(pos) end
|
||||
end,
|
||||
|
||||
on_timer = function(pos, elapsed)
|
||||
return farming.plant_growth_timer(pos, elapsed, node_name)
|
||||
end,
|
||||
})
|
||||
end
|
||||
elseif force_last then
|
||||
stages = { plant_name = plant_name, name = node_name, stage = stage, stages_left = {} }
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
plant_stages[node_name] = stages
|
||||
return stages
|
||||
end
|
||||
|
||||
register_plant_node = function(node)
|
||||
local plant_name, stage = plant_name_stage(node)
|
||||
if plant_name then
|
||||
local stages = reg_plant_stages(plant_name, stage, false)
|
||||
return stages and #stages.stages_left
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function set_growing(pos, stages_left)
|
||||
if not stages_left then return end
|
||||
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
if stages_left > 0 then
|
||||
if not timer:is_started() then
|
||||
local stage_length = statistics.normal(STAGE_LENGTH_AVG, STAGE_LENGTH_DEV)
|
||||
stage_length = clamp(stage_length, 0.5 * STAGE_LENGTH_AVG, 3.0 * STAGE_LENGTH_AVG)
|
||||
timer:set(stage_length, -0.5 * math.random() * STAGE_LENGTH_AVG)
|
||||
end
|
||||
elseif timer:is_started() then
|
||||
timer:stop()
|
||||
end
|
||||
end
|
||||
|
||||
--- Detects a plant type node at the given position, starting or stopping the plant growth timer as appopriate
|
||||
--
|
||||
-- @param pos
|
||||
-- The node's position.
|
||||
-- @param node
|
||||
-- The cached node table if available, or nil.
|
||||
--
|
||||
function farming.handle_growth(pos, node)
|
||||
if not pos then return end
|
||||
local stages_left = register_plant_node(node or pos)
|
||||
if stages_left then set_growing(pos, stages_left) end
|
||||
end
|
||||
|
||||
minetest.after(0,
|
||||
function()
|
||||
for _, node_def in pairs(minetest.registered_nodes) do
|
||||
register_plant_node(node_def)
|
||||
end
|
||||
end)
|
||||
|
||||
local abm_func = farming.handle_growth
|
||||
if farming.DEBUG then
|
||||
local normal_abm_func = abm_func
|
||||
abm_func = function(...)
|
||||
local t0 = minetest.get_us_time()
|
||||
local r = { normal_abm_func(...) }
|
||||
local t1 = minetest.get_us_time()
|
||||
DEBUG_abm_runs = DEBUG_abm_runs + 1
|
||||
DEBUG_abm_time = DEBUG_abm_time + (t1 - t0)
|
||||
return unpack(r)
|
||||
end
|
||||
end
|
||||
|
||||
-- Just in case a growing type or added node is missed (also catches existing
|
||||
-- nodes added to map before timers were incorporated).
|
||||
minetest.register_abm({
|
||||
nodenames = { "group:growing" },
|
||||
interval = 300,
|
||||
chance = 1,
|
||||
action = abm_func})
|
||||
|
||||
--- Plant timer function.
|
||||
--
|
||||
-- Grows plants under the right conditions.
|
||||
--
|
||||
function farming.plant_growth_timer(pos, elapsed, node_name)
|
||||
local stages = plant_stages[node_name]
|
||||
if not stages then return false end
|
||||
|
||||
local max_growth = #stages.stages_left
|
||||
if max_growth <= 0 then return false end
|
||||
|
||||
if stages.plant_name == "farming:cocoa" then
|
||||
if not minetest.find_node_near(pos, 1, { "default:jungletree", "moretrees:jungletree_leaves_green" }) then
|
||||
return true
|
||||
end
|
||||
else
|
||||
local under = minetest.get_node_or_nil({ x = pos.x, y = pos.y - 1, z = pos.z })
|
||||
if not under or under.name ~= "farming:soil_wet" then return true end
|
||||
end
|
||||
|
||||
local growth
|
||||
|
||||
local light_pos = { x = pos.x, y = pos.y + 1, z = pos.z }
|
||||
local lambda = elapsed / STAGE_LENGTH_AVG
|
||||
if lambda < 0.1 then return true end
|
||||
if max_growth == 1 or lambda < 2.0 then
|
||||
local light = minetest.get_node_light(light_pos)
|
||||
if not in_range(light, MIN_LIGHT, MAX_LIGHT) then return true end
|
||||
growth = 1
|
||||
else
|
||||
local night_light = minetest.get_node_light(light_pos, 0)
|
||||
local day_light = minetest.get_node_light(light_pos, 0.5)
|
||||
local night_growth = in_range(night_light, MIN_LIGHT, MAX_LIGHT)
|
||||
local day_growth = in_range(day_light, MIN_LIGHT, MAX_LIGHT)
|
||||
|
||||
if not night_growth then
|
||||
if not day_growth then return true end
|
||||
lambda = day_time(elapsed) / STAGE_LENGTH_AVG
|
||||
elseif not day_growth then
|
||||
lambda = night_time(elapsed) / STAGE_LENGTH_AVG
|
||||
end
|
||||
|
||||
growth = statistics.poisson(lambda, max_growth)
|
||||
if growth < 1 then return true end
|
||||
end
|
||||
|
||||
minetest.swap_node(pos, { name = stages.stages_left[growth] })
|
||||
|
||||
return growth ~= max_growth
|
||||
end
|
||||
|
||||
if farming.DEBUG then
|
||||
local timer_func = farming.plant_growth_timer;
|
||||
farming.plant_growth_timer = function(pos, elapsed, node_name)
|
||||
local t0 = minetest.get_us_time()
|
||||
|
||||
local r = { timer_func(pos, elapsed, node_name) }
|
||||
|
||||
local t1 = minetest.get_us_time()
|
||||
DEBUG_timer_runs = DEBUG_timer_runs + 1
|
||||
DEBUG_timer_time = DEBUG_timer_time + (t1 - t0)
|
||||
return unpack(r)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Place Seeds on Soil
|
||||
|
||||
function farming.place_seed(itemstack, placer, pointed_thing, plantname)
|
||||
@ -72,38 +419,6 @@ function farming.place_seed(itemstack, placer, pointed_thing, plantname)
|
||||
end
|
||||
end
|
||||
|
||||
-- Single ABM Handles Growing of All Plants
|
||||
|
||||
minetest.register_abm({
|
||||
nodenames = {"group:growing"},
|
||||
neighbors = {"farming:soil_wet", "default:jungletree"},
|
||||
interval = 80,
|
||||
chance = 2,
|
||||
|
||||
action = function(pos, node)
|
||||
|
||||
-- split plant name (e.g. farming:wheat_1)
|
||||
local plant = node.name:split("_")[1].."_"
|
||||
local numb = node.name:split("_")[2]
|
||||
|
||||
-- fully grown ?
|
||||
if not minetest.registered_nodes[plant..(numb + 1)] then return end
|
||||
|
||||
-- cocoa pod on jungle tree ?
|
||||
if plant ~= "farming:cocoa_" then
|
||||
|
||||
-- growing on wet soil ?
|
||||
if minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name ~= "farming:soil_wet" then return end
|
||||
end
|
||||
|
||||
-- enough light ?
|
||||
if minetest.get_node_light(pos) < 13 then return end
|
||||
|
||||
-- grow
|
||||
minetest.set_node(pos, {name=plant..(numb + 1)})
|
||||
|
||||
end
|
||||
})
|
||||
|
||||
-- Function to register plants (for compatibility)
|
||||
|
||||
@ -163,7 +478,8 @@ farming.register_plant = function(name, def)
|
||||
g = {snappy = 3, flammable = 2, plant = 1, not_in_creative_inventory = 1, attached_node = 1}
|
||||
end
|
||||
|
||||
minetest.register_node(mname .. ":" .. pname .. "_" .. i, {
|
||||
local node_name = mname .. ":" .. pname .. "_" .. i
|
||||
minetest.register_node(node_name, {
|
||||
drawtype = "plantlike",
|
||||
waving = 1,
|
||||
tiles = {mname .. "_" .. pname .. "_" .. i .. ".png"},
|
||||
@ -176,6 +492,7 @@ farming.register_plant = function(name, def)
|
||||
groups = g,
|
||||
sounds = default.node_sound_leaves_defaults(),
|
||||
})
|
||||
register_plant_node(node_name)
|
||||
end
|
||||
|
||||
-- Return info
|
||||
|
Reference in New Issue
Block a user