init.lua 16 KB

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