functions.lua 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. -- This file is designed to be reloadable.
  2. teleports = teleports or {}
  3. teleports.teleports = teleports.teleports or {}
  4. teleports.min_range = 250
  5. teleports.datafile = minetest.get_worldpath() .. "/teleports.txt"
  6. dofile(teleports.modpath .. "/sickness.lua")
  7. local nyanbow = "nyancat:nyancat_rainbow"
  8. -- Table of blocks which can be used to super-charge a teleport. Each block has a specific charge value.
  9. teleports.charge_blocks = {
  10. ["default:diamondblock"] = {charge=14 },
  11. ["default:mese"] = {charge=4 },
  12. ["default:steelblock"] = {charge=1 },
  13. ["default:copperblock"] = {charge=1.5 },
  14. ["default:bronzeblock"] = {charge=1.8 },
  15. ["default:goldblock"] = {charge=2 },
  16. ["moreores:silver_block"] = {charge=2 },
  17. ["moreores:tin_block"] = {charge=1.5 },
  18. ["moreores:mithril_block"] = {charge=24 },
  19. ["chromium:block"] = {charge=0.9 },
  20. ["zinc:block"] = {charge=1.8 },
  21. ["lead:block"] = {charge=0.4 },
  22. ["akalin:block"] = {charge=0.9 },
  23. ["alatro:block"] = {charge=0.7 },
  24. ["arol:block"] = {charge=0.5 },
  25. ["talinite:block"] = {charge=1.1 },
  26. }
  27. teleports.is_nyanbow_teleport = function(pos)
  28. local positions = {
  29. {x=pos.x-1, y=pos.y, z=pos.z},
  30. {x=pos.x+1, y=pos.y, z=pos.z},
  31. {x=pos.x, y=pos.y, z=pos.z-1},
  32. {x=pos.x, y=pos.y, z=pos.z+1},
  33. {x=pos.x-1, y=pos.y, z=pos.z-1},
  34. {x=pos.x+1, y=pos.y, z=pos.z+1},
  35. {x=pos.x-1, y=pos.y, z=pos.z+1},
  36. {x=pos.x+1, y=pos.y, z=pos.z-1},
  37. }
  38. local bows = 0
  39. for k, v in ipairs(positions) do
  40. local n = minetest.get_node(v).name
  41. if n == nyanbow then
  42. bows = bows + 1
  43. end
  44. end
  45. return (bows == 8)
  46. end
  47. teleports.save = function()
  48. local datastring = xban.serialize(teleports.teleports)
  49. if not datastring then
  50. return
  51. end
  52. local file, err = io.open(teleports.datafile, "w")
  53. if err then
  54. return
  55. end
  56. file:write(datastring)
  57. file:close()
  58. end
  59. teleports.load = function()
  60. local file, err = io.open(teleports.datafile, "r")
  61. if err then
  62. teleports.teleports = {}
  63. return
  64. end
  65. teleports.teleports = minetest.deserialize(file:read("*all"))
  66. if type(teleports.teleports) ~= "table" then
  67. teleports.teleports = {}
  68. end
  69. file:close()
  70. end
  71. teleports.clear_area = function(blockpos, action, calls_remaining, param)
  72. if action == core.EMERGE_CANCELLED or action == core.EMERGE_ERRORED then
  73. return
  74. end
  75. for x = param.minp.x, param.maxp.x, 1 do
  76. for y = param.minp.y, param.maxp.y, 1 do
  77. for z = param.minp.z, param.maxp.z, 1 do
  78. local pos = {x=x, y=y, z=z}
  79. local node = minetest.get_node(pos)
  80. if node.name ~= "ignore" then
  81. if node.name ~= "air" and node.name ~= "bones:bones" then
  82. minetest.remove_node(pos)
  83. end
  84. end
  85. end
  86. end
  87. end
  88. end
  89. function teleports.kill_players_at_pos(teleport_pos, pname)
  90. local dead_players = minetest.get_objects_inside_radius({x=teleport_pos.x, y=teleport_pos.y+1, z=teleport_pos.z}, 2)
  91. for k, v in ipairs(dead_players) do
  92. if v and v:is_player() then
  93. if not gdac.player_is_admin(v) and v:get_player_name() ~= pname then -- Don't kill admin or self (can happen due to lag).
  94. -- Only if player isn't already dead.
  95. if v:get_hp() > 0 then
  96. -- If there's a player here already the map must be loaded, so we
  97. -- can put fire where they're standing no problem.
  98. local dp = vector.round(v:get_pos())
  99. local node = minetest.get_node(dp)
  100. if node.name == "air" then
  101. minetest.add_node(dp, {name="fire:basic_flame"})
  102. end
  103. v:set_hp(0)
  104. minetest.chat_send_all("# Server: <" .. rename.gpn(v:get_player_name()) .. "> was killed by a teleport. Noob!")
  105. end
  106. end
  107. end
  108. end
  109. end
  110. teleports.teleport_player = function(player, origin_pos, teleport_pos, target)
  111. if not player or not player:is_player() then
  112. return
  113. end
  114. local pname = player:get_player_name()
  115. if sheriff.is_cheater(pname) then
  116. if sheriff.punish_probability(pname) then
  117. sheriff.punish_player(pname)
  118. return
  119. end
  120. end
  121. local player_pos = player:get_pos()
  122. if (player_pos.y < origin_pos.y) or (vector.distance(player_pos, origin_pos) > 2) then
  123. minetest.chat_send_player(pname, "# Server: You must stand on portal activation surface!")
  124. return
  125. end
  126. -- Small chance to be teleported somewhere completely random.
  127. -- The chance increases a LOT if teleports are crowded.
  128. local use_random = false
  129. local random_chance = 500 -- Actually 470, because nearby-count is always at least 1 (counting self).
  130. local count_nearby = 0
  131. for k, v in ipairs(teleports.teleports) do
  132. if vector.distance(v.pos, origin_pos) < 50 then
  133. count_nearby = count_nearby + 1
  134. end
  135. end
  136. random_chance = random_chance - (count_nearby * 30)
  137. if random_chance < 10 then
  138. random_chance = 10
  139. end
  140. if math.random(1, random_chance) == 1 then
  141. if #(teleports.teleports) > 0 then
  142. local tp = teleports.teleports[math.random(1, #(teleports.teleports))]
  143. if not tp then
  144. minetest.chat_send_player(pname, "# Server: Transport error! Aborted.")
  145. return
  146. end
  147. teleport_pos = tp.pos
  148. use_random = true
  149. end
  150. end
  151. local p = vector.round(teleport_pos)
  152. local minp = {x=p.x-1, y=p.y+1, z=p.z-1}
  153. local maxp = {x=p.x+1, y=p.y+3, z=p.z+1}
  154. local pos = vector.round(target)
  155. -- Perform this check only if teleport target wasn't randomized.
  156. if not use_random then
  157. local start_realm = rc.current_realm_at_pos(origin_pos)
  158. local target_realm = rc.current_realm_at_pos(pos)
  159. if target_realm == "" or start_realm == "" or start_realm ~= target_realm then
  160. minetest.chat_send_player(pname, "# Server: Target location is in a different realm! Aborting.")
  161. return
  162. end
  163. end
  164. minetest.log("[teleports] teleporting player <" .. pname .. "> to " .. minetest.pos_to_string(pos))
  165. -- Teleport player to chosen location.
  166. preload_tp.preload_and_teleport(pname, pos, 16, function()
  167. -- Kill players standing on target teleport pad.
  168. teleports.kill_players_at_pos(teleport_pos, pname)
  169. -- Delete 3x3x3 area above teleport.
  170. for x=minp.x, maxp.x do
  171. for y=minp.y, maxp.y do
  172. for z=minp.z, maxp.z do
  173. local pos = {x=x, y=y, z=z}
  174. local node = minetest.get_node(pos)
  175. if node.name ~= "ignore" then
  176. -- Do not destroy players' bones.
  177. if node.name ~= "air" and node.name ~= "bones:bones" then
  178. minetest.add_node(pos, {name="fire:basic_flame"})
  179. end
  180. end
  181. end
  182. end
  183. end
  184. end,
  185. function()
  186. portal_sickness.on_use_portal(pname)
  187. end,
  188. nil, false)
  189. teleports.ping_all_teleports()
  190. end
  191. teleports.find_nearby = function(pos, count, network, yespublic)
  192. local nearby = {}
  193. local trange, isnyan = teleports.calculate_range(pos)
  194. local start_realm = rc.current_realm_at_pos(pos)
  195. for i = #teleports.teleports, 1, -1 do
  196. local tp = teleports.teleports[i]
  197. if not vector.equals(tp.pos, pos) and vector.distance(tp.pos, pos) <= trange then
  198. local target_realm = rc.current_realm_at_pos(tp.pos)
  199. -- Only find teleports in the same dimension.
  200. if start_realm ~= "" and start_realm == target_realm then
  201. local othernet = tp.channel or ""
  202. if othernet == network or (othernet == "" and yespublic == 'true') then
  203. table.insert(nearby, tp)
  204. if #nearby >= count then
  205. break
  206. end
  207. end
  208. end
  209. end
  210. end
  211. return nearby
  212. end
  213. teleports.find_specific = function(pos)
  214. for i = 1, #teleports.teleports, 1 do
  215. local tp = teleports.teleports[i]
  216. if vector.equals(tp.pos, pos) then
  217. return i -- Return index of teleport.
  218. end
  219. end
  220. end
  221. teleports.calculate_charge = function(pos)
  222. local positions = {
  223. {x=pos.x-1, y=pos.y, z=pos.z},
  224. {x=pos.x+1, y=pos.y, z=pos.z},
  225. {x=pos.x, y=pos.y, z=pos.z-1},
  226. {x=pos.x, y=pos.y, z=pos.z+1},
  227. {x=pos.x-1, y=pos.y, z=pos.z-1},
  228. {x=pos.x+1, y=pos.y, z=pos.z+1},
  229. {x=pos.x-1, y=pos.y, z=pos.z+1},
  230. {x=pos.x+1, y=pos.y, z=pos.z-1},
  231. }
  232. local bows = 0
  233. local charge = 1 -- Ambient charge is at least 1 (the teleport block provides 1 KJ).
  234. for k, v in ipairs(positions) do
  235. local n = minetest.get_node(v).name
  236. local c = 0
  237. if teleports.charge_blocks[n] ~= nil then
  238. c = teleports.charge_blocks[n].charge
  239. end
  240. charge = charge + c
  241. if n == nyanbow then
  242. bows = bows + 1
  243. end
  244. end
  245. local is_nyanporter = false
  246. if bows == 8 then
  247. is_nyanporter = true
  248. end
  249. charge = math.floor(charge + 0.5)
  250. -- Combined teleports interfere with each other and reduce their range.
  251. local minp = vector.add(pos, {x=-2, y=0, z=-2})
  252. local maxp = vector.add(pos, {x=2, y=0, z=2})
  253. local others = minetest.find_nodes_in_area(minp, maxp, "teleports:teleport")
  254. local other_count = 1
  255. if others and #others > 0 then
  256. charge = charge / #others
  257. other_count = #others
  258. end
  259. return charge, other_count, is_nyanporter
  260. end
  261. local function cds(y)
  262. local scalar = 1
  263. if y < 0 then
  264. local depth = math.abs(y)
  265. scalar = depth / 30912
  266. if scalar > 1 then scalar = 1 end
  267. if scalar < 0 then scalar = 0 end
  268. scalar = (scalar * -1) + 1
  269. end
  270. return scalar
  271. end
  272. teleports.calculate_range = function(pos)
  273. -- Compute charge.
  274. local chg, other_cnt, nyan = teleports.calculate_charge(pos)
  275. if nyan then
  276. local owner = minetest.get_meta(pos):get_string("owner")
  277. -- There is an admin teleport pair between the Surface Colony and the City of Fire.
  278. -- This special exception code makes it work.
  279. if owner == "MustTest" then
  280. return 31000, nyan
  281. else
  282. -- Range of nyan teleports is reduced if they're crowded.
  283. return (7770 / other_cnt), nyan
  284. end
  285. end
  286. -- How much distance each unit of charge is good for.
  287. local inc = 25
  288. -- Compute extra range.
  289. local rng = math.floor(inc * chg)
  290. -- Calculate how much to scale extra range by depth.
  291. local scalar = cds(pos.y)
  292. -- Scale extra range by depth.
  293. rng = rng * scalar
  294. -- Add extra range to base (minimum) range.
  295. rng = rng + 250
  296. -- Teleport range shall not go below 250 meters.
  297. return math.floor(rng), nyan
  298. end
  299. function teleports.write_infotext(pos)
  300. local meta = minetest.get_meta(pos)
  301. local name = meta:get_string("name")
  302. local network = meta:get_string("network")
  303. local owner = meta:get_string("owner")
  304. local dname = rename.gpn(owner)
  305. local public = meta:get_int("public") or 1
  306. if public == 1 then public = 'true' else public = 'false' end
  307. local id = "<" .. name .. ">"
  308. local net = "<" .. network .. ">"
  309. local own = "<" .. dname .. ">"
  310. if name == "" then id = "NONE" end
  311. if network == "" then net = "PUBLIC" end
  312. if public == 'false' then net = "SUPPRESSED" end
  313. meta:set_string("infotext", "Teleporter. Punch to update controls.\nOwner: " .. own .. "\nBeacon ID: " .. id .. "\nBeacon Channel: " .. net)
  314. end
  315. teleports.update = function(pos)
  316. local meta = minetest.get_meta(pos)
  317. local network = meta:get_string("network") or ""
  318. local owner = meta:get_string("owner") or ""
  319. local name = meta:get_string("name") or ""
  320. local yespublic = meta:get_string("yespublic") or 'true'
  321. local buttons = "";
  322. local nearby = teleports.find_nearby(pos, 10, network, yespublic)
  323. local button_x = 8
  324. local button_y = 1
  325. for i, v in ipairs(nearby) do
  326. local tp = v.pos
  327. local data = tp.x .. "," .. tp.y .. "," .. tp.z
  328. local real_label = rc.pos_to_string(tp)
  329. meta:set_string("loc" .. (i), data)
  330. if v.name ~= nil then
  331. if v.name ~= "" then
  332. real_label = v.name
  333. end
  334. end
  335. buttons = buttons ..
  336. "button_exit[" .. button_x .. "," .. button_y ..
  337. ";3,0.5;tp" .. i .. ";" .. minetest.formspec_escape(real_label) .. "]";
  338. button_y = button_y + 1
  339. if button_y >= 6 then
  340. button_y = 1
  341. button_x = 5
  342. end
  343. end
  344. local public = meta:get_int("public") or 1
  345. if public == 1 then public = 'true' else public = 'false' end
  346. teleports.write_infotext(pos)
  347. local net = "<" .. network .. ">"
  348. local nm = "<" .. name .. ">"
  349. if name == "" then nm = "NONE" end
  350. if network == "" then net = "PUBLIC" end
  351. if public == 'false' then net = "SUPPRESSED" end
  352. local charge, count, isnyan = teleports.calculate_charge(pos)
  353. local range = teleports.calculate_range(pos)
  354. if isnyan then
  355. charge = "NYBW"
  356. end
  357. local formspec = "size[11,7;]" ..
  358. default.gui_bg ..
  359. default.gui_bg_img ..
  360. default.gui_slots ..
  361. "label[0,0;" .. 'Transport to nearby beacons! Need mossy cobble for energy.' .. "]" ..
  362. "label[1,0.70;Beacon ID: " .. minetest.formspec_escape(nm) .. "]" ..
  363. "label[1,1.2;Beacon Channel: " .. minetest.formspec_escape(net) .. "]" ..
  364. "field[0.3,2.7;2,0.5;id;Change Beacon ID;]" .. "button_exit[2,2.4;2,0.5;change_id;Confirm]" ..
  365. "field[0.3,3.9;2,0.5;network;Change Channel;]" .. "button_exit[2,3.6;2,0.5;change_network;Confirm]" ..
  366. buttons ..
  367. "button_exit[0,5.2;2,0.5;cancel;Close]" ..
  368. "checkbox[0.02,4.1;showhide;Show Channel;" .. public .. "]" ..
  369. "checkbox[2,4.1;yespublic;Connect Public;" .. yespublic .. "]" ..
  370. "label[2,5;Ambient Charge: " .. charge .. " KJ]" ..
  371. "label[2,5.35;Transport Range: " .. range .. " M]" ..
  372. "list[context;price;0,0.75;1,1;]" ..
  373. "list[current_player;main;0,6;11,1;]" ..
  374. "listring[]"
  375. meta:set_string("formspec", formspec)
  376. end
  377. teleports.on_receive_fields = function(pos, formname, fields, player)
  378. if not player then return end
  379. if not player:is_player() then return end
  380. if player:get_hp() <= 0 then return end -- Ignore dead players.
  381. local playername = player:get_player_name()
  382. local meta = minetest.get_meta(pos)
  383. local isnyan = teleports.is_nyanbow_teleport(pos)
  384. local owner = meta:get_string("owner") or ""
  385. local infinite_fuel = false
  386. if owner == "MustTest" then
  387. infinite_fuel = true
  388. end
  389. local admin = minetest.check_player_privs(playername, {server=true})
  390. local needsave = false
  391. -- Make sure this teleport, at this postion, has an entry.
  392. local tp_idx = teleports.find_specific(pos)
  393. if not tp_idx then
  394. minetest.chat_send_player(playername, "# Server: Transporter data error: 0xDEADBEEF.")
  395. easyvend.sound_error(playername)
  396. return
  397. end
  398. if not teleports.teleports[tp_idx] then
  399. minetest.chat_send_player(playername, "# Server: Transporter data error: 0xDEADBEEF.")
  400. easyvend.sound_error(playername)
  401. return
  402. end
  403. if fields.showhide then
  404. if owner == playername or admin then
  405. if fields.showhide == "true" then
  406. meta:set_int("public", 1)
  407. else
  408. meta:set_int("public", 0)
  409. end
  410. else
  411. minetest.chat_send_player(playername, "# Server: Only the owner can change the configuration.")
  412. easyvend.sound_error(playername)
  413. end
  414. end
  415. if fields.yespublic then
  416. if owner == playername or admin then
  417. if fields.yespublic == "true" then
  418. meta:set_string("yespublic", 'true')
  419. else
  420. meta:set_string("yespublic", 'false')
  421. end
  422. else
  423. minetest.chat_send_player(playername, "# Server: Only the owner can change the configuration.")
  424. easyvend.sound_error(playername)
  425. end
  426. end
  427. if fields.change_id and fields.id then
  428. if owner == playername or admin then
  429. meta:set_string("name", fields.id)
  430. teleports.teleports[tp_idx].name = fields.id
  431. needsave = true
  432. else
  433. minetest.chat_send_player(playername, "# Server: Only the owner can change the configuration.")
  434. easyvend.sound_error(playername)
  435. end
  436. end
  437. if fields.change_network and fields.network then
  438. if owner == playername or admin then
  439. meta:set_string("network", fields.network)
  440. teleports.teleports[tp_idx].channel = fields.network
  441. needsave = true
  442. else
  443. minetest.chat_send_player(playername, "# Server: Only the owner can change the configuration.")
  444. easyvend.sound_error(playername)
  445. end
  446. end
  447. if needsave == true then
  448. teleports.save()
  449. end
  450. local pressed_tp_button = false
  451. local pressed_tp_location
  452. for i = 1, 10, 1 do
  453. -- According to button names/data set in the machine update function.
  454. local btnname = "tp" .. i
  455. local posname = "loc" .. i
  456. if fields[btnname] then
  457. pressed_tp_button = true
  458. pressed_tp_location = meta:get_string(posname)
  459. break
  460. end
  461. end
  462. if pressed_tp_button then
  463. local have_biofuel = false
  464. local tpname = pressed_tp_location
  465. local have_target = false
  466. local target_pos = {x=0, y=0, z=0}
  467. if tpname and type(tpname) == "string" then
  468. local tppos = minetest.string_to_pos(tpname)
  469. if tppos then
  470. if vector.distance(tppos, pos) <= teleports.calculate_range(pos) then
  471. -- Do not permit teleporting from one realm to another.
  472. -- Doing so requires a different kind of teleport device.
  473. local start_realm = rc.current_realm_at_pos(pos)
  474. local target_realm = rc.current_realm_at_pos(tppos)
  475. if start_realm ~= "" and start_realm == target_realm then
  476. local exists = false
  477. for i = 1, #teleports.teleports, 1 do
  478. local tp = teleports.teleports[i]
  479. if vector.equals(tp.pos, tppos) then
  480. exists = true
  481. break
  482. end
  483. end
  484. if exists then
  485. have_target = true
  486. target_pos = tppos
  487. else
  488. minetest.chat_send_player(playername, "# Server: Transport control error: target no longer exists.")
  489. easyvend.sound_error(playername)
  490. end
  491. else
  492. minetest.chat_send_player(playername, "# Server: Cannot teleport between realm boundaries!")
  493. easyvend.sound_error(playername)
  494. end
  495. else
  496. minetest.chat_send_player(playername, "# Server: Transport control error: target out of range!")
  497. easyvend.sound_error(playername)
  498. end
  499. else
  500. minetest.chat_send_player(playername, "# Server: Transport control error: 0xDEADBEEF.")
  501. easyvend.sound_error(playername)
  502. end
  503. else
  504. minetest.chat_send_player(playername, "# Server: Transport control error: formspec.")
  505. easyvend.sound_error(playername)
  506. end
  507. if have_target == true then -- Don't use fuel unless a valid target is found.
  508. local inv = meta:get_inventory();
  509. if not admin and not isnyan and not infinite_fuel then -- Don't do fuel calculation if admin is using teleport.
  510. -- Cost is 1 item of fuel per 300 meters.
  511. -- This means players save on fuel when using long range teleports,
  512. -- instead of using a chain of short-range teleports.
  513. -- However, long range teleports cost more to make.
  514. local rcost = math.floor(vector.distance(pos, target_pos) / 300)
  515. if rcost < 1 then rcost = 1 end
  516. local price1 = {name="default:mossycobble", count=rcost, wear=0, metadata=""}
  517. local price2 = {name="flowers:waterlily", count=rcost, wear=0, metadata=""}
  518. if not inv:is_empty("price") then
  519. if inv:contains_item("price", price1) then
  520. inv:remove_item("price", price1)
  521. have_biofuel = true
  522. elseif inv:contains_item("price", price2) then
  523. inv:remove_item("price", price2)
  524. have_biofuel = true
  525. else
  526. minetest.chat_send_player(playername, "# Server: Insufficient stored energy for transport. Add more biofuel.")
  527. easyvend.sound_error(playername)
  528. end
  529. else
  530. minetest.chat_send_player(playername, "# Server: Transporter is on maintenance energy only. Add biofuel to use.")
  531. easyvend.sound_error(playername)
  532. end
  533. end
  534. if have_biofuel or admin or isnyan or infinite_fuel then
  535. local teleport_pos = {x=target_pos.x, y=target_pos.y, z=target_pos.z}
  536. local spawn_pos = {x=teleport_pos.x-1+math.random(0, 2), y=teleport_pos.y+1, z=teleport_pos.z-1+math.random(0, 2)}
  537. teleports.teleport_player(player, pos, teleport_pos, spawn_pos)
  538. end
  539. end
  540. end
  541. -- Always update the teleport formspec.
  542. teleports.update(pos)
  543. end
  544. teleports.allow_metadata_inventory_put = function(pos, listname, index, stack, player)
  545. local pname = player:get_player_name()
  546. -- Protection interferes with building public networks.
  547. --if minetest.test_protection(pos, pname) then return 0 end
  548. if listname == "price" and stack:get_name() == "default:mossycobble" then
  549. return stack:get_count()
  550. elseif listname == "price" and stack:get_name() == "flowers:waterlily" then
  551. return stack:get_count()
  552. end
  553. return 0
  554. end
  555. teleports.allow_metadata_inventory_take = function(pos, listname, index, stack, player)
  556. local pname = player:get_player_name()
  557. -- Protection interferes with building public networks.
  558. --if minetest.test_protection(pos, pname) then return 0 end
  559. return stack:get_count()
  560. end
  561. teleports.allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
  562. return 0
  563. end
  564. teleports.can_dig = function(pos)
  565. local meta = minetest.get_meta(pos)
  566. local inv = meta:get_inventory()
  567. return inv:is_empty("price")
  568. end
  569. teleports.after_place_node = function(pos, placer)
  570. if placer and placer:is_player() then
  571. local meta = minetest.get_meta(pos)
  572. local pname = placer:get_player_name()
  573. local dname = rename.gpn(pname)
  574. meta:set_string("owner", pname)
  575. meta:set_string("rename", dname)
  576. meta:set_string("name", "")
  577. meta:set_string("network", "")
  578. meta:set_int("public", 1)
  579. local inv = meta:get_inventory()
  580. inv:set_size("price", 1)
  581. local initialcharge = {name="default:mossycobble", count=30, wear=0, metadata=""}
  582. inv:add_item("price", initialcharge)
  583. teleports.update(pos)
  584. table.insert(teleports.teleports, {pos=vector.round(pos)})
  585. teleports.save()
  586. end
  587. end
  588. teleports.on_destruct = function(pos)
  589. --minetest.chat_send_all("# Server: Destructing teleport!")
  590. for i, EachTeleport in ipairs(teleports.teleports) do
  591. if vector.equals(EachTeleport.pos, pos) then
  592. table.remove(teleports.teleports, i)
  593. teleports.save()
  594. end
  595. end
  596. end
  597. function teleports.ping_all_teleports()
  598. local players = minetest.get_connected_players()
  599. local pp = {}
  600. for k, v in ipairs(players) do
  601. table.insert(pp, v:get_pos())
  602. end
  603. local ping = function(pos)
  604. local xd = 1
  605. local zd = 1
  606. minetest.add_particlespawner({
  607. amount = 80,
  608. time = 5,
  609. minpos = {x=pos.x-xd, y=pos.y+1, z=pos.z-zd},
  610. maxpos = {x=pos.x+xd, y=pos.y+3, z=pos.z+zd},
  611. minvel = {x=-1, y=-1, z=-1},
  612. maxvel = {x=1, y=1, z=1},
  613. minacc = {x=-1, y=-1, z=-1},
  614. maxacc = {x=1, y=1, z=1},
  615. minexptime = 0.5,
  616. maxexptime = 1.5,
  617. minsize = 0.5,
  618. maxsize = 2,
  619. collisiondetection = false,
  620. texture = "default_obsidian_shard.png",
  621. glow = 14,
  622. })
  623. end
  624. -- Spawn particles over every teleport that's near a player.
  625. for k, v in ipairs(teleports.teleports) do
  626. for i, j in ipairs(pp) do
  627. if vector.distance(v.pos, j) < 32 then
  628. ping(v.pos)
  629. end
  630. end
  631. end
  632. end
  633. teleports.on_punch = function(pos, node, puncher, pointed_thing)
  634. teleports.update(pos)
  635. -- Maybe this is a bit too spammy and generally unnecessary?
  636. if puncher and puncher:is_player() then
  637. minetest.chat_send_player(puncher:get_player_name(), "# Server: This machine has been updated.")
  638. end
  639. end
  640. teleports.on_diamond_place = function(itemstack, placer, pointed_thing)
  641. local stack = ItemStack("default:diamondblock")
  642. local pos = pointed_thing.above
  643. local name = "default:diamondblock"
  644. if minetest.get_node({x=pos.x+1,y=pos.y,z=pos.z}).name == name and
  645. minetest.get_node({x=pos.x+1,y=pos.y,z=pos.z+1}).name == name and
  646. minetest.get_node({x=pos.x+1,y=pos.y,z=pos.z-1}).name == name and
  647. minetest.get_node({x=pos.x-1,y=pos.y,z=pos.z}).name == name and
  648. minetest.get_node({x=pos.x-1,y=pos.y,z=pos.z+1}).name == name and
  649. minetest.get_node({x=pos.x-1,y=pos.y,z=pos.z-1}).name == name and
  650. minetest.get_node({x=pos.x,y=pos.y,z=pos.z+1}).name == name and
  651. minetest.get_node({x=pos.x,y=pos.y,z=pos.z-1}).name == name
  652. then
  653. stack = ItemStack("teleports:teleport")
  654. end
  655. local ret = minetest.item_place(stack, placer, pointed_thing)
  656. if ret == nil then
  657. return itemstack
  658. else
  659. return ItemStack("default:diamondblock " .. itemstack:get_count() - (1 - ret:get_count()))
  660. end
  661. end