cactus.lua 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. cactus = cactus or {}
  2. cactus.modpath = minetest.get_modpath("default")
  3. cactus.steptime = {min=60, max=60*6}
  4. cactus.plantname = "default:cactus"
  5. cactus.maxheight = 5
  6. cactus.minlight = 13
  7. -- Localize for performance.
  8. local math_floor = math.floor
  9. local math_random = math.random
  10. -- Should return a random height for an individual plant to grow.
  11. function cactus.random_height()
  12. return math_floor(math_random(math_random(2, 3), math_random(3, 5)))
  13. end
  14. function cactus.is_sand_name(name)
  15. if minetest.get_item_group(name, "sand") ~= 0 then
  16. return true
  17. end
  18. end
  19. function cactus.has_sand(pos)
  20. local p = vector.add(pos, {x=0, y=-1, z=0})
  21. local name = minetest.get_node(p).name
  22. -- Must be on sand or grass.
  23. if cactus.is_sand_name(name) then
  24. return true
  25. end
  26. end
  27. function cactus.can_grow(pos)
  28. -- Must have sand nearby.
  29. if not cactus.has_sand(pos) then
  30. return
  31. end
  32. local positions = {
  33. {x=pos.x+1, y=pos.y, z=pos.z},
  34. {x=pos.x-1, y=pos.y, z=pos.z},
  35. {x=pos.x, y=pos.y, z=pos.z+1},
  36. {x=pos.x, y=pos.y, z=pos.z-1},
  37. }
  38. local air = 0
  39. for k, v in ipairs(positions) do
  40. if minetest.get_node(v).name == "air" then
  41. air = air + 1
  42. end
  43. end
  44. -- If cactus is tightly packed (not enough air) then cannot grow.
  45. if air < 2 then
  46. return
  47. end
  48. -- No check for water.
  49. return true
  50. end
  51. -- Obtain growth height from soil, initializing it if not done yet.
  52. function cactus.get_grow_height(pos)
  53. local meta = minetest.get_meta({x=pos.x, y=pos.y-1, z=pos.z})
  54. local maxh = meta:get_int("cactus_height")
  55. if maxh == 0 then
  56. maxh = cactus.random_height()
  57. meta:set_int("cactus_height", maxh)
  58. end
  59. return maxh
  60. end
  61. -- Should be called when plant is dug.
  62. function cactus.reset_grow_height_and_timer(pos)
  63. -- Find soil node below plant.
  64. local p = vector.new(pos)
  65. local name = minetest.get_node(p).name
  66. local d = 0
  67. while not cactus.is_sand_name(name) and d < cactus.maxheight do
  68. -- All except bottom-most node must be plant.
  69. if name ~= cactus.plantname then
  70. return
  71. end
  72. p.y = p.y - 1
  73. d = d + 1
  74. name = minetest.get_node(p).name
  75. end
  76. -- Must be on sand.
  77. if cactus.is_sand_name(name) then
  78. local meta = minetest.get_meta(p)
  79. local maxh = cactus.random_height()
  80. meta:set_int("cactus_height", maxh)
  81. else
  82. return
  83. end
  84. -- Restart timer for plant directly above soil.
  85. p.y = p.y + 1
  86. if minetest.get_node(p).name ~= cactus.plantname then
  87. return
  88. end
  89. local min = cactus.steptime.min
  90. local max = cactus.steptime.max
  91. minetest.get_node_timer(p):start(math_random(min, max))
  92. end
  93. -- Attempt to grow cactus.
  94. -- Return 0 means nothing to report.
  95. -- 10 means plant has reached max height.
  96. -- 11 means plant is rotated wrong.
  97. -- 12 means plant cannot grow because of ice.
  98. function cactus.grow(pos, node)
  99. -- Check if we can grow.
  100. if not cactus.can_grow(pos) then
  101. return 0
  102. end
  103. if node.param2 >= 4 then
  104. return 11
  105. end
  106. if minetest.find_node_near(pos, 2, "group:cold") then
  107. return 12
  108. end
  109. -- Get how high we can grow.
  110. local maxh = cactus.get_grow_height(pos)
  111. -- Find current height of plant.
  112. local height = 0
  113. while node.name == cactus.plantname and height < maxh do
  114. height = height + 1
  115. pos.y = pos.y + 1
  116. node = minetest.get_node(pos)
  117. end
  118. if height >= maxh then
  119. -- Plant has reached max height.
  120. return 10
  121. end
  122. -- Check if we have room to grow some more.
  123. if node.name ~= "air" then
  124. return 0
  125. end
  126. -- Check if we have enough light.
  127. if minetest.get_node_light(pos) < cactus.minlight then
  128. return 0
  129. end
  130. -- Grow!
  131. minetest.add_node(pos, {name = cactus.plantname})
  132. return 0
  133. end
  134. function cactus.on_construct(pos)
  135. -- Only the ground-level plant piece should have nodetimer.
  136. -- If plant is not placed on soil, it will never have nodetimer.
  137. if cactus.has_sand(pos) then
  138. local min = cactus.steptime.min
  139. local max = cactus.steptime.max
  140. minetest.get_node_timer(pos):start(math_random(min, max))
  141. end
  142. end
  143. function cactus.on_destruct(pos)
  144. cactus.reset_grow_height_and_timer(pos)
  145. end
  146. function cactus.on_timer(pos, elapsed)
  147. --minetest.chat_send_all("# Server: Plant timer @ " .. minetest.pos_to_string(pos) .. "!")
  148. local node = minetest.get_node(pos)
  149. local result = cactus.grow(pos, node)
  150. -- Plant has reached max height.
  151. if result == 10 then return end
  152. -- Plant is rotated wrong.
  153. if result == 11 then return end
  154. -- Plant cannot grow because of ice.
  155. if result == 12 then return end
  156. return true
  157. end
  158. function cactus.after_dig_node(pos, node, metadata, digger)
  159. if node.param2 >= 4 then
  160. return
  161. end
  162. default.dig_up(pos, node, digger)
  163. -- No return value.
  164. end
  165. if not cactus.run_once then
  166. local c = "cactus:core"
  167. local f = cactus.modpath .. "/cactus.lua"
  168. reload.register_file(c, f, false)
  169. cactus.run_once = true
  170. end