farming/init.lua

667 lines
16 KiB
Lua
Raw Normal View History

2014-11-09 20:06:28 +01:00
--[[
2017-08-14 19:42:03 +02:00
Farming Redo Mod
2014-11-09 20:06:28 +01:00
by TenPlus1
2015-05-20 11:22:04 +02:00
NEW growing routine by prestidigitator
auto-refill by crabman77
2014-11-09 20:06:28 +01:00
]]
farming = {}
farming.mod = "redo"
farming.version = "1.33"
2015-05-11 20:07:21 +02:00
farming.path = minetest.get_modpath("farming")
2015-07-05 11:54:18 +02:00
farming.select = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}
}
2014-11-09 20:06:28 +01:00
2015-05-20 11:22:04 +02:00
2018-01-04 10:33:24 +01:00
local creative_mode_cache = minetest.settings:get_bool("creative_mode")
function farming.is_creative(name)
return creative_mode_cache or minetest.check_player_privs(name, {creative = true})
end
2015-05-20 11:22:04 +02:00
local statistics = dofile(farming.path.."/statistics.lua")
2016-05-30 13:30:00 +02:00
-- Intllib
2017-08-17 17:34:01 +02:00
local S = dofile(farming.path.."/intllib.lua")
2016-05-30 13:30:00 +02:00
farming.intllib = S
2015-05-20 11:22:04 +02:00
-- Utility Function
2017-10-09 12:32:00 +02:00
local time_speed = tonumber(minetest.settings:get("time_speed")) or 72
2016-03-10 18:45:55 +01:00
local SECS_PER_CYCLE = (time_speed > 0 and 24 * 60 * 60 / time_speed) or 0
2015-05-20 11:22:04 +02:00
local function clamp(x, min, max)
return (x < min and min) or (x > max and max) or x
end
-- return amount of day or night that has elapsed
-- dt is time elapsed, count_day if true counts day, otherwise night
local function day_or_night_time(dt, count_day)
2015-05-20 11:22:04 +02:00
local t_day = minetest.get_timeofday()
local t1_day = t_day - dt / SECS_PER_CYCLE
2015-05-20 11:22:04 +02:00
local t1_c, t2_c -- t1_c < t2_c and t2_c always in [0, 1)
2015-05-20 11:22:04 +02:00
if count_day then
2015-05-20 11:22:04 +02:00
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
2015-05-20 11:22:04 +02:00
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
-- Growth Logic
local STAGE_LENGTH_AVG = 160.0
local STAGE_LENGTH_DEV = STAGE_LENGTH_AVG / 6
-- return plant name and stage from node provided
2015-05-20 11:22:04 +02:00
local function plant_name_stage(node)
2015-05-20 11:22:04 +02:00
local name
if type(node) == "table" then
2015-05-20 11:22:04 +02:00
if node.name then
2016-03-10 18:45:55 +01:00
name = node.name
2015-05-20 11:22:04 +02:00
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
2016-03-10 18:45:55 +01:00
if not name or name == "ignore" then
return nil
end
2015-05-20 11:22:04 +02:00
local sep_pos = name:find("_[^_]+$")
2015-05-20 11:22:04 +02:00
if sep_pos and sep_pos > 1 then
2015-05-20 11:22:04 +02:00
local stage = tonumber(name:sub(sep_pos + 1))
2015-05-20 11:22:04 +02:00
if stage and stage >= 0 then
return name:sub(1, sep_pos - 1), stage
end
end
return name, 0
end
2016-03-10 18:45:55 +01:00
-- Map from node name to
-- { plant_name = ..., name = ..., stage = n, stages_left = { node_name, ... } }
2015-05-20 11:22:04 +02:00
local plant_stages = {}
2015-05-20 11:22:04 +02:00
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.
2015-05-20 11:22:04 +02:00
local register_plant_node
2015-05-20 11:22:04 +02:00
-- Recursive helper
local function reg_plant_stages(plant_name, stage, force_last)
2015-05-20 11:22:04 +02:00
local node_name = plant_name and plant_name .. "_" .. stage
local node_def = node_name and minetest.registered_nodes[node_name]
2016-03-10 18:45:55 +01:00
if not node_def then
return nil
end
2015-05-20 11:22:04 +02:00
local stages = plant_stages[node_name]
2016-03-10 18:45:55 +01:00
if stages then
return stages
end
2015-05-20 11:22:04 +02:00
if minetest.get_item_group(node_name, "growing") > 0 then
local ns = reg_plant_stages(plant_name, stage + 1, true)
2015-05-20 11:22:04 +02:00
local stages_left = (ns and { ns.name, unpack(ns.stages_left) }) or {}
2015-07-05 11:54:18 +02:00
stages = {
plant_name = plant_name,
name = node_name,
stage = stage,
stages_left = stages_left
}
2015-05-20 11:22:04 +02:00
if #stages_left > 0 then
2015-05-20 11:22:04 +02:00
local old_constr = node_def.on_construct
local old_destr = node_def.on_destruct
2015-05-20 11:22:04 +02:00
minetest.override_item(node_name,
{
on_construct = function(pos)
2016-03-10 18:45:55 +01:00
if old_constr then
old_constr(pos)
end
2015-05-20 11:22:04 +02:00
farming.handle_growth(pos)
end,
on_destruct = function(pos)
2016-03-10 18:45:55 +01:00
2015-05-20 11:22:04 +02:00
minetest.get_node_timer(pos):stop()
2016-03-10 18:45:55 +01:00
if old_destr then
old_destr(pos)
end
2015-05-20 11:22:04 +02:00
end,
on_timer = function(pos, elapsed)
return farming.plant_growth_timer(pos, elapsed, node_name)
end,
})
end
2015-05-20 11:22:04 +02:00
elseif force_last then
2015-07-05 11:54:18 +02:00
stages = {
plant_name = plant_name,
name = node_name,
stage = stage,
stages_left = {}
}
2015-05-20 11:22:04 +02:00
else
return nil
end
plant_stages[node_name] = stages
2015-05-20 11:22:04 +02:00
return stages
end
2018-03-22 16:57:36 +01:00
local register_plant_node = function(node)
2015-05-20 11:22:04 +02:00
local plant_name, stage = plant_name_stage(node)
2015-05-20 11:22:04 +02:00
if plant_name then
2016-03-10 18:45:55 +01:00
2015-05-20 11:22:04 +02:00
local stages = reg_plant_stages(plant_name, stage, false)
return stages and #stages.stages_left
else
return nil
end
end
2015-05-20 11:22:04 +02:00
local function set_growing(pos, stages_left)
2016-03-10 18:45:55 +01:00
if not stages_left then
return
end
2015-05-20 11:22:04 +02:00
local timer = minetest.get_node_timer(pos)
2015-05-20 11:22:04 +02:00
if stages_left > 0 then
2015-05-20 11:22:04 +02:00
if not timer:is_started() then
2015-05-20 11:22:04 +02:00
local stage_length = statistics.normal(STAGE_LENGTH_AVG, STAGE_LENGTH_DEV)
2015-05-20 11:22:04 +02:00
stage_length = clamp(stage_length, 0.5 * STAGE_LENGTH_AVG, 3.0 * STAGE_LENGTH_AVG)
2015-05-20 11:22:04 +02:00
timer:set(stage_length, -0.5 * math.random() * STAGE_LENGTH_AVG)
end
2015-05-20 11:22:04 +02:00
elseif timer:is_started() then
timer:stop()
end
end
-- detects a crop at given position, starting or stopping growth timer when needed
2015-05-20 11:22:04 +02:00
function farming.handle_growth(pos, node)
2016-03-10 18:45:55 +01:00
if not pos then
return
end
2015-05-20 11:22:04 +02:00
local stages_left = register_plant_node(node or pos)
2016-03-10 18:45:55 +01:00
if stages_left then
set_growing(pos, stages_left)
end
2015-05-20 11:22:04 +02:00
end
2016-03-10 18:45:55 +01:00
minetest.after(0, function()
for _, node_def in ipairs(minetest.registered_nodes) do
2016-03-10 18:45:55 +01:00
register_plant_node(node_def)
end
end)
2015-05-20 11:22:04 +02:00
2015-05-20 11:22:04 +02:00
local abm_func = farming.handle_growth
2015-05-20 11:22:04 +02:00
-- Just in case a growing type or added node is missed (also catches existing
-- nodes added to map before timers were incorporated).
2016-03-10 18:45:55 +01:00
minetest.register_abm({
2015-05-20 11:22:04 +02:00
nodenames = { "group:growing" },
interval = 300,
chance = 1,
action = abm_func
})
2015-05-20 11:22:04 +02:00
-- Plant timer function that grows plants under the right conditions.
2015-05-20 11:22:04 +02:00
function farming.plant_growth_timer(pos, elapsed, node_name)
2015-05-20 11:22:04 +02:00
local stages = plant_stages[node_name]
2016-03-10 18:45:55 +01:00
if not stages then
return false
end
2015-05-20 11:22:04 +02:00
local max_growth = #stages.stages_left
2016-03-10 18:45:55 +01:00
if max_growth <= 0 then
return false
end
2015-05-20 11:22:04 +02:00
if stages.plant_name == "farming:cocoa" then
2017-05-06 11:09:42 +02:00
if not minetest.find_node_near(pos, 1, {"default:jungletree"}) then
2015-05-20 11:22:04 +02:00
return true
end
else
local under = minetest.get_node({ x = pos.x, y = pos.y - 1, z = pos.z })
2016-05-25 21:58:25 +02:00
if minetest.get_item_group(under.name, "soil") < 3 then
2016-03-10 18:45:55 +01:00
return true
end
2015-05-20 11:22:04 +02:00
end
local growth
local light_pos = {x = pos.x, y = pos.y, z = pos.z} -- was y + 1
2015-05-20 11:22:04 +02:00
local lambda = elapsed / STAGE_LENGTH_AVG
2016-03-10 18:45:55 +01:00
if lambda < 0.1 then
return true
end
local MIN_LIGHT = minetest.registered_nodes[node_name].minlight or 13
2017-09-02 20:34:28 +02:00
local MAX_LIGHT = minetest.registered_nodes[node_name].maxlight or 15
--print ("---", MIN_LIGHT, MAX_LIGHT)
2015-05-20 11:22:04 +02:00
if max_growth == 1 or lambda < 2.0 then
2016-03-10 18:45:55 +01:00
local light = (minetest.get_node_light(light_pos) or 0)
2015-06-27 14:10:10 +02:00
--print ("light level:", light)
if light < MIN_LIGHT or light > MAX_LIGHT then
2016-03-10 18:45:55 +01:00
return true
end
2015-05-20 11:22:04 +02:00
growth = 1
else
2016-03-10 18:45:55 +01:00
local night_light = (minetest.get_node_light(light_pos, 0) or 0)
local day_light = (minetest.get_node_light(light_pos, 0.5) or 0)
local night_growth = night_light >= MIN_LIGHT and night_light <= MAX_LIGHT
local day_growth = day_light >= MIN_LIGHT and day_light <= MAX_LIGHT
2015-05-20 11:22:04 +02:00
if not night_growth then
2016-03-10 18:45:55 +01:00
if not day_growth then
return true
end
lambda = day_or_night_time(elapsed, true) / STAGE_LENGTH_AVG
2016-03-10 18:45:55 +01:00
2015-05-20 11:22:04 +02:00
elseif not day_growth then
2016-03-10 18:45:55 +01:00
lambda = day_or_night_time(elapsed, false) / STAGE_LENGTH_AVG
2015-05-20 11:22:04 +02:00
end
growth = statistics.poisson(lambda, max_growth)
2016-03-10 18:45:55 +01:00
if growth < 1 then
return true
end
2015-05-20 11:22:04 +02:00
end
if minetest.registered_nodes[stages.stages_left[growth]] then
local p2 = minetest.registered_nodes[stages.stages_left[growth] ].place_param2 or 1
minetest.swap_node(pos, {name = stages.stages_left[growth], param2 = p2})
else
return true
end
2015-05-20 11:22:04 +02:00
return growth ~= max_growth
end
-- refill placed plant by crabman (26/08/2015) updated by TenPlus1
function farming.refill_plant(player, plantname, index)
local inv = player:get_inventory()
local old_stack = inv:get_stack("main", index)
2016-03-10 18:45:55 +01:00
if old_stack:get_name() ~= "" then
return
end
for i, stack in ipairs(inv:get_list("main")) do
if stack:get_name() == plantname and i ~= index then
inv:set_stack("main", index, stack)
stack:clear()
inv:set_stack("main", i, stack)
return
end
end
2016-03-10 18:45:55 +01:00
end
2014-11-09 20:06:28 +01:00
-- Place Seeds on Soil
2014-11-09 20:06:28 +01:00
function farming.place_seed(itemstack, placer, pointed_thing, plantname)
2014-11-09 20:06:28 +01:00
local pt = pointed_thing
-- check if pointing at a node
2015-08-26 16:05:17 +02:00
if not pt or pt.type ~= "node" then
2014-11-09 20:06:28 +01:00
return
end
local under = minetest.get_node(pt.under)
-- am I right-clicking on something that has a custom on_place set?
-- thanks to Krock for helping with this issue :)
local def = minetest.registered_nodes[under.name]
if def and def.on_rightclick then
return def.on_rightclick(pt.under, under, placer, itemstack)
end
2014-11-09 20:06:28 +01:00
local above = minetest.get_node(pt.above)
-- check if pointing at the top of the node
2015-07-05 11:54:18 +02:00
if pt.above.y ~= pt.under.y + 1 then
2014-11-09 20:06:28 +01:00
return
end
-- return if any of the nodes is not registered
if not minetest.registered_nodes[under.name]
or not minetest.registered_nodes[above.name] then
return
end
-- can I replace above node, and am I pointing at soil
if not minetest.registered_nodes[above.name].buildable_to
2016-04-01 12:02:18 +02:00
or minetest.get_item_group(under.name, "soil") < 2
2015-08-26 16:05:17 +02:00
-- avoid multiple seed placement bug
or minetest.get_item_group(above.name, "plant") ~= 0 then
2014-11-09 20:06:28 +01:00
return
end
2015-08-26 16:05:17 +02:00
-- if not protected then add node and remove 1 item from the itemstack
if not minetest.is_protected(pt.above, placer:get_player_name()) then
local p2 = minetest.registered_nodes[plantname].place_param2 or 1
minetest.set_node(pt.above, {name = plantname, param2 = p2})
2016-04-02 14:42:14 +02:00
minetest.sound_play("default_place_node", {pos = pt.above, gain = 1.0})
2016-04-01 12:02:18 +02:00
2018-02-08 12:27:23 +01:00
if not placer or not farming.is_creative(placer:get_player_name()) then
local name = itemstack:get_name()
itemstack:take_item()
-- check for refill
if itemstack:get_count() == 0 then
minetest.after(0.10,
farming.refill_plant,
placer,
name,
placer:get_wield_index()
)
end
end
return itemstack
2014-11-09 20:06:28 +01:00
end
end
-- Function to register plants (default farming compatibility)
2014-11-09 20:06:28 +01:00
farming.register_plant = function(name, def)
2014-11-09 20:06:28 +01:00
if not def.steps then
return nil
end
local mname = name:split(":")[1]
local pname = name:split(":")[2]
-- Check def
def.description = def.description or S("Seed")
def.inventory_image = def.inventory_image or "unknown_item.png"
def.minlight = def.minlight or 13
2017-09-02 20:34:28 +02:00
def.maxlight = def.maxlight or 15
2014-11-09 20:06:28 +01:00
-- Register seed
minetest.register_node(":" .. mname .. ":seed_" .. pname, {
2014-11-09 20:06:28 +01:00
description = def.description,
tiles = {def.inventory_image},
inventory_image = def.inventory_image,
wield_image = def.inventory_image,
drawtype = "signlike",
groups = {seed = 1, snappy = 3, attached_node = 1},
paramtype = "light",
paramtype2 = "wallmounted",
walkable = false,
sunlight_propagates = true,
2015-07-05 11:54:18 +02:00
selection_box = farming.select,
place_param2 = def.place_param2 or nil,
next_plant = mname .. ":" .. pname .. "_1",
2014-11-09 20:06:28 +01:00
on_place = function(itemstack, placer, pointed_thing)
return farming.place_seed(itemstack, placer,
pointed_thing, mname .. ":" .. pname .. "_1")
2016-04-01 12:02:18 +02:00
end,
2014-11-09 20:06:28 +01:00
})
-- Register harvest
minetest.register_craftitem(":" .. mname .. ":" .. pname, {
description = pname:gsub("^%l", string.upper),
inventory_image = mname .. "_" .. pname .. ".png",
groups = def.groups or {flammable = 2},
2014-11-09 20:06:28 +01:00
})
-- Register growing steps
2015-08-26 16:05:17 +02:00
for i = 1, def.steps do
local base_rarity = 1
if def.steps ~= 1 then
base_rarity = 8 - (i - 1) * 7 / (def.steps - 1)
end
2014-11-09 20:06:28 +01:00
local drop = {
items = {
{items = {mname .. ":" .. pname}, rarity = base_rarity},
{items = {mname .. ":" .. pname}, rarity = base_rarity * 2},
{items = {mname .. ":seed_" .. pname}, rarity = base_rarity},
{items = {mname .. ":seed_" .. pname}, rarity = base_rarity * 2},
2014-11-09 20:06:28 +01:00
}
}
local g = {
snappy = 3, flammable = 2, plant = 1, growing = 1,
attached_node = 1, not_in_creative_inventory = 1,
}
2014-11-09 20:06:28 +01:00
-- Last step doesn't need growing=1 so Abm never has to check these
if i == def.steps then
2016-06-04 16:00:53 +02:00
g.growing = 0
2014-11-09 20:06:28 +01:00
end
2015-05-20 11:22:04 +02:00
local node_name = mname .. ":" .. pname .. "_" .. i
local next_plant = nil
if i < def.steps then
next_plant = mname .. ":" .. pname .. "_" .. (i + 1)
end
2015-05-20 11:22:04 +02:00
minetest.register_node(node_name, {
2014-11-09 20:06:28 +01:00
drawtype = "plantlike",
waving = 1,
tiles = {mname .. "_" .. pname .. "_" .. i .. ".png"},
paramtype = "light",
2018-03-22 16:57:36 +01:00
paramtype2 = def.paramtype2,
place_param2 = def.place_param2,
2014-11-09 20:06:28 +01:00
walkable = false,
buildable_to = true,
drop = drop,
2015-07-05 11:54:18 +02:00
selection_box = farming.select,
2014-11-09 20:06:28 +01:00
groups = g,
sounds = default.node_sound_leaves_defaults(),
minlight = def.minlight,
maxlight = def.maxlight,
next_plant = next_plant,
2014-11-09 20:06:28 +01:00
})
2017-05-06 11:09:42 +02:00
register_plant_node(node_name)
2014-11-09 20:06:28 +01:00
end
-- Return info
return {seed = mname .. ":seed_" .. pname, harvest = mname .. ":" .. pname}
2014-11-09 20:06:28 +01:00
end
2016-06-04 16:00:53 +02:00
2017-04-28 19:40:57 +02:00
-- default settings
farming.carrot = true
farming.potato = true
farming.tomato = true
farming.cucumber = true
farming.corn = true
farming.coffee = true
farming.melon = true
farming.sugar = true
farming.pumpkin = true
farming.cocoa = true
farming.raspberry = true
farming.blueberry = true
farming.rhubarb = true
farming.beans = true
farming.grapes = true
farming.barley = true
2017-08-31 13:14:54 +02:00
farming.chili = true
2017-04-28 19:40:57 +02:00
farming.hemp = true
farming.garlic = true
farming.onion = true
farming.pepper = true
2018-02-08 12:13:25 +01:00
farming.pineapple = true
2018-03-21 11:51:12 +01:00
farming.peas = true
2018-04-30 14:28:33 +02:00
farming.beetroot = true
2017-04-28 19:40:57 +02:00
farming.donuts = true
farming.rarety = 0.002 -- 0.006
2017-04-28 19:40:57 +02:00
-- Load new global settings if found inside mod folder
local input = io.open(farming.path.."/farming.conf", "r")
if input then
dofile(farming.path .. "/farming.conf")
input:close()
input = nil
end
-- load new world-specific settings if found inside world folder
local worldpath = minetest.get_worldpath()
local input = io.open(worldpath.."/farming.conf", "r")
if input then
dofile(worldpath .. "/farming.conf")
input:close()
input = nil
end
-- important items
2016-06-04 16:00:53 +02:00
dofile(farming.path.."/soil.lua")
dofile(farming.path.."/hoes.lua")
dofile(farming.path.."/grass.lua")
2018-03-21 11:51:12 +01:00
dofile(farming.path.."/utensils.lua")
-- default crops
2016-06-04 16:00:53 +02:00
dofile(farming.path.."/wheat.lua")
dofile(farming.path.."/cotton.lua")
-- additional crops and food (if enabled)
2017-04-28 19:40:57 +02:00
if farming.carrot then dofile(farming.path.."/carrot.lua") end
if farming.potato then dofile(farming.path.."/potato.lua") end
if farming.tomato then dofile(farming.path.."/tomato.lua") end
if farming.cucumber then dofile(farming.path.."/cucumber.lua") end
if farming.corn then dofile(farming.path.."/corn.lua") end
if farming.coffee then dofile(farming.path.."/coffee.lua") end
if farming.melon then dofile(farming.path.."/melon.lua") end
if farming.sugar then dofile(farming.path.."/sugar.lua") end
if farming.pumpkin then dofile(farming.path.."/pumpkin.lua") end
if farming.cocoa then dofile(farming.path.."/cocoa.lua") end
if farming.raspberry then dofile(farming.path.."/raspberry.lua") end
if farming.blueberry then dofile(farming.path.."/blueberry.lua") end
if farming.rhubarb then dofile(farming.path.."/rhubarb.lua") end
if farming.beans then dofile(farming.path.."/beanpole.lua") end
if farming.grapes then dofile(farming.path.."/grapes.lua") end
if farming.barley then dofile(farming.path.."/barley.lua") end
if farming.hemp then dofile(farming.path.."/hemp.lua") end
if farming.garlic then dofile(farming.path.."/garlic.lua") end
if farming.onion then dofile(farming.path.."/onion.lua") end
if farming.pepper then dofile(farming.path.."/pepper.lua") end
2018-02-08 12:13:25 +01:00
if farming.pineapple then dofile(farming.path.."/pineapple.lua") end
2018-03-21 11:51:12 +01:00
if farming.peas then dofile(farming.path.."/pea.lua") end
2018-04-30 14:28:33 +02:00
if farming.beetroot then dofile(farming.path.."/beetroot.lua") end
2018-03-24 15:05:42 +01:00
if farming.chili then dofile(farming.path.."/chili.lua") end
if farming.donuts then dofile(farming.path.."/donut.lua") end
2016-06-04 16:00:53 +02:00
dofile(farming.path.."/mapgen.lua")
dofile(farming.path.."/compatibility.lua") -- Farming Plus compatibility
2018-04-09 12:31:48 +02:00
dofile(farming.path.."/hoebomb.lua")
2016-11-12 12:03:04 +01:00
dofile(farming.path.."/lucky_block.lua")