init.lua 16 KB

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