api.lua 10 KB

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