api.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. function farming.notify_soil(pos)
  2. local minp = vector.add(pos, -4)
  3. local maxp = vector.add(pos, 4)
  4. local soils = minetest.find_nodes_in_area(minp, maxp, "group:field")
  5. if soils and #soils > 0 then
  6. for i=1, #soils do
  7. local timer = minetest.get_node_timer(soils[i])
  8. if timer and not timer:is_started() then
  9. timer:start(math.random(1, 60))
  10. end
  11. end
  12. end
  13. end
  14. function farming.notify_soil_single(pos)
  15. local timer = minetest.get_node_timer(pos)
  16. if timer and not timer:is_started() then
  17. timer:start(math.random(1, 60))
  18. end
  19. end
  20. -- Wear out hoes, place soil
  21. -- TODO Ignore group:flower
  22. farming.hoe_on_use = function(itemstack, user, pointed_thing, uses)
  23. local pt = pointed_thing
  24. -- check if pointing at a node
  25. if not pt then
  26. return
  27. end
  28. if pt.type ~= "node" then
  29. return
  30. end
  31. local under = minetest.get_node(pt.under)
  32. -- Let hoes be used to get resources back from planted mese crystals.
  33. -- Note that harvesting a crystal completely yeilds more fragments,
  34. -- but there is a risk that the you won't be able to restore the plant when you're done.
  35. if string.find(under.name, "^mese_crystals:mese_crystal_ore%d") then
  36. user:get_inventory():add_item("main", "default:mese_crystal_fragment 3")
  37. ambiance.sound_play("default_break_glass", pt.under, 0.3, 10)
  38. minetest.remove_node(pt.under)
  39. -- 1/2 chance to get bluerack back; this is because 1 bluerack makes 2 seeds.
  40. -- This way, we don't make it possible to magically duplicate resources.
  41. if math.random(1, 2) == 1 then
  42. local p = {x=pt.under.x, y=pt.under.y-1, z=pt.under.z}
  43. if minetest.get_node(p).name == "default:obsidian" then
  44. minetest.add_node(p, {name="rackstone:bluerack"})
  45. ambiance.sound_play("default_dig_cracky", pt.under, 1.0, 10)
  46. end
  47. end
  48. return
  49. end
  50. local p = {x=pt.under.x, y=pt.under.y+1, z=pt.under.z}
  51. local above = minetest.get_node(p)
  52. -- return if any of the nodes is not registered
  53. if not minetest.reg_ns_nodes[under.name] then
  54. return
  55. end
  56. if not minetest.reg_ns_nodes[above.name] then
  57. return
  58. end
  59. -- check if the node above the pointed thing is air
  60. if above.name ~= "air" then
  61. return
  62. end
  63. -- check if pointing at soil
  64. if minetest.get_item_group(under.name, "soil") ~= 1 then
  65. return
  66. end
  67. -- check if (wet) soil defined
  68. local ndef = minetest.reg_ns_nodes[under.name]
  69. if ndef.soil == nil or ndef.soil.wet == nil or ndef.soil.dry == nil then
  70. return
  71. end
  72. if minetest.is_protected(pt.under, user:get_player_name()) then
  73. minetest.record_protection_violation(pt.under, user:get_player_name())
  74. return
  75. end
  76. if minetest.is_protected(pt.above, user:get_player_name()) then
  77. minetest.record_protection_violation(pt.above, user:get_player_name())
  78. return
  79. end
  80. -- turn the node into soil and play sound
  81. minetest.add_node(pt.under, {name = ndef.soil.dry})
  82. minetest.sound_play("default_dig_crumbly", {
  83. pos = pt.under,
  84. gain = 0.5,
  85. })
  86. farming.notify_soil_single(pt.under)
  87. if not minetest.setting_getbool("creative_mode") then
  88. -- wear tool
  89. local wdef = itemstack:get_definition()
  90. itemstack:add_wear(65535/(uses-1))
  91. -- tool break sound
  92. if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then
  93. minetest.sound_play(wdef.sound.breaks, {pos = pt.above, gain = 0.5})
  94. end
  95. end
  96. return itemstack
  97. end
  98. -- Register new hoes
  99. farming.register_hoe = function(name, def)
  100. -- Check for : prefix (register new hoes in your mod's namespace)
  101. if name:sub(1,1) ~= ":" then
  102. name = ":" .. name
  103. end
  104. -- Check def table
  105. if def.description == nil then
  106. def.description = "Hoe"
  107. end
  108. if def.inventory_image == nil then
  109. def.inventory_image = "unknown_item.png"
  110. end
  111. if def.recipe == nil then
  112. def.recipe = {
  113. {"air","air",""},
  114. {"","group:stick",""},
  115. {"","group:stick",""}
  116. }
  117. end
  118. if def.max_uses == nil then
  119. def.max_uses = 30
  120. end
  121. -- Register the tool
  122. minetest.register_tool(name, {
  123. description = def.description,
  124. inventory_image = def.inventory_image,
  125. on_use = function(itemstack, user, pointed_thing)
  126. return farming.hoe_on_use(itemstack, user, pointed_thing, def.max_uses)
  127. end,
  128. groups = def.groups,
  129. sound = {breaks = "default_tool_breaks"},
  130. })
  131. -- Register its recipe
  132. if def.material == nil then
  133. minetest.register_craft({
  134. output = name:sub(2),
  135. recipe = def.recipe
  136. })
  137. else
  138. minetest.register_craft({
  139. output = name:sub(2),
  140. recipe = {
  141. {def.material, def.material, ""},
  142. {"", "group:stick", ""},
  143. {"", "group:stick", ""}
  144. }
  145. })
  146. -- Reverse Recipe
  147. minetest.register_craft({
  148. output = name:sub(2),
  149. recipe = {
  150. {"", def.material, def.material},
  151. {"", "group:stick", ""},
  152. {"", "group:stick", ""}
  153. }
  154. })
  155. end
  156. end
  157. local function tick_multiplier(pos)
  158. local minp = vector.subtract(pos, 2)
  159. local maxp = vector.add(pos, 2)
  160. local mult = 1
  161. local cold = minetest.find_nodes_in_area(minp, maxp, "group:cold")
  162. mult = mult + (#cold / 2)
  163. minp = vector.subtract(pos, 3)
  164. maxp = vector.add(pos, 3)
  165. local minerals = minetest.find_nodes_in_area(minp, maxp, "glowstone:minerals")
  166. mult = mult - (#minerals / 4)
  167. -- Multiplier cannot be less than 0.3.
  168. if mult < 0.2 then mult = 0.2 end
  169. return mult
  170. end
  171. -- how often node timers for plants will tick, +/- some random value
  172. local function tick(pos)
  173. local mult = tick_multiplier(pos)
  174. local min = 200 * mult
  175. local max = 350 * mult
  176. minetest.get_node_timer(pos):start(math.random(min, max))
  177. --minetest.get_node_timer(pos):start(1.0) -- Debug
  178. end
  179. -- how often a growth failure tick is retried (e.g. too dark)
  180. local function tick_again(pos)
  181. local min = 40
  182. local max = 80
  183. minetest.get_node_timer(pos):start(math.random(min, max))
  184. --minetest.get_node_timer(pos):start(1.0) -- Debug
  185. end
  186. -- Seed placement
  187. farming.place_seed = function(itemstack, placer, pointed_thing, plantname)
  188. local pt = pointed_thing
  189. -- check if pointing at a node
  190. if not pt then
  191. return itemstack
  192. end
  193. if pt.type ~= "node" then
  194. return itemstack
  195. end
  196. local under = minetest.get_node(pt.under)
  197. -- Pass through interactions to nodes that define them (like chests).
  198. do
  199. local pdef = minetest.reg_ns_nodes[under.name]
  200. if pdef and pdef.on_rightclick and not placer:get_player_control().sneak then
  201. return pdef.on_rightclick(pt.under, under, placer, itemstack, pt)
  202. end
  203. end
  204. local above = minetest.get_node(pt.above)
  205. -- Permit player to place seed on protected soil (by commenting this code).
  206. -- This allows players to build public farms.
  207. --if minetest.is_protected(pt.under, placer:get_player_name()) then
  208. -- minetest.record_protection_violation(pt.under, placer:get_player_name())
  209. -- return
  210. --end
  211. if minetest.is_protected(pt.above, placer:get_player_name()) then
  212. minetest.record_protection_violation(pt.above, placer:get_player_name())
  213. return
  214. end
  215. -- return if any of the nodes is not registered
  216. if not minetest.reg_ns_nodes[under.name] then
  217. return itemstack
  218. end
  219. if not minetest.reg_ns_nodes[above.name] then
  220. return itemstack
  221. end
  222. -- check if pointing at the top of the node
  223. if pt.above.y ~= pt.under.y+1 then
  224. return itemstack
  225. end
  226. -- check if you can replace the node above the pointed node
  227. local ndef = minetest.reg_ns_nodes[above.name]
  228. if not ndef or not ndef.buildable_to then
  229. return itemstack
  230. end
  231. -- check if pointing at soil
  232. if minetest.get_item_group(under.name, "soil") < 2 then
  233. return itemstack
  234. end
  235. -- add the node and remove 1 item from the itemstack
  236. -- note: use of `add_node` automatically invokes droplift + dirtspread notifications.
  237. minetest.add_node(pt.above, {name = plantname, param2 = 1})
  238. tick(pt.above)
  239. itemstack:take_item()
  240. return itemstack
  241. end
  242. -- This should only ever be called from the `on_timer' callback of a node.
  243. farming.grow_plant = function(pos, elapsed)
  244. local node = minetest.get_node(pos)
  245. local name = node.name
  246. local def = minetest.reg_ns_nodes[name]
  247. if not def.next_plant then
  248. -- disable timer for fully grown plant
  249. return
  250. end
  251. -- grow seed
  252. if minetest.get_item_group(node.name, "seed") and def.fertility then
  253. local soil_node = minetest.get_node_or_nil({x = pos.x, y = pos.y - 1, z = pos.z})
  254. if not soil_node then
  255. tick_again(pos)
  256. return
  257. end
  258. -- omitted is a check for light, we assume seeds can germinate in the dark.
  259. for _, v in pairs(def.fertility) do
  260. if minetest.get_item_group(soil_node.name, v) ~= 0 then
  261. local placenode = {name = def.next_plant}
  262. if def.place_param2 then
  263. placenode.param2 = def.place_param2
  264. end
  265. minetest.swap_node(pos, placenode)
  266. if minetest.reg_ns_nodes[def.next_plant].next_plant then
  267. tick(pos)
  268. return
  269. end
  270. end
  271. end
  272. return
  273. end
  274. -- check if on wet soil
  275. local below = minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z})
  276. if minetest.get_item_group(below.name, "soil") < 3 then
  277. tick_again(pos)
  278. return
  279. end
  280. -- check light
  281. local light = minetest.get_node_light(pos)
  282. if not light or light < def.minlight or light > def.maxlight then
  283. tick_again(pos)
  284. return
  285. end
  286. -- grow
  287. local placenode = {name = def.next_plant}
  288. if def.place_param2 then
  289. placenode.param2 = def.place_param2
  290. end
  291. minetest.swap_node(pos, placenode)
  292. -- new timer needed?
  293. if minetest.reg_ns_nodes[def.next_plant].next_plant then
  294. tick(pos)
  295. end
  296. return
  297. end
  298. -- Register plants
  299. farming.register_plant = function(name, def)
  300. local mname = name:split(":")[1]
  301. local pname = name:split(":")[2]
  302. -- Check def table
  303. if not def.description then
  304. def.description = "Seed"
  305. end
  306. if not def.inventory_image then
  307. def.inventory_image = "unknown_item.png"
  308. end
  309. if not def.steps then
  310. return nil
  311. end
  312. if not def.minlight then
  313. def.minlight = 1
  314. end
  315. if not def.maxlight then
  316. def.maxlight = 14
  317. end
  318. if not def.fertility then
  319. def.fertility = {}
  320. end
  321. -- Register seed
  322. local g = {level = 1, seed = 1, snappy = 3, attached_node = 1, flammable = 2, notify_destruct = 1}
  323. for k, v in pairs(def.fertility) do
  324. g[v] = 1
  325. end
  326. minetest.register_node(":" .. mname .. ":seed_" .. pname, {
  327. description = def.description,
  328. tiles = {def.inventory_image},
  329. inventory_image = def.inventory_image,
  330. wield_image = def.inventory_image,
  331. drawtype = "signlike",
  332. groups = g,
  333. paramtype = "light",
  334. paramtype2 = "wallmounted",
  335. place_param2 = def.place_param2 or nil, -- this isn't actually used for placement
  336. walkable = false,
  337. sunlight_propagates = true,
  338. selection_box = {
  339. type = "fixed",
  340. fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5},
  341. },
  342. fertility = def.fertility,
  343. sounds = default.node_sound_dirt_defaults({
  344. dug = {name = "default_grass_footstep", gain = 0.2},
  345. place = {name = "default_place_node", gain = 0.25},
  346. }),
  347. on_place = function(itemstack, placer, pointed_thing)
  348. local under = pointed_thing.under
  349. local node = minetest.get_node(under)
  350. local udef = minetest.reg_ns_nodes[node.name]
  351. if udef and udef.on_rightclick and
  352. not (placer and placer:get_player_control().sneak) then
  353. return udef.on_rightclick(under, node, placer, itemstack,
  354. pointed_thing) or itemstack
  355. end
  356. return farming.place_seed(itemstack, placer, pointed_thing, mname .. ":seed_" .. pname)
  357. end,
  358. next_plant = mname .. ":" .. pname .. "_1",
  359. on_timer = farming.grow_plant,
  360. minlight = def.minlight,
  361. maxlight = def.maxlight,
  362. })
  363. -- Register harvest
  364. minetest.register_craftitem(":" .. mname .. ":" .. pname, {
  365. description = pname:gsub("^%l", string.upper),
  366. inventory_image = mname .. "_" .. pname .. ".png",
  367. groups = {flammable = 2},
  368. -- Pass through flowerpot data if available.
  369. flowerpot_insert = def.flowerpot_insert,
  370. })
  371. -- Register growing steps
  372. for i = 1, def.steps do
  373. local drop = {
  374. items = {
  375. {items = {mname .. ":" .. pname}, rarity = 9 - i},
  376. {items = {mname .. ":" .. pname}, rarity= 18 - i * 2},
  377. {items = {mname .. ":seed_" .. pname}, rarity = 9 - i},
  378. {items = {mname .. ":seed_" .. pname}, rarity = 18 - i * 2},
  379. }
  380. }
  381. local nodegroups = utility.dig_groups("crop", {flammable = 2, plant = 1, not_in_creative_inventory = 1, attached_node = 1, notify_destruct = 1})
  382. nodegroups[pname] = i
  383. local next_plant = nil
  384. if i < def.steps then
  385. next_plant = mname .. ":" .. pname .. "_" .. (i + 1)
  386. end
  387. minetest.register_node(mname .. ":" .. pname .. "_" .. i, {
  388. drawtype = "plantlike",
  389. waving = 1,
  390. tiles = {mname .. "_" .. pname .. "_" .. i .. ".png"},
  391. paramtype = "light",
  392. paramtype2 = def.paramtype2 or nil,
  393. place_param2 = def.place_param2 or nil,
  394. walkable = false,
  395. buildable_to = true,
  396. drop = drop,
  397. selection_box = {
  398. type = "fixed",
  399. fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5},
  400. },
  401. groups = nodegroups,
  402. sounds = default.node_sound_leaves_defaults(),
  403. next_plant = next_plant,
  404. on_timer = farming.grow_plant,
  405. minlight = def.minlight,
  406. maxlight = def.maxlight,
  407. movement_speed_multiplier = default.SLOW_SPEED_PLANTS,
  408. -- Pass through flowerpot data if available.
  409. flowerpot_drop = def.flowerpot_drop,
  410. })
  411. end
  412. -- Return
  413. local r = {
  414. seed = mname .. ":seed_" .. pname,
  415. harvest = mname .. ":" .. pname
  416. }
  417. return r
  418. end