init.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. -- This mod provides an item which enables players to teleport to registered locations without the need of a teleporter.
  2. -- This item doubles as the means by which the server control scripts decide which playerfiles to delete and which to keep.
  3. passport = passport or {}
  4. passport.recalls = passport.recalls or {}
  5. passport.players = passport.players or {}
  6. passport.registered_players = passport.registered_players or {} -- Cache of registered players.
  7. passport.keyed_players = passport.keyed_players or {}
  8. passport.modpath = minetest.get_modpath("passport")
  9. -- List of players with open keys.
  10. -- On formspec close, playername should be removed and close-sound played.
  11. passport.open_keys = passport.open_keys or {}
  12. local PASSPORT_TELEPORT_RANGE = 3000 -- 3 Kilometers.
  13. minetest.register_privilege("recall", {
  14. description = "Player can request a teleport back to the city.",
  15. give_to_singleplayer = false,
  16. })
  17. function passport.is_passport(name)
  18. if name == "passport:passport" then
  19. return true
  20. end
  21. if name == "passport:passport_adv" then
  22. return true
  23. end
  24. return false
  25. end
  26. -- Public API function. Other mods are expected to register recall locations.
  27. passport.register_recall = function(recalldef)
  28. local name = recalldef.name
  29. local position = recalldef.position
  30. local min_dist = recalldef.min_dist
  31. local on_success = recalldef.on_success
  32. local on_failure = recalldef.on_failure
  33. local tname = recalldef.codename
  34. local suppress = recalldef.suppress
  35. local idx = #(passport.recalls) + 1
  36. local code = "v" .. idx .. ""
  37. passport.recalls[idx] = {
  38. name = name,
  39. position = position,
  40. code = code,
  41. on_success = on_success,
  42. min_dist = min_dist,
  43. on_failure = on_failure,
  44. tname = tname,
  45. suppress = suppress,
  46. }
  47. end
  48. passport.compose_formspec = function(pname)
  49. local buttons = ""
  50. local i = 1
  51. for k, v in pairs(passport.recalls) do
  52. local n = v.name
  53. local c = v.code
  54. buttons = buttons .. "button_exit[6," .. (i-0.3) .. ";3,1;" .. c .. ";" .. n .. "]"
  55. i = i + 1
  56. end
  57. local boolecho = 'true'
  58. local echo = chat_echo.get_echo(pname)
  59. if echo == true then boolecho = 'true' end
  60. if echo == false then boolecho = 'false' end
  61. local boolparticle = 'true'
  62. local particle = default.particles_enabled_for(pname)
  63. if particle == true then boolparticle = 'true' end
  64. if particle == false then boolparticle = 'false' end
  65. local formspec = "size[10,7]" ..
  66. default.gui_bg ..
  67. default.gui_bg_img ..
  68. default.gui_slots ..
  69. "label[1,0.0;" ..
  70. minetest.formspec_escape("Active Interface to your Key of Citizenship. Owner: <" .. rename.gpn(pname) .. ">") .. "]" ..
  71. buttons ..
  72. "button_exit[1,5.7;2,1;exit;Close]" ..
  73. "button_exit[1,2.7;2,1;mapfix;Fix Map]" ..
  74. "button[3,0.7;2,1;email;Mail]" ..
  75. "button[1,1.7;2,1;survivalist;Survivalist]" ..
  76. "button[3,1.7;2,1;rename;Nickname]" ..
  77. "button[3,2.7;2,1;chatfilter;Chat Filter]" ..
  78. "button[1,0.7;2,1;marker;Markers]" ..
  79. "tooltip[email;Hold 'E' while using the Key to access directly.]" ..
  80. "tooltip[marker;Hold 'sneak' while using the Key to access directly.]" ..
  81. "checkbox[3,5.0;togglechat;Enable Echo;" ..
  82. boolecho .. "]" ..
  83. "checkbox[1,5.0;toggleparticles;Want Particles;" ..
  84. boolparticle .. "]" ..
  85. "tooltip[togglechat;" ..
  86. minetest.formspec_escape(
  87. "Toggle whether the server should echo your chat back to your client.\n" ..
  88. "Newer clients should keep this checked.") .. "]" ..
  89. "tooltip[toggleparticles;" ..
  90. minetest.formspec_escape(
  91. "Toggle whether the server should send game-enhancing particle effects to your client.\n" ..
  92. "Sometimes these are purely for visual effect, sometimes they have gameplay meaning ...") .. "]"
  93. if survivalist.player_beat_cave_challenge(pname) then
  94. formspec = formspec .. "button[1,3.7;2,1;jaunt;Jaunt]"
  95. end
  96. if survivalist.player_beat_nether_challenge(pname) then
  97. if cloaking.is_cloaked(pname) then
  98. formspec = formspec .. "button[3,3.7;2,1;cloak;Uncloak]"
  99. else
  100. formspec = formspec .. "button[3,3.7;2,1;cloak;Cloak]"
  101. end
  102. end
  103. for i=1, 7, 1 do
  104. local name = "xdecor:ivy"
  105. if i == 1 then
  106. name = "passport:passport_adv"
  107. elseif i == 7 then
  108. name = "default:sword_steel"
  109. end
  110. formspec = formspec .. "item_image[0," .. i-1 .. ";1,1;" .. name .. "]"
  111. formspec = formspec .. "item_image[9," .. i-1 .. ";1,1;" .. name .. "]"
  112. end
  113. return formspec
  114. end
  115. passport.show_formspec = function(pname)
  116. local formspec = passport.compose_formspec(pname)
  117. minetest.show_formspec(pname, "passport:passport", formspec)
  118. end
  119. passport.on_use = function(itemstack, user, pointed)
  120. local changed = false
  121. if user and user:is_player() then
  122. local pname = user:get_player_name()
  123. -- Check (and if needed, set) owner.
  124. local meta = itemstack:get_meta()
  125. local owner = meta:get_string("owner")
  126. if owner == "" then
  127. owner = pname
  128. -- Store owner and data of activation.
  129. meta:set_string("owner", owner)
  130. meta:set_int("date", os.time())
  131. minetest.after(3, function()
  132. minetest.chat_send_player(pname, "# Server: A newly initialized Key of Citizenship begins to emit a soft blue glow.")
  133. end)
  134. changed = true
  135. end
  136. -- Initialize data if not set.
  137. if meta:get_int("date") == 0 then
  138. meta:set_int("date", os.time())
  139. changed = true
  140. end
  141. if owner ~= pname then
  142. minetest.chat_send_player(pname, "# Server: This Key was initialized by someone else! You cannot access it.")
  143. easyvend.sound_error(pname)
  144. return
  145. end
  146. -- Record number of uses.
  147. meta:set_int("uses", meta:get_int("uses") + 1)
  148. changed = true
  149. local control = user:get_player_control()
  150. if control.sneak then
  151. marker.show_formspec(pname)
  152. elseif control.aux1 then
  153. mailgui.show_formspec(pname)
  154. else
  155. -- Show KoC interface.
  156. passport.show_formspec(pname)
  157. end
  158. passport.open_keys[pname] = true
  159. local ppos = user:get_pos()
  160. minetest.after(0, ambiance.sound_play, "fancy_chime1", ppos, 1.0, 20, "", false)
  161. end
  162. if changed then
  163. return itemstack
  164. end
  165. end
  166. passport.on_use_simple = function(itemstack, user, pointed)
  167. if user and user:is_player() then
  168. minetest.chat_send_player(user:get_player_name(),
  169. "# Server: This awkward chunk of reflective metal seems to mock you, " ..
  170. "yet remains strangely inert. Perhaps it can be upgraded?")
  171. end
  172. return itemstack
  173. end
  174. passport.on_receive_fields = function(player, formname, fields)
  175. if formname ~= "passport:passport" then return end
  176. local pname = player:get_player_name()
  177. if fields.mapfix then
  178. mapfix.command(pname, "")
  179. return true
  180. end
  181. if fields.email then
  182. mailgui.show_formspec(pname)
  183. return true
  184. end
  185. if fields.chatfilter then
  186. chat_controls.show_formspec(pname)
  187. return true
  188. end
  189. if fields.marker then
  190. marker.show_formspec(pname)
  191. return true
  192. end
  193. if fields.jaunt and survivalist.player_beat_cave_challenge(pname) then
  194. -- Jaunt code performs its own security validation.
  195. jaunt.show_formspec(pname)
  196. return true
  197. end
  198. if fields.cloak and survivalist.player_beat_nether_challenge(pname) then
  199. -- Security check to make sure player can use this feature.
  200. if not passport.player_has_key(pname) then
  201. return true
  202. end
  203. if not survivalist.player_beat_nether_challenge(pname) then
  204. return true
  205. end
  206. cloaking.toggle_cloak(pname)
  207. passport.show_formspec(pname) -- Reshow formspec.
  208. return true
  209. end
  210. if fields.survivalist then
  211. survivalist.show_formspec(pname)
  212. return true
  213. end
  214. if fields.rename then
  215. rename.show_formspec(pname)
  216. return true
  217. end
  218. if fields.togglechat then
  219. if fields.togglechat == 'true' then
  220. chat_echo.set_echo(pname, true)
  221. elseif fields.togglechat == 'false' then
  222. chat_echo.set_echo(pname, false)
  223. end
  224. passport.show_formspec(pname) -- Reshow formspec.
  225. return true
  226. end
  227. if fields.toggleparticles then
  228. if fields.toggleparticles == 'true' then
  229. default.enable_particles_for(pname, true)
  230. elseif fields.toggleparticles == 'false' then
  231. default.enable_particles_for(pname, false)
  232. end
  233. passport.show_formspec(pname) -- Reshow formspec.
  234. return true
  235. end
  236. for k, v in pairs(passport.recalls) do
  237. local c = v.code
  238. if fields[c] then
  239. if not minetest.check_player_privs(pname, {recall=true}) then
  240. minetest.chat_send_player(pname, "# Server: You are not authorized to request transport.")
  241. easyvend.sound_error(pname)
  242. return true
  243. end
  244. passport.attempt_teleport(player, v)
  245. return true
  246. end
  247. end
  248. return true
  249. end
  250. passport.attempt_teleport = function(player, data)
  251. local pp = player:get_pos()
  252. local nn = player:get_player_name()
  253. local tg = data.position(player)
  254. if rc.current_realm_at_pos(tg) ~= rc.current_realm_at_pos(pp) then
  255. minetest.chat_send_player(nn, "# Server: Beacon signal is in another dimension!")
  256. -- Wrong realm.
  257. return
  258. end
  259. for k, v in pairs(passport.recalls) do
  260. if v.suppress then
  261. if v.suppress(nn) then
  262. minetest.chat_send_player(nn, "# Server: Beacon signal is suppressed and cannot be triangulated.")
  263. easyvend.sound_error(nn)
  264. return -- Someone suppressed the ability to teleport.
  265. end
  266. end
  267. end
  268. for k, v in pairs(passport.recalls) do
  269. if vector.distance(pp, v.position(player)) < v.min_dist then
  270. if data.on_failure then data.on_failure(nn, "too_close", v.tname) end
  271. minetest.chat_send_player(nn, "# Server: You are too close to a nearby beacon signal.")
  272. easyvend.sound_error(nn)
  273. return -- To close to a beacon.
  274. end
  275. end
  276. if vector.distance(pp, tg) > PASSPORT_TELEPORT_RANGE then
  277. if data.on_failure then data.on_failure(nn, "too_far", data.tname) end
  278. local dist = math.floor(vector.distance(pp, tg))
  279. minetest.chat_send_player(nn, "# Server: Beacon signal is too weak. You are out of range: distance " .. dist/1000 .. " kilometers.")
  280. easyvend.sound_error(nn)
  281. return -- To far from requested beacon.
  282. end
  283. if passport.players[nn] then
  284. if data.on_failure then data.on_failure(nn, "in_progress", data.tname) end
  285. minetest.chat_send_player(nn, "# Server: Signal triangulation already underway; stand by.")
  286. return -- Teleport already in progress.
  287. end
  288. -- Everything satisfied. Let's teleport!
  289. local dist = vector.distance(pp, tg)
  290. local time = math.ceil(math.sqrt(dist / 10))
  291. minetest.chat_send_player(nn, "# Server: Recall beacon signal requires " .. time .. " seconds to triangulate; please hold still.")
  292. passport.players[nn] = true
  293. local pos = vector.add(tg, {x=math.random(-2, 2), y=0, z=math.random(-2, 2)})
  294. minetest.after(time, passport.do_teleport, nn, pp, pos, data.on_success)
  295. end
  296. -- Called from minetest.after() to actually execute a teleport.
  297. passport.do_teleport = function(name, start_pos, target_pos, func)
  298. passport.players[name] = nil
  299. local player = minetest.get_player_by_name(name)
  300. if player and player:is_player() then
  301. if sheriff.is_cheater(name) then
  302. if sheriff.punish_probability(name) then
  303. sheriff.punish_player(name)
  304. return
  305. end
  306. end
  307. if vector.distance(player:getpos(), start_pos) < 0.1 then
  308. --local fwrap = function(...)
  309. -- minetest.chat_send_player(name, "# Server: Transport successful.")
  310. -- portal_sickness.on_use_portal(name)
  311. -- return func(...)
  312. --end
  313. preload_tp.preload_and_teleport(name, target_pos, 32, nil, func, name, false)
  314. --if func then func(name) end
  315. else
  316. minetest.chat_send_player(name, "# Server: Unable to accurately triangulate beacon position! Aborted.")
  317. easyvend.sound_error(name)
  318. end
  319. end
  320. end
  321. function passport.exec_spawn(name, param)
  322. local player = minetest.get_player_by_name(name)
  323. if not player then return false end
  324. local pos = vector.round(player:get_pos())
  325. if jail.suppress(name) then
  326. return true
  327. end
  328. local target = randspawn.get_respawn_pos(pos, name)
  329. if vector.distance(pos, target) < 20 then
  330. minetest.chat_send_player(name, "# Server: Too close to the spawnpoint!")
  331. easyvend.sound_error(name)
  332. return true
  333. end
  334. if sheriff.is_cheater(name) then
  335. if sheriff.punish_probability(name) then
  336. sheriff.punish_player(name)
  337. return true
  338. end
  339. end
  340. if vector.distance(pos, target) <= 256 then
  341. randspawn.reposition_player(name, pos)
  342. minetest.after(1, function()
  343. minetest.chat_send_player(name, "# Server: You have been returned to the spawnpoint.")
  344. portal_sickness.on_use_portal(name)
  345. end)
  346. else
  347. minetest.chat_send_player(name, "# Server: You are too far from the spawnpoint!")
  348. easyvend.sound_error(name)
  349. end
  350. return true
  351. end
  352. function passport.award_cash(pname, player)
  353. local inv = player:get_inventory()
  354. if not inv then
  355. return
  356. end
  357. local cash_stack = ItemStack("currency:minegeld_20 10")
  358. local prot_stack = ItemStack("protector:protect3")
  359. local cash_left = inv:add_item("main", cash_stack)
  360. local prot_left = inv:add_item("main", prot_stack)
  361. minetest.chat_send_player(pname,
  362. core.get_color_escape_sequence("#ffff00") ..
  363. "# Server: Bank notice - As this is the first recorded time you have obtained a PoC, the Colony grants you 200 minegeld.")
  364. minetest.chat_send_player(pname,
  365. core.get_color_escape_sequence("#ffff00") ..
  366. "# Server: This is roughly equivalent to 8 gold ingots according to the Guild of Weights and Measures.")
  367. if cash_left:is_empty() and prot_left:is_empty() then
  368. minetest.chat_send_player(pname,
  369. core.get_color_escape_sequence("#ffff00") ..
  370. "# Server: The cash has been directly added to your inventory. Trade wisely and well, Adventurer!")
  371. else
  372. local pos = vector.round(player:get_pos())
  373. pos.y = pos.y + 1
  374. if not cash_left:is_empty() then
  375. minetest.add_item(pos, cash_left)
  376. end
  377. if not prot_left:is_empty() then
  378. minetest.add_item(pos, prot_left)
  379. end
  380. minetest.chat_send_player(pname,
  381. core.get_color_escape_sequence("#ffff00") ..
  382. "# Server: The cash could not be added to your inventory (no space). Check near your position for drops.")
  383. end
  384. end
  385. function passport.on_craft(itemstack, player, old_craft_grid, craft_inv)
  386. local name = itemstack:get_name()
  387. if name == "passport:passport_adv" then
  388. local pname = player:get_player_name()
  389. local meta = itemstack:get_meta()
  390. -- Store owner and data of activation.
  391. meta:set_string("owner", pname)
  392. meta:set_int("date", os.time())
  393. minetest.after(3, function()
  394. minetest.chat_send_player(pname,
  395. "# Server: A newly fashioned Key of Citizenship emits a soft blue glow mere moments after its crafter finishes the device.")
  396. end)
  397. -- Clear cache of player registration.
  398. passport.keyed_players[pname] = nil
  399. passport.registered_players[pname] = nil
  400. elseif name == "passport:passport" then
  401. -- Check if this is the first time this player has crafted a PoC.
  402. local pname = player:get_player_name()
  403. local meta = passport.modstorage
  404. local key = pname .. ":crafted_poc"
  405. if meta:get_int(key) == 0 then
  406. meta:set_int(key, 1)
  407. passport.award_cash(pname, player)
  408. end
  409. -- Clear cache of player registration.
  410. passport.keyed_players[pname] = nil
  411. passport.registered_players[pname] = nil
  412. end
  413. end
  414. if not passport.registered then
  415. -- Obtain modstorage.
  416. passport.modstorage = minetest.get_mod_storage()
  417. -- Keep this in inventory to prevent deletion.
  418. minetest.register_craftitem("passport:passport", {
  419. description = "Proof Of Citizenship\n\n" ..
  420. "Keep this in your MAIN inventory at ALL times!\n" ..
  421. "This preserves your Account during server purge.\n" ..
  422. "It cannot be stolen or lost by dying.",
  423. inventory_image = "default_bronze_block.png^default_tool_steelpick.png",
  424. stack_max = 1,
  425. on_use = function(...) return passport.on_use_simple(...) end,
  426. })
  427. -- Keep this in inventory to prevent deletion.
  428. minetest.register_craftitem("passport:passport_adv", {
  429. description = "Key Of Citizenship\n\n" ..
  430. "Keep this in your MAIN inventory at ALL times!\n" ..
  431. "This preserves your Account during server purge.\n" ..
  432. "It cannot be stolen or lost by dying.",
  433. inventory_image = "adv_passport.png",
  434. stack_max = 1,
  435. on_use = function(...) return passport.on_use(...) end,
  436. })
  437. minetest.register_craft({
  438. output = 'passport:passport 1',
  439. recipe = {
  440. {'default:copper_ingot', 'default:copper_ingot', 'default:copper_ingot'},
  441. },
  442. })
  443. minetest.register_craft({
  444. output = 'passport:passport_adv 1',
  445. recipe = {
  446. {'mese_crystals:zentamine', 'passport:passport', 'mese_crystals:zentamine'},
  447. {'techcrafts:control_logic_unit', 'quartz:quartz_crystal_piece', 'techcrafts:control_logic_unit'},
  448. {'dusts:diamond_shard', 'techcrafts:control_logic_unit', 'default:obsidian_shard'},
  449. },
  450. })
  451. minetest.register_on_player_receive_fields(function(...) return passport.on_receive_fields(...) end)
  452. minetest.register_alias("command_tokens:live_preserver", "passport:passport")
  453. -- It's very common for servers to have a /spawn command. This one is limited.
  454. minetest.register_chatcommand("spawn", {
  455. params = "",
  456. description = "Teleport the player back to the spawnpoint. This only works within 256 meters of spawn.",
  457. privs = {recall=true},
  458. func = function(...)
  459. return passport.exec_spawn(...)
  460. end,
  461. })
  462. -- Let players used to using /recall know that gameplay has changed in this respect.
  463. minetest.register_chatcommand("recall", {
  464. params = "",
  465. description = "Teleport the player back to the spawnpoint. This only works within 256 meters of spawn.",
  466. privs = {recall=true},
  467. func = function(...)
  468. return passport.exec_spawn(...)
  469. end,
  470. })
  471. minetest.register_on_leaveplayer(function(...)
  472. return passport.on_leaveplayer(...)
  473. end)
  474. minetest.register_on_craft(function(...) passport.on_craft(...) end)
  475. passport.registered = true
  476. end
  477. -- This function may be called serveral times on player-login and other times.
  478. -- We cache the result on first call.
  479. passport.player_registered = function(pname)
  480. local all_players = passport.registered_players
  481. -- Read cache if available.
  482. local registered = all_players[pname]
  483. if registered ~= nil then
  484. return registered
  485. end
  486. local player = minetest.get_player_by_name(pname)
  487. if player and player:is_player() then
  488. local inv = player:get_inventory()
  489. if inv then
  490. if inv:contains_item("main", "passport:passport") or inv:contains_item("main", "passport:passport_adv") then
  491. all_players[pname] = true -- Cache for next time.
  492. return true
  493. else
  494. all_players[pname] = false -- Cache for next time.
  495. return false
  496. end
  497. end
  498. end
  499. -- Return false, but don't cache the value -- we could not confirm it!
  500. return false
  501. end
  502. -- This checks (and caches the result!) of whether the player has a KEY OF CITIZENSHIP.
  503. -- Second param is optional, may be nil.
  504. passport.player_has_key = function(pname, player)
  505. local all_players = passport.keyed_players
  506. -- Read cache if available.
  507. local keyed = all_players[pname]
  508. if keyed ~= nil then
  509. return keyed
  510. end
  511. local pref = player or minetest.get_player_by_name(pname)
  512. if pref then
  513. local inv = pref:get_inventory()
  514. if inv then
  515. if inv:contains_item("main", "passport:passport_adv") then
  516. all_players[pname] = true -- Cache for next time.
  517. return true
  518. else
  519. all_players[pname] = false -- Cache for next time.
  520. return false
  521. end
  522. end
  523. end
  524. -- Return false, but don't cache the value -- we could not confirm it!
  525. return false
  526. end
  527. function passport.on_leaveplayer(player, timeout)
  528. local pname = player:get_player_name()
  529. -- Remove cache of player registration.
  530. passport.registered_players[pname] = nil
  531. passport.keyed_players[pname] = nil
  532. end
  533. if minetest.get_modpath("reload") then
  534. local c = "passport:core"
  535. local f = passport.modpath .. "/init.lua"
  536. if not reload.file_registered(c) then
  537. reload.register_file(c, f, false)
  538. end
  539. end