spawner.lua 4.3 KB

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