init.lua 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. obsidian_gateway = obsidian_gateway or {}
  2. obsidian_gateway.modpath = minetest.get_modpath("obsidian_gateway")
  3. -- Localize for performance.
  4. local vector_distance = vector.distance
  5. local vector_round = vector.round
  6. local math_random = math.random
  7. -- Gateway schematic.
  8. local o = {"default:obsidian", "griefer:grieferstone", "cavestuff:dark_obsidian", "cavestuff:glow_obsidian"}
  9. local a = function(name)
  10. -- Bones check needed here to prevent players from blocking a gate by dying in
  11. -- it. E.g., bones never decay while a hacker is online.
  12. if name == "air" or name == "bones:bones" then
  13. return true
  14. end
  15. if name == "nether:portal_liquid" or name == "nether:portal_hidden" then
  16. return true
  17. end
  18. end
  19. local gate_northsouth = {
  20. data = {
  21. {p={x=0, y=0, z=0}, n=o},
  22. {p={x=1, y=0, z=0}, n=o},
  23. {p={x=2, y=0, z=0}, n=o},
  24. {p={x=3, y=0, z=0}, n=o},
  25. {p={x=0, y=1, z=0}, n=o},
  26. {p={x=0, y=2, z=0}, n=o},
  27. {p={x=0, y=3, z=0}, n=o},
  28. {p={x=0, y=4, z=0}, n=o},
  29. {p={x=1, y=4, z=0}, n=o},
  30. {p={x=2, y=4, z=0}, n=o},
  31. {p={x=3, y=4, z=0}, n=o},
  32. {p={x=3, y=1, z=0}, n=o},
  33. {p={x=3, y=2, z=0}, n=o},
  34. {p={x=3, y=3, z=0}, n=o},
  35. {p={x=1, y=1, z=0}, n=a},
  36. {p={x=2, y=1, z=0}, n=a},
  37. {p={x=1, y=2, z=0}, n=a},
  38. {p={x=2, y=2, z=0}, n=a},
  39. {p={x=1, y=3, z=0}, n=a},
  40. {p={x=2, y=3, z=0}, n=a},
  41. },
  42. minp = {x=0, y=0, z=0},
  43. maxp = {x=3, y=4, z=0},
  44. }
  45. local gate_eastwest = {
  46. data = {
  47. {p={x=0, y=0, z=0}, n=o},
  48. {p={x=0, y=0, z=1}, n=o},
  49. {p={x=0, y=0, z=2}, n=o},
  50. {p={x=0, y=0, z=3}, n=o},
  51. {p={x=0, y=1, z=0}, n=o},
  52. {p={x=0, y=2, z=0}, n=o},
  53. {p={x=0, y=3, z=0}, n=o},
  54. {p={x=0, y=4, z=0}, n=o},
  55. {p={x=0, y=4, z=1}, n=o},
  56. {p={x=0, y=4, z=2}, n=o},
  57. {p={x=0, y=4, z=3}, n=o},
  58. {p={x=0, y=1, z=3}, n=o},
  59. {p={x=0, y=2, z=3}, n=o},
  60. {p={x=0, y=3, z=3}, n=o},
  61. {p={x=0, y=1, z=1}, n=a},
  62. {p={x=0, y=1, z=2}, n=a},
  63. {p={x=0, y=2, z=1}, n=a},
  64. {p={x=0, y=2, z=2}, n=a},
  65. {p={x=0, y=3, z=1}, n=a},
  66. {p={x=0, y=3, z=2}, n=a},
  67. },
  68. minp = {x=0, y=0, z=0},
  69. maxp = {x=0, y=4, z=3},
  70. }
  71. obsidian_gateway.gate_ns_data = gate_northsouth
  72. obsidian_gateway.gate_ew_data = gate_eastwest
  73. -- Quickly check for protection in an area.
  74. local function check_protection(pos, radius)
  75. -- How much beyond the radius to check for protections.
  76. local e = 3
  77. local minp = vector.new(pos.x-(radius+e), pos.y-(radius+e), pos.z-(radius+e))
  78. local maxp = vector.new(pos.x+(radius+e), pos.y+(radius+e), pos.z+(radius+e))
  79. -- Step size, to avoid checking every single node.
  80. -- This assumes protections cannot be smaller than this size.
  81. local ss = 3
  82. local check = minetest.test_protection
  83. for x=minp.x, maxp.x, ss do
  84. for y=minp.y, maxp.y, ss do
  85. for z=minp.z, maxp.z, ss do
  86. if check({x=x, y=y, z=z}, "") then
  87. -- Protections are present.
  88. return true
  89. end
  90. end
  91. end
  92. end
  93. -- Nothing in the area is protected.
  94. return false
  95. end
  96. -- Get a list of node positions inside the gate's frame.
  97. function obsidian_gateway.door_positions(origin, northsouth)
  98. local airpoints
  99. if northsouth then
  100. local o = origin
  101. airpoints = {
  102. {x=o.x+1, y=o.y+1, z=o.z+0},
  103. {x=o.x+2, y=o.y+1, z=o.z+0},
  104. {x=o.x+1, y=o.y+2, z=o.z+0},
  105. {x=o.x+2, y=o.y+2, z=o.z+0},
  106. {x=o.x+1, y=o.y+3, z=o.z+0},
  107. {x=o.x+2, y=o.y+3, z=o.z+0},
  108. }
  109. else
  110. local o = origin
  111. airpoints = {
  112. {x=o.x+0, y=o.y+1, z=o.z+1},
  113. {x=o.x+0, y=o.y+1, z=o.z+2},
  114. {x=o.x+0, y=o.y+2, z=o.z+1},
  115. {x=o.x+0, y=o.y+2, z=o.z+2},
  116. {x=o.x+0, y=o.y+3, z=o.z+1},
  117. {x=o.x+0, y=o.y+3, z=o.z+2},
  118. }
  119. end
  120. return airpoints
  121. end
  122. function obsidian_gateway.spawn_liquid(origin, northsouth, returngate, force)
  123. local color = 6
  124. local rotation = 1
  125. local str_origin = minetest.pos_to_string(origin)
  126. if northsouth then
  127. rotation = 0
  128. end
  129. if returngate then
  130. color = 5
  131. end
  132. -- Node's drawtype is "colorfacedir".
  133. local node = {
  134. name = "nether:portal_liquid",
  135. param2 = (color * 32 + rotation),
  136. }
  137. local vadd = vector.add
  138. local airpoints = obsidian_gateway.door_positions(origin, northsouth)
  139. local spawned = false
  140. local count = #airpoints
  141. for k = 1, count, 1 do
  142. local p = airpoints[k]
  143. local oldnode = minetest.get_node(p)
  144. -- Make sure node to be replaced is air. If we were to overwrite any
  145. -- existing portal liquid, that would cause callbacks to run, which would
  146. -- interfere with what we're doing here.
  147. if oldnode.name == "air" then
  148. -- Run 'on_construct' callbacks, etc.
  149. minetest.set_node(p, node)
  150. -- This tells the particle code (inside node's on_timer) what color to use.
  151. local meta = minetest.get_meta(p)
  152. if returngate then
  153. meta:set_string("color", "red")
  154. else
  155. meta:set_string("color", "gold")
  156. end
  157. meta:set_string("gate_origin", str_origin)
  158. meta:set_string("gate_northsouth", tostring(northsouth))
  159. spawned = true
  160. elseif force and oldnode.name == "nether:portal_hidden" then
  161. minetest.swap_node(p, node)
  162. -- Manually execute callback.
  163. local ndef = minetest.registered_nodes[node.name]
  164. ndef.on_construct(p)
  165. -- Do not need to set "color" meta here, the hidden node already has it.
  166. -- Same with 'gate_origin'.
  167. spawned = true
  168. end
  169. end
  170. if spawned then
  171. ambiance.sound_play("nether_portal_ignite", origin, 1.0, 64)
  172. end
  173. end
  174. -- Determine whether the gateway has active portal liquid.
  175. function obsidian_gateway.have_liquid(origin, northsouth)
  176. local airpoints = obsidian_gateway.door_positions(origin, northsouth)
  177. local total = 0
  178. local count = #airpoints
  179. for k = 1, count, 1 do
  180. local p = airpoints[k]
  181. local node = minetest.get_node(p)
  182. if node.name == "nether:portal_liquid" then
  183. total = total + 1
  184. end
  185. end
  186. return (total == 6)
  187. end
  188. -- Get gate's origin and northsouth/eastwest orientation.
  189. function obsidian_gateway.get_origin_and_dir(pos)
  190. local meta = minetest.get_meta(pos)
  191. local str_origin = meta:get_string("gate_origin")
  192. local str_northsouth = meta:get_string("gate_northsouth")
  193. local origin = minetest.string_to_pos(str_origin)
  194. if origin then
  195. -- Returns origin, true/false.
  196. return origin, (str_northsouth == "true")
  197. end
  198. end
  199. function obsidian_gateway.remove_liquid(pos, points)
  200. local node = {name="air"}
  201. local removed = false
  202. local count = #points
  203. for k = 1, count, 1 do
  204. local v = points[k]
  205. local n = minetest.get_node(v)
  206. if n.name == "nether:portal_liquid" then
  207. -- Must use 'swap_node' to avoid triggering further callbacks on the
  208. -- portal liquid node (and nearby portal liquid nodes).
  209. minetest.swap_node(v, node)
  210. removed = true
  211. elseif n.name == "nether:portal_hidden" then
  212. minetest.swap_node(v, node)
  213. -- Node is hidden, so do not set 'removed' flag (removal makes no sound).
  214. end
  215. end
  216. if removed then
  217. ambiance.sound_play("nether_portal_extinguish", pos, 1.0, 64)
  218. end
  219. end
  220. function obsidian_gateway.regenerate_liquid(target, northsouth, isret)
  221. local success, so, ap, ns, key, po =
  222. obsidian_gateway.find_gate(target, northsouth)
  223. -- Spawn portal liquid only if there is a gate here with the expected
  224. -- orientation. Force liquid placement over hidden portal nodes.
  225. if success then
  226. obsidian_gateway.spawn_liquid(so, ns, isret, true)
  227. end
  228. end
  229. function obsidian_gateway.find_gate(pos, require_ns)
  230. local result
  231. local points
  232. local counts
  233. local origin
  234. local northsouth
  235. local ns_key
  236. local playerorigin
  237. -- Find the gateway (threshold under player)!
  238. result, points, counts, origin =
  239. schematic_find.detect_schematic(pos, gate_northsouth)
  240. northsouth = true
  241. ns_key = "ns"
  242. if result then
  243. playerorigin = vector.add(origin, {x=1, y=1, z=0})
  244. end
  245. if not result then
  246. -- Couldn't find northsouth gateway, so try to find eastwest.
  247. result, points, counts, origin =
  248. schematic_find.detect_schematic(pos, gate_eastwest)
  249. northsouth = false
  250. ns_key = "ew"
  251. if result then
  252. playerorigin = vector.add(origin, {x=0, y=1, z=1})
  253. end
  254. end
  255. -- Early exit.
  256. if not result then
  257. return
  258. end
  259. -- If a specific orientation is required, then check that.
  260. if require_ns ~= nil then
  261. if northsouth ~= require_ns then
  262. return
  263. end
  264. end
  265. -- Store locations of air/portal nodes inside the gateway.
  266. local airpoints = {}
  267. if result then
  268. for k, v in ipairs(points) do
  269. local nn = minetest.get_node(v).name
  270. if nn == "air" or nn == "nether:portal_liquid" or
  271. nn == "nether:portal_hidden" then
  272. airpoints[#airpoints+1] = v
  273. end
  274. end
  275. end
  276. -- Did we find a working gateway?
  277. local yes = false
  278. if result then
  279. local o = counts["default:obsidian"] or 0
  280. local d = counts["cavestuff:dark_obsidian"] or 0
  281. local c = counts["cavestuff:glow_obsidian"] or 0
  282. local g = counts["griefer:grieferstone"] or 0
  283. local a = (#airpoints == 6)
  284. if (o + d + c) == 12 and g == 2 and a == true then
  285. yes = true
  286. end
  287. end
  288. if yes then
  289. return true, origin, airpoints, northsouth, ns_key, playerorigin
  290. end
  291. end
  292. function obsidian_gateway.attempt_activation(pos, player)
  293. local pname = player:get_player_name()
  294. local ppos = vector_round(player:get_pos())
  295. local under = utility.node_under_pos(player:get_pos())
  296. local inside = vector.add(under, {x=0, y=1, z=0})
  297. local nodeunder = minetest.get_node(under).name
  298. -- Player must be standing on one of these.
  299. if nodeunder ~= "default:obsidian" and
  300. nodeunder ~= "griefer:grieferstone" and
  301. nodeunder ~= "cavestuff:dark_obsidian" and
  302. nodeunder ~= "cavestuff:glow_obsidian" then
  303. -- This triggers when other types of portals are used, so is incorrect to display this chat.
  304. --minetest.chat_send_player(pname, "# Server: You need to be standing in the gateway for it to work!")
  305. return
  306. end
  307. local success
  308. local origin
  309. local northsouth
  310. local ns_key
  311. local playerorigin
  312. local airpoints
  313. success, origin, airpoints, northsouth, ns_key, playerorigin =
  314. obsidian_gateway.find_gate(pos)
  315. if not success then
  316. return
  317. end
  318. -- Add/update sound beacon.
  319. ambiance.spawn_sound_beacon("soundbeacon:gate", origin, 20, 1)
  320. ambiance.replay_nearby_sound_beacons(origin, 6)
  321. if sheriff.is_cheater(pname) then
  322. if sheriff.punish_probability(pname) then
  323. sheriff.punish_player(pname)
  324. return
  325. end
  326. end
  327. local target
  328. local meta = minetest.get_meta(origin)
  329. -- By spliting the key names by ns/ew, I ensure connected portals don't
  330. -- stomp on each other's data.
  331. target = minetest.string_to_pos(meta:get_string("obsidian_gateway_destination_" .. ns_key))
  332. --if not target then
  333. -- minetest.chat_send_player(pname, "# Server: Gateway has no destination! Aborting.")
  334. -- return
  335. --end
  336. -- Enable this if any serious problems occur.
  337. --if pname ~= gdac.name_of_admin then
  338. -- minetest.chat_send_player(pname, "# Server: Safety abort! Gateways are locked until further notice due to an error in the code.")
  339. -- return
  340. --end
  341. -- Gates CANNOT be initialized in the Abyss!
  342. -- (Only the outgoing realm-gate is useable.)
  343. -- This prevents players from building their own gates in the Abyss.
  344. if not target and rc.current_realm_at_pos(origin) == "abyss" then
  345. minetest.after(0, function()
  346. -- Detonate some TNT!
  347. tnt.boom(vector.add(ppos, {x=math_random(-3, 3), y=0, z=math_random(-3, 3)}), {
  348. radius = 3,
  349. ignore_protection = false,
  350. ignore_on_blast = false,
  351. damage_radius = 5,
  352. disable_drops = true,
  353. })
  354. end)
  355. return
  356. end
  357. local isreturngate = (meta:get_int("obsidian_gateway_return_gate_" .. ns_key) == 1)
  358. local actual_owner = meta:get_string("obsidian_gateway_owner_" .. ns_key)
  359. local isowner = (actual_owner == pname)
  360. local first_time_init = false
  361. -- Event horizon color depends on whether we are a return gate.
  362. obsidian_gateway.spawn_liquid(origin, northsouth, isreturngate)
  363. minetest.log("action", pname .. " activated gateway @ " .. minetest.pos_to_string(pos))
  364. -- Initialize gateway for the first time.
  365. if not target or (meta:get_string("obsidian_gateway_success_" .. ns_key) ~= "yes" and not isreturngate) then
  366. -- Target is valid then this could be an OLD gate with old metadata.
  367. if target and not isreturngate and meta:get_string("obsidian_gateway_success_" .. ns_key) == "" then
  368. minetest.chat_send_player(pname, "# Server: It looks like this could possibly be an OLD gate! Aborting for safety reasons.")
  369. minetest.chat_send_player(pname, "# Server: If this Gateway was previously functioning normally, please mail the admin with the coordinates.")
  370. minetest.chat_send_player(pname, "# Server: If this is a Gate that you have just constructed, you can safely ignore this message.")
  371. minetest.chat_send_player(pname, "# Server: The Gateway's EXIT location is @ " .. rc.pos_to_namestr(target) .. ".")
  372. minetest.after(1.5, function() easyvend.sound_error(pname) end)
  373. return
  374. end
  375. -- Algorithm for locating the destination.
  376. -- Get a potential gate location.
  377. target = rc.get_random_realm_gate_position(pname, origin)
  378. -- Is target outside bounds?
  379. local bad = function(target, origin)
  380. -- Handle nil.
  381. if not target then
  382. return true
  383. end
  384. -- Don't allow exit points near the colonies.
  385. if vector_distance(target, {x=0, y=0, z=0}) < 1000 or
  386. vector_distance(target, {x=0, y=-30790, z=0}) < 1000 then
  387. return true
  388. end
  389. -- Exit must not be too close to start.
  390. if vector_distance(target, origin) < 100 then
  391. return true
  392. end
  393. -- Or too far.
  394. -- This causes too many failures.
  395. -- Note: this is now handled by the 'rc' mod.
  396. --if vector_distance(target, origin) > 7000 then
  397. -- return true
  398. --end
  399. if not rc.is_valid_gateway_region(target) then
  400. return true
  401. end
  402. end
  403. -- Keep trying until the target is within bounds.
  404. local num_tries = 0
  405. while bad(target, origin) do
  406. target = rc.get_random_realm_gate_position(pname, origin)
  407. num_tries = num_tries + 1
  408. -- Max 3 tries.
  409. if num_tries >= 2 then
  410. ---[[
  411. minetest.after(0, function()
  412. -- Detonate some TNT!
  413. tnt.boom(vector.add(ppos, {x=math_random(-3, 3), y=0, z=math_random(-3, 3)}), {
  414. radius = 3,
  415. ignore_protection = false,
  416. ignore_on_blast = false,
  417. damage_radius = 5,
  418. disable_drops = true,
  419. })
  420. end)
  421. --]]
  422. return
  423. end
  424. end
  425. meta:set_string("obsidian_gateway_destination_" .. ns_key, minetest.pos_to_string(target))
  426. meta:set_string("obsidian_gateway_owner_" .. ns_key, pname)
  427. first_time_init = true
  428. isowner = true
  429. end
  430. if gdac.player_is_admin(pname) then
  431. isowner = true
  432. end
  433. -- Let everyone use gates owned by the admin.
  434. if actual_owner == gdac.name_of_admin then
  435. isowner = true
  436. end
  437. -- Slightly randomize player's exit coordinates.
  438. -- Without changing the coordinates of the gateway.
  439. local pdest
  440. if northsouth then
  441. pdest = vector.add(target, {x=math_random(0, 1), y=0, z=0})
  442. else
  443. pdest = vector.add(target, {x=0, y=0, z=math_random(0, 1)})
  444. end
  445. -- Collect any friends to bring along.
  446. local friendstobring = {}
  447. local allplayers = minetest.get_connected_players()
  448. for k, v in ipairs(allplayers) do
  449. if v:get_player_name() ~= pname then
  450. if vector_distance(v:get_pos(), player:get_pos()) < 3 then
  451. friendstobring[#friendstobring+1] = v:get_player_name()
  452. end
  453. end
  454. end
  455. -- Create a gateway at the player's destination.
  456. -- This gateway links back to the first.
  457. -- If it is destroyed, the player is stuck!
  458. preload_tp.execute({
  459. player_name = pname,
  460. target_position = pdest,
  461. emerge_radius = 32,
  462. particle_effects = true,
  463. pre_teleport_callback = function()
  464. -- Cancel teleport if origin gate does not have portal liquid.
  465. if not obsidian_gateway.have_liquid(origin, northsouth) then
  466. minetest.chat_send_player(pname, "# Server: Portal disrupted.")
  467. -- Cancel transport.
  468. return true
  469. end
  470. -- Don't build return portal on top of someone's protected stuff.
  471. if first_time_init then
  472. if check_protection(vector.add(target, {x=0, y=3, z=0}), 5) then
  473. minetest.chat_send_player(pname, "# Server: Return-gate construction FAILED due to protection near " .. rc.pos_to_namestr(target) .. ".")
  474. -- Clear data for the initial gate. This will permit the player to retry without tearing everything down and building it again.
  475. local meta = minetest.get_meta(origin)
  476. meta:set_string("obsidian_gateway_success_" .. ns_key, "")
  477. meta:set_string("obsidian_gateway_destination_" .. ns_key, "")
  478. meta:set_string("obsidian_gateway_owner_" .. ns_key, "")
  479. -- Cancel transport.
  480. return true
  481. end
  482. end
  483. -- Build return portal (only if not already using a return portal).
  484. -- Also, only build return portal on first use of the initial portal.
  485. if not isreturngate and first_time_init then
  486. if northsouth then
  487. -- Place northsouth gateway.
  488. local path = obsidian_gateway.modpath .. "/obsidian_gateway_northsouth.mts"
  489. local gpos = vector.add(target, {x=-1, y=-1, z=0})
  490. minetest.place_schematic(gpos, path, "0", nil, true)
  491. local meta = minetest.get_meta(gpos)
  492. meta:set_string("obsidian_gateway_destination_" .. ns_key, minetest.pos_to_string(playerorigin))
  493. meta:set_string("obsidian_gateway_owner_" .. ns_key, pname)
  494. meta:set_int("obsidian_gateway_return_gate_" .. ns_key, 1)
  495. else
  496. -- Place eastwest gateway.
  497. local path = obsidian_gateway.modpath .. "/obsidian_gateway_eastwest.mts"
  498. local gpos = vector.add(target, {x=0, y=-1, z=-1})
  499. minetest.place_schematic(gpos, path, "0", nil, true)
  500. local meta = minetest.get_meta(gpos)
  501. meta:set_string("obsidian_gateway_destination_" .. ns_key, minetest.pos_to_string(playerorigin))
  502. meta:set_string("obsidian_gateway_owner_" .. ns_key, pname)
  503. meta:set_int("obsidian_gateway_return_gate_" .. ns_key, 1)
  504. end
  505. end
  506. -- Mark the initial gate as success.
  507. -- If this is not done, then gate will assume it is not initialized
  508. -- the next time it is used. This fixes a bug where the return gate is
  509. -- not properly constructed if the player moves during transport
  510. -- (because this callback function doesn't get called).
  511. if not isreturngate and first_time_init then
  512. local meta = minetest.get_meta(origin)
  513. meta:set_string("obsidian_gateway_success_" .. ns_key, "yes")
  514. meta:mark_as_private("obsidian_gateway_success_" .. ns_key)
  515. end
  516. -- If the destination is the Abyss, then kill player first.
  517. -- This helps to prevent player from bringing any foreign items into this realm.
  518. -- Note: this relies on the teleport code already checking all other preconditions
  519. -- first. I.e., if this callback returns 'false', then the player absolutely
  520. -- will be teleported.
  521. if rc.current_realm_at_pos(pdest) == "abyss" then
  522. -- Dump player bones, as if they died.
  523. -- This should behave exactly as if the player died, with the exception of
  524. -- setting the player's health to 0.
  525. bones.dump_bones(pname, true)
  526. bones.last_known_death_locations[pname] = nil -- Fake death.
  527. local pref = minetest.get_player_by_name(pname)
  528. pref:set_hp(pref:get_properties().hp_max)
  529. give_initial_stuff.give(pref)
  530. end
  531. -- Always regenerate portal liquid in the destination portal.
  532. -- (It will often be missing since no one was near it.)
  533. -- This function will check if there actually is a gate, here.
  534. obsidian_gateway.regenerate_liquid(target, northsouth, (not isreturngate))
  535. -- If the player is someone other than the owner, using this Gate has consequences.
  536. if not isowner then
  537. -- This function is already called normally, when a Gate is used.
  538. -- Calling it again here, effectively doubles the chance that the user
  539. -- starts feeling rather ill.
  540. portal_sickness.on_use_portal(pname)
  541. end
  542. end,
  543. post_teleport_callback = function()
  544. for k, v in ipairs(friendstobring) do
  545. local friend = minetest.get_player_by_name(v)
  546. if friend then
  547. local fname = friend:get_player_name()
  548. preload_tp.execute({
  549. player_name = fname,
  550. target_position = pdest,
  551. particle_effects = true,
  552. pre_teleport_callback = function()
  553. -- If the destination is the Abyss, then kill player first.
  554. -- Note: this relies on the teleport code already checking all other preconditions
  555. -- first. I.e., if this callback returns 'false', then the player absolutely
  556. -- will be teleported.
  557. if rc.current_realm_at_pos(pdest) == "abyss" then
  558. -- Dump player bones, as if they died.
  559. -- This should behave exactly as if the player died, with the exception of
  560. -- setting the player's health to 0.
  561. bones.dump_bones(fname, true)
  562. bones.last_known_death_locations[fname] = nil -- Fake death.
  563. local pref = minetest.get_player_by_name(fname)
  564. pref:set_hp(pref:get_properties().hp_max)
  565. give_initial_stuff.give(pref)
  566. end
  567. end,
  568. force_teleport = true,
  569. send_blocks = true,
  570. })
  571. portal_sickness.on_use_portal(fname)
  572. end
  573. end
  574. -- Update liquids around on first init.
  575. if first_time_init then
  576. minetest.after(2, function()
  577. mapfix.execute(target, 10)
  578. end)
  579. end
  580. ambiance.spawn_sound_beacon("soundbeacon:gate", target, 20, 1)
  581. ambiance.replay_nearby_sound_beacons(target, 6)
  582. portal_sickness.on_use_portal(pname)
  583. end,
  584. teleport_sound = "nether_portal_usual",
  585. send_blocks = true,
  586. })
  587. end
  588. -- To be called inside node's 'on_destruct' callback.
  589. -- Note: 'transient' is ONLY true when node to be destructed is portal liquid!
  590. function obsidian_gateway.on_damage_gate(pos, transient)
  591. -- First, perform some cheap checks to see if there could possibly be a gate
  592. -- at this location. We only perform the expensive checks if the cheap checks
  593. -- pass!
  594. local minp = vector.add(pos, {x=-4, y=-4, z=-4})
  595. local maxp = vector.add(pos, {x=4, y=4, z=4})
  596. local names = {
  597. "default:obsidian",
  598. "cavestuff:dark_obsidian",
  599. "cavestuff:glow_obsidian",
  600. "griefer:grieferstone",
  601. "nether:portal_liquid",
  602. "nether:portal_hidden",
  603. }
  604. local points, counts = minetest.find_nodes_in_area(minp, maxp, names)
  605. if #points == 0 then
  606. return
  607. end
  608. local doorpoints = points
  609. -- Remove all portal-liquid nodes. (Will play sound if any removed.)
  610. -- First, try to get gate origin from meta. If this fails, then we use the
  611. -- 'points' array as a fallback (old behavior).
  612. local origin, northsouth = obsidian_gateway.get_origin_and_dir(pos)
  613. if origin then
  614. doorpoints = obsidian_gateway.door_positions(origin, northsouth)
  615. end
  616. minetest.after(0, obsidian_gateway.remove_liquid, pos, doorpoints)
  617. -- If transient "pop" of portal liquid nodes, then do not continue further to
  618. -- actually damage the gate.
  619. if transient then
  620. return
  621. end
  622. -- A valid gate requires exactly 2 oerkki stone.
  623. -- (There may be additional oerkki stone not part of the gate.)
  624. if counts["griefer:grieferstone"] < 2 then
  625. return
  626. end
  627. do
  628. local o = counts["default:obsidian"] or 0
  629. local d = counts["cavestuff:dark_obsidian"] or 0
  630. local c = counts["cavestuff:glow_obsidian"] or 0
  631. -- Including the node that will be removed (we should have been called
  632. -- inside of 'on_destruct' for a given node), there should be 12 obsidian
  633. -- remaining, otherwise cannot be a valid gate. (There may be additional
  634. -- obsidian nearby not part of the gate.)
  635. if (o + d + c) < 12 then
  636. return
  637. end
  638. end
  639. -- Cheap checks completed, now do the expensive check.
  640. if not obsidian_gateway.find_gate(pos) then
  641. return
  642. end
  643. -- If we reach here, we know we have a valid gate attached to this position.
  644. -- We don't care if it is a NS or EW-facing gate.
  645. -- This has a chance to destroy one of the oerkki stones, which costs some
  646. -- resources to craft again. But don't spawn lava in overworld. The point of
  647. -- this is to make it a bit more costly to constantly reset the gate if you
  648. -- don't like where it goes. Note: using 'swap_node' first in order to prevent
  649. -- calling additional callbacks.
  650. local idx = math.random(1, #points)
  651. local tar = points[idx]
  652. minetest.swap_node(tar, {name="air"})
  653. minetest.set_node(tar, {name="fire:basic_flame"})
  654. ambiance.sound_play("nether_rack_destroy", pos, 1.0, 64)
  655. end
  656. dofile(obsidian_gateway.modpath .. "/flame_staff.lua")
  657. if not obsidian_gateway.run_once then
  658. local c = "obsidian_gateway:core"
  659. local f = obsidian_gateway.modpath .. "/init.lua"
  660. reload.register_file(c, f, false)
  661. obsidian_gateway.run_once = true
  662. end