spawner.lua 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. local S = mobs.translate
  2. -- are we a real player ?
  3. local function is_player(player)
  4. if player and type(player) == "userdata" and minetest.is_player(player) then
  5. return true
  6. end
  7. end
  8. -- mob spawner
  9. local spawner_default = "mobs_animal:pumba 10 15 0 0 0"
  10. minetest.register_node("mobs:spawner", {
  11. tiles = {"mob_spawner.png"},
  12. drawtype = "glasslike",
  13. paramtype = "light",
  14. walkable = true,
  15. description = S("Mob Spawner"),
  16. groups = {cracky = 1},
  17. on_construct = function(pos)
  18. local meta = minetest.get_meta(pos)
  19. -- setup formspec
  20. local head = S("(mob name) (min light) (max light) (amount)"
  21. .. " (player distance) (Y offset)")
  22. -- text entry formspec
  23. meta:set_string("formspec", "size[10,3.5]"
  24. .. "label[0.15,0.5;" .. minetest.formspec_escape(head) .. "]"
  25. .. "field[1,2.5;8.5,0.8;text;" .. S("Command:")
  26. .. ";${command}]")
  27. meta:set_string("infotext", S("Spawner Not Active (enter settings)"))
  28. meta:set_string("command", spawner_default)
  29. end,
  30. on_right_click = function(pos, placer)
  31. if minetest.is_protected(pos, placer:get_player_name()) then
  32. return
  33. end
  34. end,
  35. on_receive_fields = function(pos, formname, fields, sender)
  36. if not fields.text or fields.text == "" then
  37. return
  38. end
  39. local meta = minetest.get_meta(pos)
  40. local comm = fields.text:split(" ")
  41. local name = sender:get_player_name()
  42. if minetest.is_protected(pos, name) then
  43. minetest.record_protection_violation(pos, name)
  44. return
  45. end
  46. local mob = comm[1] -- mob to spawn
  47. local mlig = tonumber(comm[2]) -- min light
  48. local xlig = tonumber(comm[3]) -- max light
  49. local num = tonumber(comm[4]) -- total mobs in area
  50. local pla = tonumber(comm[5]) -- player distance (0 to disable)
  51. local yof = tonumber(comm[6]) or 0 -- Y offset to spawn mob
  52. if mob and mob ~= "" and mobs.spawning_mobs[mob]
  53. and num and num >= 0 and num <= 10
  54. and mlig and mlig >= 0 and mlig <= 15
  55. and xlig and xlig >= 0 and xlig <= 15
  56. and pla and pla >= 0 and pla <= 20
  57. and yof and yof > -10 and yof < 10 then
  58. meta:set_string("command", fields.text)
  59. meta:set_string("infotext", S("Spawner Active (@1)", mob))
  60. else
  61. minetest.chat_send_player(name, S("Mob Spawner settings failed!"))
  62. minetest.chat_send_player(name,
  63. S("Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] player_distance[1-20] y_offset[-10 to 10]”"))
  64. end
  65. end
  66. })
  67. local max_per_block = tonumber(minetest.settings:get("max_objects_per_block") or 99)
  68. -- spawner abm
  69. minetest.register_abm({
  70. label = "Mob spawner node",
  71. nodenames = {"mobs:spawner"},
  72. interval = 10,
  73. chance = 4,
  74. catch_up = false,
  75. action = function(pos, node, active_object_count, active_object_count_wider)
  76. -- return if too many entities already
  77. if active_object_count_wider >= max_per_block then
  78. return
  79. end
  80. -- get meta and command
  81. local meta = minetest.get_meta(pos)
  82. local comm = meta:get_string("command"):split(" ")
  83. -- get settings from command
  84. local mob = comm[1]
  85. local mlig = tonumber(comm[2])
  86. local xlig = tonumber(comm[3])
  87. local num = tonumber(comm[4])
  88. local pla = tonumber(comm[5]) or 0
  89. local yof = tonumber(comm[6]) or 0
  90. -- if amount is 0 then do nothing
  91. if num == 0 then
  92. return
  93. end
  94. -- are we spawning a registered mob?
  95. if not mobs.spawning_mobs[mob] then
  96. --print ("--- mob doesn't exist", mob)
  97. return
  98. end
  99. -- check objects inside 9x9 area around spawner
  100. local objs = minetest.get_objects_inside_radius(pos, 9)
  101. local count = 0
  102. local ent
  103. -- count mob objects of same type in area
  104. for _, obj in ipairs(objs) do
  105. ent = obj:get_luaentity()
  106. if ent and ent.name and ent.name == mob then
  107. count = count + 1
  108. end
  109. end
  110. -- is there too many of same type?
  111. if count >= num then
  112. return
  113. end
  114. -- spawn mob if player detected and in range
  115. if pla > 0 then
  116. local in_range = 0
  117. local objsp = minetest.get_objects_inside_radius(pos, pla)
  118. for _, oir in pairs(objsp) do
  119. if is_player(oir) then
  120. in_range = 1
  121. break
  122. end
  123. end
  124. -- player not found
  125. if in_range == 0 then
  126. return
  127. end
  128. end
  129. -- set medium mob usually spawns in (defaults to air)
  130. local reg = minetest.registered_entities[mob].fly_in
  131. if not reg or type(reg) == "string" then
  132. reg = {(reg or "air")}
  133. end
  134. -- find air blocks within 5 nodes of spawner
  135. local air = minetest.find_nodes_in_area(
  136. {x = pos.x - 5, y = pos.y + yof, z = pos.z - 5},
  137. {x = pos.x + 5, y = pos.y + yof, z = pos.z + 5}, reg)
  138. -- spawn in random air block
  139. if air and #air > 0 then
  140. local pos2 = air[math.random(#air)]
  141. local lig = minetest.get_node_light(pos2) or 0
  142. pos2.y = pos2.y + 0.5
  143. -- only if light levels are within range
  144. if lig >= mlig and lig <= xlig
  145. and minetest.registered_entities[mob] then
  146. minetest.add_entity(pos2, mob)
  147. end
  148. end
  149. end
  150. })