api.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. -- Wear out hoes, place soil
  2. -- TODO Ignore group:flower
  3. farming.registered_plants = {}
  4. farming.hoe_on_use = function(itemstack, user, pointed_thing, uses)
  5. local pt = pointed_thing
  6. -- check if pointing at a node
  7. if not pt then
  8. return
  9. end
  10. if pt.type ~= "node" then
  11. return
  12. end
  13. local under = minetest.get_node(pt.under)
  14. local p = {x=pt.under.x, y=pt.under.y+1, z=pt.under.z}
  15. local above = minetest.get_node(p)
  16. -- return if any of the nodes is not registered
  17. if not minetest.registered_nodes[under.name] then
  18. return
  19. end
  20. if not minetest.registered_nodes[above.name] then
  21. return
  22. end
  23. -- check if the node above the pointed thing is air
  24. if above.name ~= "air" then
  25. return
  26. end
  27. -- check if pointing at soil
  28. if minetest.get_item_group(under.name, "soil") ~= 1 then
  29. return
  30. end
  31. -- check if (wet) soil defined
  32. local regN = minetest.registered_nodes
  33. if regN[under.name].soil == nil or regN[under.name].soil.wet == nil or regN[under.name].soil.dry == nil then
  34. return
  35. end
  36. if minetest.is_protected(pt.under, user:get_player_name()) then
  37. minetest.record_protection_violation(pt.under, user:get_player_name())
  38. return
  39. end
  40. if minetest.is_protected(pt.above, user:get_player_name()) then
  41. minetest.record_protection_violation(pt.above, user:get_player_name())
  42. return
  43. end
  44. -- turn the node into soil and play sound
  45. minetest.set_node(pt.under, {name = regN[under.name].soil.dry})
  46. minetest.sound_play("default_dig_crumbly", {
  47. pos = pt.under,
  48. gain = 0.5,
  49. })
  50. if not (creative and creative.is_enabled_for
  51. and creative.is_enabled_for(user:get_player_name())) then
  52. -- wear tool
  53. local wdef = itemstack:get_definition()
  54. itemstack:add_wear(65535/(uses-1))
  55. -- tool break sound
  56. if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then
  57. minetest.sound_play(wdef.sound.breaks, {pos = pt.above, gain = 0.5})
  58. end
  59. end
  60. return itemstack
  61. end
  62. -- Register new hoes
  63. farming.register_hoe = function(name, def)
  64. -- Check for : prefix (register new hoes in your mod's namespace)
  65. if name:sub(1,1) ~= ":" then
  66. name = ":" .. name
  67. end
  68. -- Check def table
  69. if def.description == nil then
  70. def.description = "Hoe"
  71. end
  72. if def.inventory_image == nil then
  73. def.inventory_image = "unknown_item.png"
  74. end
  75. if def.recipe == nil then
  76. def.recipe = {
  77. {"air","air",""},
  78. {"","group:stick",""},
  79. {"","group:stick",""}
  80. }
  81. end
  82. if def.max_uses == nil then
  83. def.max_uses = 30
  84. end
  85. -- Register the tool
  86. minetest.register_tool(name, {
  87. description = def.description,
  88. inventory_image = def.inventory_image,
  89. on_use = function(itemstack, user, pointed_thing)
  90. return farming.hoe_on_use(itemstack, user, pointed_thing, def.max_uses)
  91. end,
  92. groups = def.groups,
  93. sound = {breaks = "default_tool_breaks"},
  94. })
  95. -- Register its recipe
  96. if def.material == nil then
  97. minetest.register_craft({
  98. output = name:sub(2),
  99. recipe = def.recipe
  100. })
  101. else
  102. minetest.register_craft({
  103. output = name:sub(2),
  104. recipe = {
  105. {def.material, def.material, ""},
  106. {"", "group:stick", ""},
  107. {"", "group:stick", ""}
  108. }
  109. })
  110. -- Reverse Recipe
  111. minetest.register_craft({
  112. output = name:sub(2),
  113. recipe = {
  114. {"", def.material, def.material},
  115. {"", "group:stick", ""},
  116. {"", "group:stick", ""}
  117. }
  118. })
  119. end
  120. end
  121. -- how often node timers for plants will tick, +/- some random value
  122. local function tick(pos)
  123. minetest.get_node_timer(pos):start(math.random(166, 286))
  124. end
  125. -- how often a growth failure tick is retried (e.g. too dark)
  126. local function tick_again(pos)
  127. minetest.get_node_timer(pos):start(math.random(40, 80))
  128. end
  129. -- Seed placement
  130. farming.place_seed = function(itemstack, placer, pointed_thing, plantname)
  131. local pt = pointed_thing
  132. -- check if pointing at a node
  133. if not pt then
  134. return itemstack
  135. end
  136. if pt.type ~= "node" then
  137. return itemstack
  138. end
  139. local under = minetest.get_node(pt.under)
  140. local above = minetest.get_node(pt.above)
  141. if minetest.is_protected(pt.under, placer:get_player_name()) then
  142. minetest.record_protection_violation(pt.under, placer:get_player_name())
  143. return
  144. end
  145. if minetest.is_protected(pt.above, placer:get_player_name()) then
  146. minetest.record_protection_violation(pt.above, placer:get_player_name())
  147. return
  148. end
  149. -- return if any of the nodes is not registered
  150. if not minetest.registered_nodes[under.name] then
  151. return itemstack
  152. end
  153. if not minetest.registered_nodes[above.name] then
  154. return itemstack
  155. end
  156. -- check if pointing at the top of the node
  157. if pt.above.y ~= pt.under.y+1 then
  158. return itemstack
  159. end
  160. -- check if you can replace the node above the pointed node
  161. if not minetest.registered_nodes[above.name].buildable_to then
  162. return itemstack
  163. end
  164. -- check if pointing at soil
  165. if minetest.get_item_group(under.name, "soil") < 2 then
  166. return itemstack
  167. end
  168. -- add the node and remove 1 item from the itemstack
  169. minetest.add_node(pt.above, {name = plantname, param2 = 1})
  170. tick(pt.above)
  171. if not (creative and creative.is_enabled_for
  172. and creative.is_enabled_for(placer:get_player_name())) then
  173. itemstack:take_item()
  174. end
  175. return itemstack
  176. end
  177. farming.grow_plant = function(pos, elapsed)
  178. local node = minetest.get_node(pos)
  179. local name = node.name
  180. local def = minetest.registered_nodes[name]
  181. if not def.next_plant then
  182. -- disable timer for fully grown plant
  183. return
  184. end
  185. -- grow seed
  186. if minetest.get_item_group(node.name, "seed") and def.fertility then
  187. local soil_node = minetest.get_node_or_nil({x = pos.x, y = pos.y - 1, z = pos.z})
  188. if not soil_node then
  189. tick_again(pos)
  190. return
  191. end
  192. -- omitted is a check for light, we assume seeds can germinate in the dark.
  193. for _, v in pairs(def.fertility) do
  194. if minetest.get_item_group(soil_node.name, v) ~= 0 then
  195. local placenode = {name = def.next_plant}
  196. if def.place_param2 then
  197. placenode.param2 = def.place_param2
  198. end
  199. minetest.swap_node(pos, placenode)
  200. if minetest.registered_nodes[def.next_plant].next_plant then
  201. tick(pos)
  202. return
  203. end
  204. end
  205. end
  206. return
  207. end
  208. -- check if on wet soil
  209. local below = minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z})
  210. if minetest.get_item_group(below.name, "soil") < 3 then
  211. tick_again(pos)
  212. return
  213. end
  214. -- check light
  215. local light = minetest.get_node_light(pos)
  216. if not light or light < def.minlight or light > def.maxlight then
  217. tick_again(pos)
  218. return
  219. end
  220. -- grow
  221. local placenode = {name = def.next_plant}
  222. if def.place_param2 then
  223. placenode.param2 = def.place_param2
  224. end
  225. minetest.swap_node(pos, placenode)
  226. -- new timer needed?
  227. if minetest.registered_nodes[def.next_plant].next_plant then
  228. tick(pos)
  229. end
  230. return
  231. end
  232. -- Register plants
  233. farming.register_plant = function(name, def)
  234. local mname = name:split(":")[1]
  235. local pname = name:split(":")[2]
  236. -- Check def table
  237. if not def.description then
  238. def.description = "Seed"
  239. end
  240. if not def.inventory_image then
  241. def.inventory_image = "unknown_item.png"
  242. end
  243. if not def.steps then
  244. return nil
  245. end
  246. if not def.minlight then
  247. def.minlight = 1
  248. end
  249. if not def.maxlight then
  250. def.maxlight = 14
  251. end
  252. if not def.fertility then
  253. def.fertility = {}
  254. end
  255. farming.registered_plants[pname] = def
  256. -- Register seed
  257. local lbm_nodes = {mname .. ":seed_" .. pname}
  258. local g = {seed = 1, snappy = 3, attached_node = 1, flammable = 2}
  259. for k, v in pairs(def.fertility) do
  260. g[v] = 1
  261. end
  262. minetest.register_node(":" .. mname .. ":seed_" .. pname, {
  263. description = def.description,
  264. tiles = {def.inventory_image},
  265. inventory_image = def.inventory_image,
  266. wield_image = def.inventory_image,
  267. drawtype = "signlike",
  268. groups = g,
  269. paramtype = "light",
  270. paramtype2 = "wallmounted",
  271. place_param2 = def.place_param2 or nil, -- this isn't actually used for placement
  272. walkable = false,
  273. sunlight_propagates = true,
  274. selection_box = {
  275. type = "fixed",
  276. fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5},
  277. },
  278. fertility = def.fertility,
  279. sounds = default.node_sound_dirt_defaults({
  280. dig = {name = "", gain = 0},
  281. dug = {name = "default_grass_footstep", gain = 0.2},
  282. place = {name = "default_place_node", gain = 0.25},
  283. }),
  284. on_place = function(itemstack, placer, pointed_thing)
  285. local under = pointed_thing.under
  286. local node = minetest.get_node(under)
  287. local udef = minetest.registered_nodes[node.name]
  288. if udef and udef.on_rightclick and
  289. not (placer and placer:get_player_control().sneak) then
  290. return udef.on_rightclick(under, node, placer, itemstack,
  291. pointed_thing) or itemstack
  292. end
  293. return farming.place_seed(itemstack, placer, pointed_thing, mname .. ":seed_" .. pname)
  294. end,
  295. next_plant = mname .. ":" .. pname .. "_1",
  296. on_timer = farming.grow_plant,
  297. minlight = def.minlight,
  298. maxlight = def.maxlight,
  299. })
  300. -- Register harvest
  301. minetest.register_craftitem(":" .. mname .. ":" .. pname, {
  302. description = pname:gsub("^%l", string.upper),
  303. inventory_image = mname .. "_" .. pname .. ".png",
  304. groups = {flammable = 2},
  305. })
  306. -- Register growing steps
  307. for i = 1, def.steps do
  308. local base_rarity = 1
  309. if def.steps ~= 1 then
  310. base_rarity = 8 - (i - 1) * 7 / (def.steps - 1)
  311. end
  312. local drop = {
  313. items = {
  314. {items = {mname .. ":" .. pname}, rarity = base_rarity},
  315. {items = {mname .. ":" .. pname}, rarity = base_rarity * 2},
  316. {items = {mname .. ":seed_" .. pname}, rarity = base_rarity},
  317. {items = {mname .. ":seed_" .. pname}, rarity = base_rarity * 2},
  318. }
  319. }
  320. local nodegroups = {snappy = 3, flammable = 2, plant = 1, not_in_creative_inventory = 1, attached_node = 1}
  321. nodegroups[pname] = i
  322. local next_plant = nil
  323. if i < def.steps then
  324. next_plant = mname .. ":" .. pname .. "_" .. (i + 1)
  325. lbm_nodes[#lbm_nodes + 1] = mname .. ":" .. pname .. "_" .. i
  326. end
  327. minetest.register_node(":" .. mname .. ":" .. pname .. "_" .. i, {
  328. drawtype = "plantlike",
  329. waving = 1,
  330. tiles = {mname .. "_" .. pname .. "_" .. i .. ".png"},
  331. paramtype = "light",
  332. paramtype2 = def.paramtype2 or nil,
  333. place_param2 = def.place_param2 or nil,
  334. walkable = false,
  335. buildable_to = true,
  336. drop = drop,
  337. selection_box = {
  338. type = "fixed",
  339. fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5},
  340. },
  341. groups = nodegroups,
  342. sounds = default.node_sound_leaves_defaults(),
  343. next_plant = next_plant,
  344. on_timer = farming.grow_plant,
  345. minlight = def.minlight,
  346. maxlight = def.maxlight,
  347. })
  348. end
  349. -- replacement LBM for pre-nodetimer plants
  350. minetest.register_lbm({
  351. name = ":" .. mname .. ":start_nodetimer_" .. pname,
  352. nodenames = lbm_nodes,
  353. action = function(pos, node)
  354. tick_again(pos)
  355. end,
  356. })
  357. -- Return
  358. local r = {
  359. seed = mname .. ":seed_" .. pname,
  360. harvest = mname .. ":" .. pname
  361. }
  362. return r
  363. end