flame_staff.lua 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. local function get_midfeld_spawn(realm)
  2. local tries = 0
  3. ::try_again::
  4. tries = tries + 1
  5. local pos = {
  6. x = math.random(realm.gate_minp.x, realm.gate_maxp.x),
  7. y = math.random(realm.gate_minp.y, realm.gate_maxp.y),
  8. z = math.random(realm.gate_minp.z, realm.gate_maxp.z),
  9. }
  10. -- Need to load the area otherwise 'find_node_near' will usually fail.
  11. minetest.load_area(
  12. vector.add(pos, {x=-16, y=-16, z=-16}),
  13. vector.add(pos, {x=16, y=16, z=16}))
  14. pos = minetest.find_node_near(pos, 16, "air", true)
  15. if pos and minetest.get_node(vector.add(pos, {x=0, y=1, z=0})).name == "air" then
  16. local minp = realm.minp
  17. local maxp = realm.maxp
  18. if pos.x >= minp.x and pos.x <= maxp.x and
  19. pos.y >= minp.y and pos.y <= maxp.y and
  20. pos.z >= minp.z and pos.z <= maxp.z then
  21. return pos
  22. end
  23. end
  24. if tries < 10 then
  25. goto try_again
  26. end
  27. return realm.realm_origin
  28. end
  29. function obsidian_gateway.get_midfeld_spawn()
  30. local realm = rc.get_realm_data("midfeld")
  31. return get_midfeld_spawn(realm)
  32. end
  33. function obsidian_gateway.on_flamestaff_use(item, user, pt)
  34. if pt.type ~= "node" then
  35. return
  36. end
  37. if not user:is_player() then
  38. return
  39. end
  40. local pname = user:get_player_name()
  41. ambiance.sound_play("fire_flint_and_steel", pt.above, 0.7, 10)
  42. -- Punched node must be one of these.
  43. do
  44. local nn = minetest.get_node(pt.under).name
  45. if nn ~= "default:obsidian" and
  46. nn ~= "griefer:grieferstone" and
  47. nn ~= "cavestuff:dark_obsidian" and
  48. nn ~= "cavestuff:glow_obsidian" then
  49. return
  50. end
  51. end
  52. local pos = pt.under
  53. local success, origin, airpoints, northsouth, ns_key, playerorigin =
  54. obsidian_gateway.find_gate(pos)
  55. if not success then
  56. return
  57. end
  58. -- Using the staff on an uninitialized gate does nothing.
  59. do
  60. local meta = minetest.get_meta(origin)
  61. local target = meta:get_string("obsidian_gateway_destination_" .. ns_key)
  62. local dest = minetest.string_to_pos(target)
  63. if not dest then
  64. return
  65. end
  66. end
  67. -- Add/update sound beacon.
  68. ambiance.spawn_sound_beacon("soundbeacon:gate", origin, 20, 1)
  69. ambiance.replay_nearby_sound_beacons(origin, 6)
  70. if sheriff.is_cheater(pname) then
  71. if sheriff.punish_probability(pname) then
  72. sheriff.punish_player(pname)
  73. return
  74. end
  75. end
  76. local do_effect = false
  77. do
  78. local meta = item:get_meta()
  79. if meta:get_string("gate1") == "" then
  80. -- Punch first gate.
  81. meta:set_string("gate1", minetest.pos_to_string(playerorigin))
  82. meta:set_string("description", "Flame Staff (Partially Linked)")
  83. meta:set_string("owner", pname)
  84. do_effect = true
  85. elseif meta:get_string("gate2") == "" then
  86. -- Punch second gate.
  87. if meta:get_string("owner") ~= pname then
  88. return
  89. end
  90. meta:set_string("gate2", minetest.pos_to_string(playerorigin))
  91. local info = "\n" ..
  92. rc.pos_to_namestr(minetest.string_to_pos(meta:get_string("gate1"))) .. "\n" ..
  93. rc.pos_to_namestr(minetest.string_to_pos(meta:get_string("gate2")))
  94. meta:set_string("description", "Flame Staff (Linked)" .. info)
  95. item:set_wear(1)
  96. do_effect = true
  97. end
  98. end
  99. if do_effect then
  100. local p = pt.above
  101. minetest.sound_play("default_item_smoke", {
  102. pos = pos,
  103. max_hear_distance = 8,
  104. }, true)
  105. minetest.add_particlespawner({
  106. amount = 3,
  107. time = 0.1,
  108. minpos = {x = p.x - 0.1, y = p.y + 0.1, z = p.z - 0.1 },
  109. maxpos = {x = p.x + 0.1, y = p.y + 0.2, z = p.z + 0.1 },
  110. minvel = {x = 0, y = 2.5, z = 0},
  111. maxvel = {x = 0, y = 2.5, z = 0},
  112. minacc = {x = -0.15, y = -0.02, z = -0.15},
  113. maxacc = {x = 0.15, y = -0.01, z = 0.15},
  114. minexptime = 4,
  115. maxexptime = 6,
  116. minsize = 5,
  117. maxsize = 5,
  118. collisiondetection = true,
  119. texture = "default_item_smoke.png"
  120. })
  121. end
  122. -- Success, but do not continue to the teleport logic.
  123. if do_effect then
  124. return item
  125. end
  126. -- If user is not staff owner, do nothing. But if staff doesn't yet have an
  127. -- owner (old staff), then set owner to first user.
  128. do
  129. local meta = item:get_meta()
  130. local owner = meta:get_string("owner")
  131. if owner == "" then
  132. owner = pname
  133. meta:set_string("owner", pname)
  134. -- Meta changes do not take effect until we return the itemstack.
  135. end
  136. if owner ~= pname then
  137. return
  138. end
  139. end
  140. -- Player must be standing in the gate.
  141. do
  142. local posunder = utility.node_under_pos(user:get_pos())
  143. local namunder = minetest.get_node(posunder).name
  144. if namunder ~= "default:obsidian" and
  145. namunder ~= "griefer:grieferstone" and
  146. namunder ~= "cavestuff:dark_obsidian" and
  147. namunder ~= "cavestuff:glow_obsidian" then
  148. return
  149. end
  150. end
  151. do
  152. local meta = item:get_meta()
  153. local spos1 = meta:get_string("gate1")
  154. local spos2 = meta:get_string("gate2")
  155. if spos1 == "" or spos2 == "" then
  156. return
  157. end
  158. local tar1 = minetest.string_to_pos(spos1)
  159. local tar2 = minetest.string_to_pos(spos2)
  160. if not tar1 or not tar2 then
  161. return
  162. end
  163. -- If the linked targets are the same, nothing happens.
  164. if vector.equals(tar1, tar2) then
  165. return
  166. end
  167. -- If either target is in a realm which disallows incoming gates ...
  168. do
  169. local d1 = rc.get_realm_data(rc.current_realm_at_pos(tar1)) or {}
  170. local d2 = rc.get_realm_data(rc.current_realm_at_pos(tar2)) or {}
  171. if d1.disabled or d2.disabled then
  172. return
  173. end
  174. end
  175. -- If player activates gate #2 first, then pretend we didn't notice and just
  176. -- swap the datums.
  177. if vector.equals(playerorigin, tar2) then
  178. local tmp
  179. tmp = tar1
  180. tar1 = tar2
  181. tar2 = tmp
  182. meta:set_string("gate1", spos2)
  183. meta:set_string("gate2", spos1)
  184. tmp = spos1
  185. spos1 = spos2
  186. spos2 = tmp
  187. -- Meta changes do not take effect until we return the itemstack.
  188. end
  189. -- Don't allow player to teleport into Void, we cannot have chunks generated
  190. -- there!
  191. if not rc.is_valid_realm_pos(tar1) then
  192. return
  193. end
  194. if not rc.is_valid_realm_pos(tar2) then
  195. return
  196. end
  197. -- Just in case someone managed to link a flamestaff to the Outback gate.
  198. if rc.current_realm_at_pos(tar1) == "abyss" or rc.current_realm_at_pos(tar2) == "abyss" then
  199. return
  200. end
  201. -- Player has activated gate #1.
  202. -- Send them to the MIDFELD!
  203. if vector.equals(playerorigin, tar1) then
  204. local realmdata = rc.get_realm_data("midfeld")
  205. assert(realmdata)
  206. local hidden_spawn = get_midfeld_spawn(realmdata)
  207. preload_tp.execute({
  208. player_name = pname,
  209. target_position = hidden_spawn,
  210. emerge_radius = 8,
  211. particle_effects = true,
  212. spinup_time = 2,
  213. pre_teleport_callback = function()
  214. -- Need better sound someday.
  215. --coresounds.play_death_sound(user, pname)
  216. end,
  217. post_teleport_callback = function()
  218. portal_sickness.on_use_portal(pname)
  219. end,
  220. teleport_sound = "nether_portal_usual",
  221. send_blocks = true,
  222. })
  223. -- The metadata will have been updated if we swapped datums.
  224. ambiance.sound_play("fire_flint_and_steel", pos, 0.7, 20)
  225. item:add_wear_by_uses(20)
  226. return item
  227. end
  228. -- Player has activated a gate in Midfeld. Send then back.
  229. -- Also destroy the flame staff.
  230. if rc.current_realm_at_pos(origin) == "midfeld" then
  231. preload_tp.execute({
  232. player_name = pname,
  233. target_position = tar2,
  234. emerge_radius = 16,
  235. particle_effects = true,
  236. spinup_time = 2,
  237. pre_teleport_callback = function()
  238. obsidian_gateway.regenerate_liquid(tar2, nil)
  239. end,
  240. post_teleport_callback = function()
  241. -- Only check for portal sickness ONCE!
  242. -- Already checked on first gate use.
  243. --portal_sickness.on_use_portal(pname)
  244. end,
  245. teleport_sound = "nether_portal_usual",
  246. send_blocks = true,
  247. })
  248. ambiance.sound_play("fire_flint_and_steel", pos, 0.7, 20)
  249. item:add_wear_by_uses(20)
  250. return item
  251. end
  252. -- Implicit is this: if player uses a linked flamestaff on a third-party
  253. -- gate located NOT in MIDFELD, then nothing shall happen. Stuff happens
  254. -- only if player uses flamestaff on gate #1, #2, or any gate in MIDFELD.
  255. end
  256. end