Newer
Older
farming.select = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}
}
local statistics = dofile(farming.path.."/statistics.lua")
local S
if minetest.get_modpath("intllib") then
S = intllib.Getter()
else
S = function(s) return s end
end
farming.intllib = S
-- 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 0
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 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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
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
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, ... } }
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.
-- 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
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
}
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)
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
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 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 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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
-- https://git.tchncs.de/Illuna-Minetest/farming_plus/blob/master/init.lua#L54
function farming.generate_tree(pos, trunk, leaves, underground, replacements)
pos.y = pos.y-1
local nodename = minetest.get_node(pos).name
local ret = true
for _,name in ipairs(underground) do
if nodename == name then
ret = false
break
end
end
pos.y = pos.y+1
if not minetest.get_node_light(pos) then
return
end
if ret or minetest.get_node_light(pos) < 8 then
return
end
node = {name = ""}
for dy=1,4 do
pos.y = pos.y+dy
if minetest.get_node(pos).name ~= "air" then
return
end
pos.y = pos.y-dy
end
node.name = trunk
for dy=0,4 do
pos.y = pos.y+dy
minetest.set_node(pos, node)
pos.y = pos.y-dy
end
if not replacements then
replacements = {}
end
node.name = leaves
pos.y = pos.y+3
for dx=-2,2 do
for dz=-2,2 do
for dy=0,3 do
pos.x = pos.x+dx
pos.y = pos.y+dy
pos.z = pos.z+dz
if dx == 0 and dz == 0 and dy==3 then
if minetest.get_node(pos).name == "air" and math.random(1, 5) <= 4 then
minetest.set_node(pos, node)
for name,rarity in pairs(replacements) do
if math.random(1, rarity) == 1 then
minetest.set_node(pos, {name=name})
end
end
end
elseif dx == 0 and dz == 0 and dy==4 then
if minetest.get_node(pos).name == "air" and math.random(1, 5) <= 4 then
minetest.set_node(pos, node)
for name,rarity in pairs(replacements) do
if math.random(1, rarity) == 1 then
minetest.set_node(pos, {name=name})
end
end
end
elseif math.abs(dx) ~= 2 and math.abs(dz) ~= 2 then
if minetest.get_node(pos).name == "air" then
minetest.set_node(pos, node)
for name,rarity in pairs(replacements) do
if math.random(1, rarity) == 1 then
minetest.set_node(pos, {name=name})
end
end
end
else
if math.abs(dx) ~= 2 or math.abs(dz) ~= 2 then
if minetest.get_node(pos).name == "air" and math.random(1, 5) <= 4 then
minetest.set_node(pos, node)
for name,rarity in pairs(replacements) do
if math.random(1, rarity) == 1 then
minetest.set_node(pos, {name=name})
end
end
end
end
end
pos.x = pos.x-dx
pos.y = pos.y-dy
pos.z = pos.z-dz
end
end
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.
local stages_left = register_plant_node(node or pos)
if stages_left then
set_growing(pos, stages_left)
end
for _, node_def in ipairs(minetest.registered_nodes) do
register_plant_node(node_def)
end
end)
-- Just in case a growing type or added node is missed (also catches existing
-- nodes added to map before timers were incorporated).
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)
if not stages then
return false
end
if max_growth <= 0 then
return false
end
if not minetest.find_node_near(pos, 1, {"default:jungletree"}) then
local under = minetest.get_node({ x = pos.x, y = pos.y - 1, z = pos.z })
if minetest.get_item_group(under.name, "soil") < 3 then
local light_pos = {x = pos.x, y = pos.y + 1, z = pos.z}
if lambda < 0.1 then
return true
end
local light = (minetest.get_node_light(light_pos) or 0)
if not in_range(light, MIN_LIGHT, MAX_LIGHT) then
return true
end
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 = 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 = night_time(elapsed) / STAGE_LENGTH_AVG
end
growth = statistics.poisson(lambda, max_growth)
if growth < 1 then
return true
end
if minetest.registered_nodes[stages.stages_left[growth]] then
minetest.swap_node(pos, {name = stages.stages_left[growth]})
-- refill placed plant by crabman (26/08/2015)
local can_refill_plant = {
["farming:blueberry_1"] = "farming:blueberries",
["farming:carrot_1"] = "farming:carrot",
["farming:coffee_1"] = "farming:coffee_beans",
["farming:corn_1"] = "farming:corn",
["farming:cotton_1"] = "farming:seed_cotton",
["farming:cucumber_1"] = "farming:cucumber",
["farming:melon_1"] = "farming:melon_slice",
["farming:potato_1"] = "farming:potato",
["farming:pumpkin_1"] = "farming:pumpkin_slice",
["farming:raspberry_1"] = "farming:raspberries",
["farming:rhubarb_1"] = "farming:rhubarb",
["farming:tomato_1"] = "farming:tomato",
["farming:wheat_1"] = "farming:seed_wheat",
["farming:grapes_1"] = "farming:grapes",
["farming:beans_1"] = "farming:beans",
["farming:rhubarb_1"] = "farming:rhubarb",
["farming:cocoa_1"] = "farming:cocoa_beans",
["farming:barley_1"] = "farming:seed_barley",
}
function farming.refill_plant(player, plantname, index)
local inv = player:get_inventory()
local old_stack = inv:get_stack("main", index)
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)
--minetest.log("action", "farming: refilled stack("..plantname..") of " .. player:get_player_name() )
return
end
end
-- Place Seeds on Soil
function farming.place_seed(itemstack, placer, pointed_thing, plantname)
local pt = pointed_thing
-- check if pointing at a node
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
-- thanks to w_laenger from minetest irc
-- fixes itemframes compatibility
if minetest.registered_nodes[under.name].on_rightclick then
return minetest.registered_nodes[under.name].on_rightclick(pt.under, under, placer, itemstack, pt)
end
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
or minetest.get_item_group(under.name, "soil") < 2
-- avoid multiple seed placement bug
or minetest.get_item_group(above.name, "plant") ~= 0 then
-- 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
minetest.set_node(pt.above, {name = plantname, param2 = 1})
minetest.sound_play("default_place_node", {pos = pt.above, gain = 1.0})
if not minetest.setting_getbool("creative_mode") then
-- check for refill
if itemstack:get_count() == 0
and can_refill_plant[plantname] then
minetest.after(0.10,
farming.refill_plant,
placer,
can_refill_plant[plantname],
placer:get_wield_index()
)
-- Function to register plants (default farming compatibility)
local mname = name:split(":")[1]
local pname = name:split(":")[2]
-- Check def table
if not def.description then
if not def.inventory_image then
def.inventory_image = "unknown_item.png"
end
if not def.steps then
return nil
end
-- Register seed
minetest.register_node(":" .. mname .. ":seed_" .. pname, {
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,
on_place = function(itemstack, placer, pointed_thing)
return farming.place_seed(itemstack, placer,
pointed_thing, mname .. ":" .. pname .. "_1")
})
-- Register harvest
minetest.register_craftitem(":" .. mname .. ":" .. pname, {
description = pname:gsub("^%l", string.upper),
inventory_image = mname .. "_" .. pname .. ".png",
})
-- Register growing steps
local base_rarity = 1
if def.steps ~= 1 then
base_rarity = 8 - (i - 1) * 7 / (def.steps - 1)
end
{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},
local g = {snappy = 3, flammable = 2, plant = 1, not_in_creative_inventory = 1, attached_node = 1, growing = 1}
-- Last step doesn't need growing=1 so Abm never has to check these
if i == def.steps then
local node_name = mname .. ":" .. pname .. "_" .. i
drawtype = "plantlike",
waving = 1,
tiles = {mname .. "_" .. pname .. "_" .. i .. ".png"},
paramtype = "light",
walkable = false,
buildable_to = true,
drop = drop,
groups = g,
sounds = default.node_sound_leaves_defaults(),
})
end
-- Return info
local r = {seed = mname .. ":seed_" .. pname, harvest = mname .. ":" .. pname}
return r
end
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
-- default settings
farming.carrot = true
farming.potato = true
farming.tomato = true
farming.cucumber = true
farming.corn = true
farming.coffee = 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
farming.hemp = true
farming.donuts = true
-- 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
-- load crops
if not minetest.get_modpath("ethereal") then
dofile(farming.path.."/banana.lua")
end
dofile(farming.path.."/hoes.lua")
dofile(farming.path.."/grass.lua")
dofile(farming.path.."/wheat.lua")
dofile(farming.path.."/cotton.lua")
dofile(farming.path.."/carrot.lua")
dofile(farming.path.."/potato.lua")
dofile(farming.path.."/tomato.lua")
dofile(farming.path.."/cucumber.lua")
dofile(farming.path.."/corn.lua")
dofile(farming.path.."/coffee.lua")
dofile(farming.path.."/melon.lua")
dofile(farming.path.."/sugar.lua")
dofile(farming.path.."/pumpkin.lua")
dofile(farming.path.."/cocoa.lua")
dofile(farming.path.."/raspberry.lua")
dofile(farming.path.."/blueberry.lua")
dofile(farming.path.."/rhubarb.lua")
dofile(farming.path.."/beanpole.lua")
dofile(farming.path.."/grapes.lua")
dofile(farming.path.."/barley.lua")
dofile(farming.path.."/donut.lua")
dofile(farming.path.."/mapgen.lua")
dofile(farming.path.."/compatibility.lua") -- Farming Plus compatibility