init.lua 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. --[[
  2. Farming Redo Mod
  3. by TenPlus1
  4. NEW growing routine by prestidigitator
  5. auto-refill by crabman77
  6. ]]
  7. farming = {
  8. mod = "redo",
  9. version = "20220603",
  10. path = minetest.get_modpath("farming"),
  11. select = {
  12. type = "fixed",
  13. fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}
  14. },
  15. registered_plants = {},
  16. min_light = 12,
  17. max_light = 15
  18. }
  19. local creative_mode_cache = minetest.settings:get_bool("creative_mode")
  20. function farming.is_creative(name)
  21. return creative_mode_cache or minetest.check_player_privs(name, {creative = true})
  22. end
  23. local statistics = dofile(farming.path .. "/statistics.lua")
  24. -- Intllib
  25. local S
  26. if minetest.get_translator ~= nil then
  27. S = minetest.get_translator("farming") -- 5.x translation function
  28. else
  29. if minetest.get_modpath("intllib") then
  30. dofile(minetest.get_modpath("intllib") .. "/init.lua")
  31. if intllib.make_gettext_pair then
  32. gettext, ngettext = intllib.make_gettext_pair() -- new gettext method
  33. else
  34. gettext = intllib.Getter() -- old text file method
  35. end
  36. S = gettext
  37. else -- boilerplate function
  38. S = function(str, ...)
  39. local args = {...}
  40. return str:gsub("@%d+", function(match)
  41. return args[tonumber(match:sub(2))]
  42. end)
  43. end
  44. end
  45. end
  46. farming.intllib = S
  47. -- Utility Function
  48. local time_speed = tonumber(minetest.settings:get("time_speed")) or 72
  49. local SECS_PER_CYCLE = (time_speed > 0 and (24 * 60 * 60) / time_speed) or 0
  50. local function clamp(x, min, max)
  51. return (x < min and min) or (x > max and max) or x
  52. end
  53. -- return amount of day or night that has elapsed
  54. -- dt is time elapsed, count_day if true counts day, otherwise night
  55. local function day_or_night_time(dt, count_day)
  56. local t_day = minetest.get_timeofday()
  57. local t1_day = t_day - dt / SECS_PER_CYCLE
  58. local t1_c, t2_c -- t1_c < t2_c and t2_c always in [0, 1)
  59. if count_day then
  60. if t_day < 0.25 then
  61. t1_c = t1_day + 0.75 -- Relative to sunup, yesterday
  62. t2_c = t_day + 0.75
  63. else
  64. t1_c = t1_day - 0.25 -- Relative to sunup, today
  65. t2_c = t_day - 0.25
  66. end
  67. else
  68. if t_day < 0.75 then
  69. t1_c = t1_day + 0.25 -- Relative to sundown, yesterday
  70. t2_c = t_day + 0.25
  71. else
  72. t1_c = t1_day - 0.75 -- Relative to sundown, today
  73. t2_c = t_day - 0.75
  74. end
  75. end
  76. local dt_c = clamp(t2_c, 0, 0.5) - clamp(t1_c, 0, 0.5) -- this cycle
  77. if t1_c < -0.5 then
  78. local nc = math.floor(-t1_c)
  79. t1_c = t1_c + nc
  80. dt_c = dt_c + 0.5 * nc + clamp(-t1_c - 0.5, 0, 0.5)
  81. end
  82. return dt_c * SECS_PER_CYCLE
  83. end
  84. -- Growth Logic
  85. local STAGE_LENGTH_AVG = tonumber(
  86. minetest.settings:get("farming_stage_length")) or 200 -- 160
  87. local STAGE_LENGTH_DEV = STAGE_LENGTH_AVG / 6
  88. -- return plant name and stage from node provided
  89. local function plant_name_stage(node)
  90. local name
  91. if type(node) == "table" then
  92. if node.name then
  93. name = node.name
  94. elseif node.x and node.y and node.z then
  95. node = minetest.get_node_or_nil(node)
  96. name = node and node.name
  97. end
  98. else
  99. name = tostring(node)
  100. end
  101. if not name or name == "ignore" then
  102. return nil
  103. end
  104. local sep_pos = name:find("_[^_]+$")
  105. if sep_pos and sep_pos > 1 then
  106. local stage = tonumber(name:sub(sep_pos + 1))
  107. if stage and stage >= 0 then
  108. return name:sub(1, sep_pos - 1), stage
  109. end
  110. end
  111. return name, 0
  112. end
  113. -- Map from node name to
  114. -- { plant_name = ..., name = ..., stage = n, stages_left = { node_name, ... } }
  115. local plant_stages = {}
  116. farming.plant_stages = plant_stages
  117. --- Registers the stages of growth of a (possible plant) node.
  118. --
  119. -- @param node
  120. -- Node or position table, or node name.
  121. -- @return
  122. -- The (possibly zero) number of stages of growth the plant will go through
  123. -- before being fully grown, or nil if not a plant.
  124. local register_plant_node
  125. -- Recursive helper
  126. local function reg_plant_stages(plant_name, stage, force_last)
  127. local node_name = plant_name and plant_name .. "_" .. stage
  128. local node_def = node_name and minetest.registered_nodes[node_name]
  129. if not node_def then
  130. return nil
  131. end
  132. local stages = plant_stages[node_name]
  133. if stages then
  134. return stages
  135. end
  136. if minetest.get_item_group(node_name, "growing") > 0 then
  137. local ns = reg_plant_stages(plant_name, stage + 1, true)
  138. local stages_left = (ns and { ns.name, unpack(ns.stages_left) }) or {}
  139. stages = {
  140. plant_name = plant_name,
  141. name = node_name,
  142. stage = stage,
  143. stages_left = stages_left
  144. }
  145. if #stages_left > 0 then
  146. local old_constr = node_def.on_construct
  147. local old_destr = node_def.on_destruct
  148. minetest.override_item(node_name,
  149. {
  150. on_construct = function(pos)
  151. if old_constr then
  152. old_constr(pos)
  153. end
  154. farming.handle_growth(pos)
  155. end,
  156. on_destruct = function(pos)
  157. minetest.get_node_timer(pos):stop()
  158. if old_destr then
  159. old_destr(pos)
  160. end
  161. end,
  162. on_timer = function(pos, elapsed)
  163. return farming.plant_growth_timer(pos, elapsed, node_name)
  164. end,
  165. })
  166. end
  167. elseif force_last then
  168. stages = {
  169. plant_name = plant_name,
  170. name = node_name,
  171. stage = stage,
  172. stages_left = {}
  173. }
  174. else
  175. return nil
  176. end
  177. plant_stages[node_name] = stages
  178. return stages
  179. end
  180. local register_plant_node = function(node)
  181. local plant_name, stage = plant_name_stage(node)
  182. if plant_name then
  183. local stages = reg_plant_stages(plant_name, stage, false)
  184. return stages and #stages.stages_left
  185. else
  186. return nil
  187. end
  188. end
  189. local function set_growing(pos, stages_left)
  190. if not stages_left then
  191. return
  192. end
  193. local timer = minetest.get_node_timer(pos)
  194. if stages_left > 0 then
  195. if not timer:is_started() then
  196. local stage_length = statistics.normal(STAGE_LENGTH_AVG, STAGE_LENGTH_DEV)
  197. stage_length = clamp(stage_length, 0.5 * STAGE_LENGTH_AVG, 3.0 * STAGE_LENGTH_AVG)
  198. timer:set(stage_length, -0.5 * math.random() * STAGE_LENGTH_AVG)
  199. end
  200. elseif timer:is_started() then
  201. timer:stop()
  202. end
  203. end
  204. -- detects a crop at given position, starting or stopping growth timer when needed
  205. function farming.handle_growth(pos, node)
  206. if not pos then
  207. return
  208. end
  209. local stages_left = register_plant_node(node or pos)
  210. if stages_left then
  211. set_growing(pos, stages_left)
  212. end
  213. end
  214. minetest.after(0, function()
  215. for _, node_def in pairs(minetest.registered_nodes) do
  216. register_plant_node(node_def)
  217. end
  218. end)
  219. -- Just in case a growing type or added node is missed (also catches existing
  220. -- nodes added to map before timers were incorporated).
  221. minetest.register_abm({
  222. nodenames = {"group:growing"},
  223. interval = 300,
  224. chance = 1,
  225. catch_up = false,
  226. action = function(pos, node)
  227. farming.handle_growth(pos, node)
  228. end
  229. })
  230. -- Plant timer function that grows plants under the right conditions.
  231. function farming.plant_growth_timer(pos, elapsed, node_name)
  232. local stages = plant_stages[node_name]
  233. if not stages then
  234. return false
  235. end
  236. local max_growth = #stages.stages_left
  237. if max_growth <= 0 then
  238. return false
  239. end
  240. -- custom growth check
  241. local chk = minetest.registered_nodes[node_name].growth_check
  242. if chk then
  243. if chk(pos, node_name) then
  244. return true
  245. end
  246. -- otherwise check for wet soil beneath crop
  247. else
  248. local under = minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z})
  249. if minetest.get_item_group(under.name, "soil") < 3 then
  250. return true
  251. end
  252. end
  253. local growth
  254. local light_pos = {x = pos.x, y = pos.y, z = pos.z}
  255. local lambda = elapsed / STAGE_LENGTH_AVG
  256. if lambda < 0.1 then
  257. return true
  258. end
  259. local MIN_LIGHT = minetest.registered_nodes[node_name].minlight or farming.min_light
  260. local MAX_LIGHT = minetest.registered_nodes[node_name].maxlight or farming.max_light
  261. if max_growth == 1 or lambda < 2.0 then
  262. local light = (minetest.get_node_light(light_pos) or 0)
  263. if light < MIN_LIGHT or light > MAX_LIGHT then
  264. return true
  265. end
  266. growth = 1
  267. else
  268. local night_light = (minetest.get_node_light(light_pos, 0) or 0)
  269. local day_light = (minetest.get_node_light(light_pos, 0.5) or 0)
  270. local night_growth = night_light >= MIN_LIGHT and night_light <= MAX_LIGHT
  271. local day_growth = day_light >= MIN_LIGHT and day_light <= MAX_LIGHT
  272. if not night_growth then
  273. if not day_growth then
  274. return true
  275. end
  276. lambda = day_or_night_time(elapsed, true) / STAGE_LENGTH_AVG
  277. elseif not day_growth then
  278. lambda = day_or_night_time(elapsed, false) / STAGE_LENGTH_AVG
  279. end
  280. growth = statistics.poisson(lambda, max_growth)
  281. if growth < 1 then
  282. return true
  283. end
  284. end
  285. if minetest.registered_nodes[stages.stages_left[growth]] then
  286. local p2 = minetest.registered_nodes[stages.stages_left[growth] ].place_param2 or 1
  287. minetest.swap_node(pos, {name = stages.stages_left[growth], param2 = p2})
  288. else
  289. return true
  290. end
  291. return growth ~= max_growth
  292. end
  293. -- refill placed plant by crabman (26/08/2015) updated by TenPlus1
  294. function farming.refill_plant(player, plantname, index)
  295. if not player then return end
  296. local inv = player:get_inventory()
  297. if not inv then return end
  298. local old_stack = inv:get_stack("main", index)
  299. if old_stack:get_name() ~= "" then
  300. return
  301. end
  302. for i, stack in ipairs(inv:get_list("main")) do
  303. if stack:get_name() == plantname and i ~= index then
  304. inv:set_stack("main", index, stack)
  305. stack:clear()
  306. inv:set_stack("main", i, stack)
  307. return
  308. end
  309. end
  310. end
  311. -- Place Seeds on Soil
  312. function farming.place_seed(itemstack, placer, pointed_thing, plantname)
  313. local pt = pointed_thing
  314. -- check if pointing at a node
  315. if not pt or pt.type ~= "node" then
  316. return
  317. end
  318. local under = minetest.get_node(pt.under)
  319. -- am I right-clicking on something that has a custom on_place set?
  320. -- thanks to Krock for helping with this issue :)
  321. local def = minetest.registered_nodes[under.name]
  322. if placer and itemstack and def and def.on_rightclick then
  323. return def.on_rightclick(pt.under, under, placer, itemstack, pt)
  324. end
  325. local above = minetest.get_node(pt.above)
  326. -- check if pointing at the top of the node
  327. if pt.above.y ~= pt.under.y + 1 then
  328. return
  329. end
  330. -- return if any of the nodes is not registered
  331. if not minetest.registered_nodes[under.name]
  332. or not minetest.registered_nodes[above.name] then
  333. return
  334. end
  335. -- can I replace above node, and am I pointing at soil
  336. if not minetest.registered_nodes[above.name].buildable_to
  337. or minetest.get_item_group(under.name, "soil") < 2
  338. -- avoid multiple seed placement bug
  339. or minetest.get_item_group(above.name, "plant") ~= 0 then
  340. return
  341. end
  342. -- is player planting seed?
  343. local name = placer and placer:get_player_name() or ""
  344. -- if not protected then add node and remove 1 item from the itemstack
  345. if not minetest.is_protected(pt.above, name) then
  346. local p2 = minetest.registered_nodes[plantname].place_param2 or 1
  347. minetest.set_node(pt.above, {name = plantname, param2 = p2})
  348. --minetest.get_node_timer(pt.above):start(1)
  349. --farming.handle_growth(pt.above)--, node)
  350. minetest.sound_play("default_place_node", {pos = pt.above, gain = 1.0})
  351. if placer and itemstack
  352. and not farming.is_creative(placer:get_player_name()) then
  353. local name = itemstack:get_name()
  354. itemstack:take_item()
  355. -- check for refill
  356. if itemstack:get_count() == 0 then
  357. minetest.after(0.2,
  358. farming.refill_plant,
  359. placer,
  360. name,
  361. placer:get_wield_index()
  362. )
  363. end
  364. end
  365. return itemstack
  366. end
  367. end
  368. -- Function to register plants (default farming compatibility)
  369. farming.register_plant = function(name, def)
  370. if not def.steps then
  371. return nil
  372. end
  373. local mname = name:split(":")[1]
  374. local pname = name:split(":")[2]
  375. -- Check def
  376. def.description = def.description or S("Seed")
  377. def.inventory_image = def.inventory_image or "unknown_item.png"
  378. def.minlight = def.minlight or 12
  379. def.maxlight = def.maxlight or 15
  380. -- Register seed
  381. minetest.register_node(":" .. mname .. ":seed_" .. pname, {
  382. description = def.description,
  383. tiles = {def.inventory_image},
  384. inventory_image = def.inventory_image,
  385. wield_image = def.inventory_image,
  386. drawtype = "signlike",
  387. groups = {seed = 1, snappy = 3, attached_node = 1, flammable = 2},
  388. paramtype = "light",
  389. paramtype2 = "wallmounted",
  390. walkable = false,
  391. sunlight_propagates = true,
  392. selection_box = farming.select,
  393. place_param2 = def.place_param2 or nil,
  394. next_plant = mname .. ":" .. pname .. "_1",
  395. on_place = function(itemstack, placer, pointed_thing)
  396. return farming.place_seed(itemstack, placer,
  397. pointed_thing, mname .. ":" .. pname .. "_1")
  398. end,
  399. })
  400. -- Register harvest
  401. minetest.register_craftitem(":" .. mname .. ":" .. pname, {
  402. description = pname:gsub("^%l", string.upper),
  403. inventory_image = mname .. "_" .. pname .. ".png",
  404. groups = def.groups or {flammable = 2},
  405. })
  406. -- Register growing steps
  407. for i = 1, def.steps do
  408. local base_rarity = 1
  409. if def.steps ~= 1 then
  410. base_rarity = 8 - (i - 1) * 7 / (def.steps - 1)
  411. end
  412. local drop = {
  413. items = {
  414. {items = {mname .. ":" .. pname}, rarity = base_rarity},
  415. {items = {mname .. ":" .. pname}, rarity = base_rarity * 2},
  416. {items = {mname .. ":seed_" .. pname}, rarity = base_rarity},
  417. {items = {mname .. ":seed_" .. pname}, rarity = base_rarity * 2},
  418. }
  419. }
  420. local g = {
  421. snappy = 3, flammable = 2, plant = 1, growing = 1,
  422. attached_node = 1, not_in_creative_inventory = 1,
  423. }
  424. -- Last step doesn't need growing=1 so Abm never has to check these
  425. if i == def.steps then
  426. g.growing = 0
  427. end
  428. local node_name = mname .. ":" .. pname .. "_" .. i
  429. local next_plant = nil
  430. if i < def.steps then
  431. next_plant = mname .. ":" .. pname .. "_" .. (i + 1)
  432. end
  433. minetest.register_node(node_name, {
  434. drawtype = "plantlike",
  435. waving = 1,
  436. tiles = {mname .. "_" .. pname .. "_" .. i .. ".png"},
  437. paramtype = "light",
  438. paramtype2 = def.paramtype2,
  439. place_param2 = def.place_param2,
  440. walkable = false,
  441. buildable_to = true,
  442. sunlight_propagates = true,
  443. drop = drop,
  444. selection_box = farming.select,
  445. groups = g,
  446. sounds = default.node_sound_leaves_defaults(),
  447. minlight = def.minlight,
  448. maxlight = def.maxlight,
  449. next_plant = next_plant
  450. })
  451. end
  452. -- add to farming.registered_plants
  453. farming.registered_plants[mname .. ":" .. pname] = {
  454. crop = mname .. ":" .. pname,
  455. seed = mname .. ":seed_" .. pname,
  456. steps = def.steps,
  457. minlight = def.minlight,
  458. maxlight = def.maxlight
  459. }
  460. --print(dump(farming.registered_plants[mname .. ":" .. pname]))
  461. -- Return info
  462. return {seed = mname .. ":seed_" .. pname, harvest = mname .. ":" .. pname}
  463. end
  464. -- default settings
  465. farming.carrot = 0.001
  466. farming.potato = 0.001
  467. farming.tomato = 0.001
  468. farming.cucumber = 0.001
  469. farming.corn = 0.001
  470. farming.coffee = 0.001
  471. farming.melon = 0.001
  472. farming.pumpkin = 0.001
  473. farming.cocoa = true
  474. farming.raspberry = 0.001
  475. farming.blueberry = 0.001
  476. farming.rhubarb = 0.001
  477. farming.beans = 0.001
  478. farming.grapes = 0.001
  479. farming.barley = true
  480. farming.chili = 0.003
  481. farming.hemp = 0.003
  482. farming.garlic = 0.001
  483. farming.onion = 0.001
  484. farming.pepper = 0.002
  485. farming.pineapple = 0.001
  486. farming.peas = 0.001
  487. farming.beetroot = 0.001
  488. farming.mint = 0.005
  489. farming.cabbage = 0.001
  490. farming.blackberry = 0.002
  491. farming.soy = 0.001
  492. farming.vanilla = 0.001
  493. farming.lettuce = 0.001
  494. farming.artichoke = 0.001
  495. farming.parsley = 0.002
  496. farming.sunflower = 0.001
  497. farming.grains = true
  498. farming.rice = true
  499. farming.rarety = 0.002
  500. -- Load new global settings if found inside mod folder
  501. local input = io.open(farming.path.."/farming.conf", "r")
  502. if input then
  503. dofile(farming.path .. "/farming.conf")
  504. input:close()
  505. end
  506. -- load new world-specific settings if found inside world folder
  507. local worldpath = minetest.get_worldpath()
  508. input = io.open(worldpath.."/farming.conf", "r")
  509. if input then
  510. dofile(worldpath .. "/farming.conf")
  511. input:close()
  512. end
  513. -- important items
  514. dofile(farming.path.."/soil.lua")
  515. dofile(farming.path.."/hoes.lua")
  516. dofile(farming.path.."/grass.lua")
  517. dofile(farming.path.."/utensils.lua")
  518. -- default crops
  519. dofile(farming.path.."/crops/wheat.lua")
  520. dofile(farming.path.."/crops/cotton.lua")
  521. -- helper function
  522. local function ddoo(file, check)
  523. if check then
  524. dofile(farming.path .. "/crops/" .. file)
  525. end
  526. end
  527. -- add additional crops and food (if enabled)
  528. ddoo("carrot.lua", farming.carrot)
  529. ddoo("potato.lua", farming.potato)
  530. ddoo("tomato.lua", farming.tomato)
  531. ddoo("cucumber.lua", farming.cucumber)
  532. ddoo("corn.lua", farming.corn)
  533. ddoo("coffee.lua", farming.coffee)
  534. ddoo("melon.lua", farming.melon)
  535. ddoo("pumpkin.lua", farming.pumpkin)
  536. ddoo("cocoa.lua", farming.cocoa)
  537. ddoo("raspberry.lua", farming.raspberry)
  538. ddoo("blueberry.lua", farming.blueberry)
  539. ddoo("rhubarb.lua", farming.rhubarb)
  540. ddoo("beans.lua", farming.beans)
  541. ddoo("grapes.lua", farming.grapes)
  542. ddoo("barley.lua", farming.barley)
  543. ddoo("hemp.lua", farming.hemp)
  544. ddoo("garlic.lua", farming.garlic)
  545. ddoo("onion.lua", farming.onion)
  546. ddoo("pepper.lua", farming.pepper)
  547. ddoo("pineapple.lua", farming.pineapple)
  548. ddoo("peas.lua", farming.peas)
  549. ddoo("beetroot.lua", farming.beetroot)
  550. ddoo("chili.lua", farming.chili)
  551. ddoo("ryeoatrice.lua", farming.grains)
  552. ddoo("rice.lua", farming.rice)
  553. ddoo("mint.lua", farming.mint)
  554. ddoo("cabbage.lua", farming.cabbage)
  555. ddoo("blackberry.lua", farming.blackberry)
  556. ddoo("soy.lua", farming.soy)
  557. ddoo("vanilla.lua", farming.vanilla)
  558. ddoo("lettuce.lua", farming.lettuce)
  559. ddoo("artichoke.lua", farming.artichoke)
  560. ddoo("parsley.lua", farming.parsley)
  561. ddoo("sunflower.lua", farming.sunflower)
  562. dofile(farming.path .. "/food.lua")
  563. dofile(farming.path .. "/mapgen.lua")
  564. dofile(farming.path .. "/compatibility.lua") -- Farming Plus compatibility
  565. dofile(farming.path .. "/lucky_block.lua")