api.lua 14 KB

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