init.lua 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  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. if not minetest.global_exists("passport") then passport = {} end
  4. passport.recalls = passport.recalls or {}
  5. passport.players = passport.players or {}
  6. passport.player_recalls = passport.player_recalls or {}
  7. passport.registered_players = passport.registered_players or {} -- Cache of registered players.
  8. passport.keyed_players = passport.keyed_players or {}
  9. passport.modpath = minetest.get_modpath("passport")
  10. -- List of players with open keys.
  11. -- On formspec close, playername should be removed and close-sound played.
  12. passport.open_keys = passport.open_keys or {}
  13. -- Localize for performance.
  14. local vector_distance = vector.distance
  15. local vector_round = vector.round
  16. local vector_add = vector.add
  17. local math_floor = math.floor
  18. local math_random = math.random
  19. local PASSPORT_TELEPORT_RANGE = 750
  20. minetest.register_privilege("recall", {
  21. description = "Player can request a teleport to nearby recall beacons.",
  22. give_to_singleplayer = false,
  23. })
  24. function passport.is_passport(name)
  25. if name == "passport:passport" then
  26. return true
  27. end
  28. if name == "passport:passport_adv" then
  29. return true
  30. end
  31. return false
  32. end
  33. -- Public API function. Only used by jail mod.
  34. passport.register_recall = function(recalldef)
  35. local name = recalldef.name
  36. local position = recalldef.position
  37. local min_dist = recalldef.min_dist
  38. local on_success = recalldef.on_success
  39. local on_failure = recalldef.on_failure
  40. local tname = recalldef.codename
  41. local suppress = recalldef.suppress
  42. local idx = #(passport.recalls) + 1
  43. local code = "z" .. idx .. ""
  44. passport.recalls[idx] = {
  45. name = name,
  46. position = position,
  47. code = code,
  48. on_success = on_success,
  49. min_dist = min_dist,
  50. on_failure = on_failure,
  51. tname = tname,
  52. suppress = suppress,
  53. }
  54. end
  55. function passport.beacons_to_recalls(beacons)
  56. local recalls = {}
  57. for k, v in ipairs(beacons) do
  58. local idx = #recalls + 1
  59. local real_label = rc.pos_to_string(v.pos)
  60. if v.name ~= nil and v.name ~= "" then
  61. real_label = v.name
  62. end
  63. recalls[idx] = {
  64. name = real_label,
  65. position = function() return vector_add(v.pos, {x=0, y=1, z=0}) end,
  66. code = "v" .. idx .. "",
  67. min_dist = 30,
  68. }
  69. end
  70. return recalls
  71. end
  72. passport.compose_formspec = function(pname)
  73. local buttons = ""
  74. local i = 1
  75. for k, v in pairs(passport.recalls) do
  76. local n = v.name
  77. local c = v.code
  78. if v.tname == "jail:jail" then
  79. buttons = buttons .. "button_exit[3,5.7;2,1;" .. c .. ";" .. n .. "]"
  80. else
  81. buttons = buttons .. "button_exit[6," .. (i-0.3) .. ";3,1;" .. c .. ";" .. n .. "]"
  82. i = i + 1
  83. end
  84. end
  85. local pref = minetest.get_player_by_name(pname)
  86. local beacons = {}
  87. if pref then
  88. local player_pos = pref:get_pos()
  89. -- Shall return an empty table if there are no beacons.
  90. beacons = teleports.nearest_beacons_to_position(player_pos, 6, 1000)
  91. end
  92. passport.player_recalls[pname] = passport.beacons_to_recalls(beacons)
  93. local h = 1
  94. for k, v in ipairs(passport.player_recalls[pname]) do
  95. local n = v.name
  96. local c = v.code
  97. buttons = buttons .. "button_exit[6," .. (h-0.3) .. ";3,1;" .. c .. ";" .. n .. "]"
  98. h = h + 1
  99. end
  100. local boolecho = 'true'
  101. local echo = chat_echo.get_echo(pname)
  102. if echo == true then boolecho = 'true' end
  103. if echo == false then boolecho = 'false' end
  104. local boolparticle = 'true'
  105. local particle = default.particles_enabled_for(pname)
  106. if particle == true then boolparticle = 'true' end
  107. if particle == false then boolparticle = 'false' end
  108. local formspec = "size[10,7]" ..
  109. default.gui_bg ..
  110. default.gui_bg_img ..
  111. default.gui_slots ..
  112. "label[1,0.0;" ..
  113. minetest.formspec_escape("Key of Citizenship Interface") .. "]" ..
  114. "label[6,0.0;" ..
  115. minetest.formspec_escape("Recalls Nearby (" .. #beacons .. ")") .. "]" ..
  116. buttons ..
  117. "button_exit[1,5.7;2,1;exit;Close]" ..
  118. "button_exit[1,2.7;2,1;mapfix;Fix Map]" ..
  119. "button[3,0.7;2,1;email;Mail]" ..
  120. "button[1,1.7;2,1;survivalist;Survivalist]" ..
  121. "button[3,1.7;2,1;rename;Nickname]" ..
  122. "button[3,2.7;2,1;chatfilter;Chat Filter]" ..
  123. "button[1,0.7;2,1;marker;Markers]" ..
  124. "tooltip[email;Hold 'E' while using the Key to access directly.]" ..
  125. "tooltip[marker;Hold 'sneak' while using the Key to access directly.]" ..
  126. "checkbox[3,5.0;togglechat;Text Echo;" ..
  127. boolecho .. "]" ..
  128. "checkbox[1,5.0;toggleparticles;Particles;" ..
  129. boolparticle .. "]" ..
  130. "tooltip[togglechat;" ..
  131. minetest.formspec_escape(
  132. "Toggle whether the server should echo your chat back to your client.\n" ..
  133. "Newer clients should keep this checked.") .. "]" ..
  134. "tooltip[toggleparticles;" ..
  135. minetest.formspec_escape(
  136. "Toggle whether the server should send game-enhancing particle effects to your client.\n" ..
  137. "Sometimes these are purely for visual effect, sometimes they have gameplay meaning ...") .. "]"
  138. -- Special abilities are revoked for cheaters.
  139. if not sheriff.is_cheater(pname) then
  140. if survivalist.player_beat_cave_challenge(pname) then
  141. formspec = formspec .. "button[1,3.7;2,1;jaunt;Jaunt]"
  142. end
  143. if survivalist.player_beat_nether_challenge(pname) then
  144. if cloaking.is_cloaked(pname) then
  145. formspec = formspec .. "button[3,3.7;2,1;cloak;Uncloak]"
  146. else
  147. formspec = formspec .. "button[3,3.7;2,1;cloak;Cloak]"
  148. end
  149. end
  150. end
  151. for i=1, 7, 1 do
  152. local name = "xdecor:ivy"
  153. if i == 1 then
  154. name = "passport:passport_adv"
  155. elseif i == 7 then
  156. name = "default:sword_steel"
  157. end
  158. formspec = formspec .. "item_image[0," .. i-1 .. ";1,1;" .. name .. "]"
  159. formspec = formspec .. "item_image[9," .. i-1 .. ";1,1;" .. name .. "]"
  160. end
  161. local status_info = {}
  162. if pref then
  163. local player_pos = pref:get_pos()
  164. local city_info = city_block.city_info(player_pos)
  165. if city_info then
  166. status_info[#status_info + 1] = "Lawful Zone"
  167. local count = 0
  168. local targets = minetest.get_connected_players()
  169. for k, v in ipairs(targets) do
  170. -- Ignore admin, don't count self.
  171. if not gdac.player_is_admin(v) and v ~= pref then
  172. local tpos = v:get_pos()
  173. -- Ignore far, ignore dead.
  174. if vector_distance(player_pos, tpos) < 100 and v:get_hp() > 0 then
  175. count = count + 1
  176. end
  177. end
  178. end
  179. status_info[#status_info + 1] = (count .. " nearby")
  180. end
  181. end
  182. if cloaking.is_cloaked(pname) then
  183. status_info[#status_info + 1] = "Cloaked"
  184. elseif player_labels.query_nametag_onoff(pname) == false then
  185. status_info[#status_info + 1] = "Name OFF"
  186. end
  187. local scale = 500
  188. status_info[#status_info + 1] = tostring(math_floor(pref:get_hp() / scale) .. " HP")
  189. status_info[#status_info + 1] = tostring("Respawns: " .. beds.get_respawn_count(pname))
  190. -- Status info.
  191. formspec = formspec .. "label[1,6.6;Status: " ..
  192. minetest.formspec_escape(table.concat(status_info, " | ")) .. "]"
  193. return formspec
  194. end
  195. passport.show_formspec = function(pname)
  196. local formspec = passport.compose_formspec(pname)
  197. minetest.show_formspec(pname, "passport:passport", formspec)
  198. end
  199. passport.on_use = function(itemstack, user, pointed)
  200. local changed = false
  201. if user and user:is_player() then
  202. local pname = user:get_player_name()
  203. -- Check (and if needed, set) owner.
  204. local meta = itemstack:get_meta()
  205. local owner = meta:get_string("owner")
  206. if owner == "" then
  207. owner = pname
  208. -- Store owner and data of activation.
  209. meta:set_string("owner", owner)
  210. meta:set_int("date", os.time())
  211. minetest.after(3, function()
  212. minetest.chat_send_player(pname, "# Server: A newly initialized Key of Citizenship begins to emit a soft blue glow.")
  213. end)
  214. changed = true
  215. end
  216. -- Initialize data if not set.
  217. if meta:get_int("date") == 0 then
  218. meta:set_int("date", os.time())
  219. changed = true
  220. end
  221. if owner ~= pname then
  222. minetest.chat_send_player(pname, "# Server: This Key was initialized by someone else! You cannot access it.")
  223. easyvend.sound_error(pname)
  224. return
  225. end
  226. -- Record number of uses.
  227. meta:set_int("uses", meta:get_int("uses") + 1)
  228. changed = true
  229. local control = user:get_player_control()
  230. if control.sneak then
  231. marker.show_formspec(pname)
  232. elseif control.aux1 then
  233. mailgui.show_formspec(pname)
  234. else
  235. -- Show KoC interface.
  236. passport.show_formspec(pname)
  237. end
  238. passport.open_keys[pname] = true
  239. local ppos = user:get_pos()
  240. minetest.after(0, ambiance.sound_play, "fancy_chime1", ppos, 1.0, 20, "", false)
  241. end
  242. if changed then
  243. return itemstack
  244. end
  245. end
  246. passport.on_use_simple = function(itemstack, user, pointed)
  247. if user and user:is_player() then
  248. minetest.chat_send_player(user:get_player_name(),
  249. "# Server: This awkward chunk of reflective metal seems to mock you, " ..
  250. "yet remains strangely inert. Perhaps it can be upgraded?")
  251. end
  252. return itemstack
  253. end
  254. passport.on_receive_fields = function(player, formname, fields)
  255. if formname ~= "passport:passport" then return end
  256. local pname = player:get_player_name()
  257. if fields.mapfix then
  258. mapfix.command(pname, "")
  259. return true
  260. end
  261. if fields.email then
  262. mailgui.show_formspec(pname)
  263. return true
  264. end
  265. if fields.chatfilter then
  266. chat_controls.show_formspec(pname)
  267. return true
  268. end
  269. if fields.marker then
  270. marker.show_formspec(pname)
  271. return true
  272. end
  273. if fields.jaunt and survivalist.player_beat_cave_challenge(pname) then
  274. -- Jaunt code performs its own security validation.
  275. jaunt.show_formspec(pname)
  276. return true
  277. end
  278. if fields.cloak and survivalist.player_beat_nether_challenge(pname) then
  279. -- Security check to make sure player can use this feature.
  280. if not passport.player_has_key(pname) then
  281. return true
  282. end
  283. if not survivalist.player_beat_nether_challenge(pname) then
  284. return true
  285. end
  286. -- Cloaking ability is revoked for cheaters.
  287. if sheriff.is_cheater(pname) then
  288. return true
  289. end
  290. cloaking.toggle_cloak(pname)
  291. passport.show_formspec(pname) -- Reshow formspec.
  292. return true
  293. end
  294. if fields.survivalist then
  295. survivalist.show_formspec(pname)
  296. return true
  297. end
  298. if fields.rename then
  299. rename.show_formspec(pname)
  300. return true
  301. end
  302. if fields.togglechat then
  303. if fields.togglechat == 'true' then
  304. chat_echo.set_echo(pname, true)
  305. elseif fields.togglechat == 'false' then
  306. chat_echo.set_echo(pname, false)
  307. end
  308. passport.show_formspec(pname) -- Reshow formspec.
  309. return true
  310. end
  311. if fields.toggleparticles then
  312. if fields.toggleparticles == 'true' then
  313. default.enable_particles_for(pname, true)
  314. elseif fields.toggleparticles == 'false' then
  315. default.enable_particles_for(pname, false)
  316. end
  317. passport.show_formspec(pname) -- Reshow formspec.
  318. return true
  319. end
  320. if passport.player_recalls[pname] then
  321. for k, v in ipairs(passport.player_recalls[pname]) do
  322. local c = v.code
  323. if fields[c] then
  324. if not minetest.check_player_privs(pname, {recall=true}) then
  325. minetest.chat_send_player(pname, "# Server: You are not authorized to request transport.")
  326. easyvend.sound_error(pname)
  327. return true
  328. end
  329. passport.attempt_teleport(player, v)
  330. return true
  331. end
  332. end
  333. end
  334. for k, v in ipairs(passport.recalls) do
  335. local c = v.code
  336. if fields[c] then
  337. if not minetest.check_player_privs(pname, {recall=true}) then
  338. minetest.chat_send_player(pname, "# Server: You are not authorized to request transport.")
  339. easyvend.sound_error(pname)
  340. return true
  341. end
  342. passport.attempt_teleport(player, v)
  343. return true
  344. end
  345. end
  346. return true
  347. end
  348. passport.attempt_teleport = function(player, data)
  349. local pp = player:get_pos()
  350. local nn = player:get_player_name()
  351. local tg = data.position(player) -- May return nil.
  352. local recalls = passport.player_recalls[nn]
  353. if not recalls then
  354. minetest.chat_send_player(nn, "# Server: No data associated with beacon signal.")
  355. return
  356. end
  357. if not tg then
  358. minetest.chat_send_player(nn, "# Server: Beacon does not provide position data. Aborting.")
  359. return
  360. end
  361. if rc.current_realm_at_pos(tg) ~= rc.current_realm_at_pos(pp) then
  362. minetest.chat_send_player(nn, "# Server: Beacon signal is in another dimension!")
  363. -- Wrong realm.
  364. return
  365. end
  366. for k, v in ipairs(recalls) do
  367. if v.suppress then
  368. if v.suppress(nn) then
  369. minetest.chat_send_player(nn, "# Server: Beacon signal is jammed and cannot be triangulated.")
  370. easyvend.sound_error(nn)
  371. return -- Someone suppressed the ability to teleport.
  372. end
  373. end
  374. end
  375. -- Is player too close to custom (player-built) recalls?
  376. for k, v in ipairs(recalls) do
  377. local vpp = v.position(player) -- May return nil.
  378. if vpp then
  379. if vector_distance(pp, vpp) < v.min_dist then
  380. if data.on_failure then data.on_failure(nn, "too_close", v.tname) end
  381. minetest.chat_send_player(nn, "# Server: You are too close to a nearby beacon signal.")
  382. easyvend.sound_error(nn)
  383. return -- Too close to a beacon.
  384. end
  385. end
  386. end
  387. -- Is player too close to builtin (server) recalls?
  388. for k, v in ipairs(passport.recalls) do
  389. local vpp = v.position(player) -- May return nil.
  390. if vpp then
  391. if vector_distance(pp, vpp) < v.min_dist then
  392. if data.on_failure then data.on_failure(nn, "too_close", v.tname) end
  393. minetest.chat_send_player(nn, "# Server: You are too close to a nearby beacon signal.")
  394. easyvend.sound_error(nn)
  395. return -- Too close to a beacon.
  396. end
  397. end
  398. end
  399. if vector_distance(pp, tg) > PASSPORT_TELEPORT_RANGE then
  400. if data.on_failure then data.on_failure(nn, "too_far", data.tname) end
  401. local dist = math_floor(vector_distance(pp, tg))
  402. minetest.chat_send_player(nn, "# Server: Beacon signal is too weak. You are out of range: distance " .. dist/1000 .. " kilometers.")
  403. easyvend.sound_error(nn)
  404. return -- To far from requested beacon.
  405. end
  406. if passport.players[nn] then
  407. if data.on_failure then data.on_failure(nn, "in_progress", data.tname) end
  408. minetest.chat_send_player(nn, "# Server: Signal triangulation already underway; stand by.")
  409. return -- Teleport already in progress.
  410. end
  411. -- Everything satisfied. Let's teleport!
  412. local dist = vector_distance(pp, tg)
  413. local time = math.ceil(math.sqrt(dist / 10))
  414. minetest.chat_send_player(nn, "# Server: Recall beacon signal requires " .. time .. " seconds to triangulate; please hold still.")
  415. passport.players[nn] = true
  416. local pos = vector.add(tg, {x=math_random(-1, 1), y=0, z=math_random(-1, 1)})
  417. minetest.after(time, passport.do_teleport, nn, pp, pos, data.on_success)
  418. end
  419. -- Called from minetest.after() to actually execute a teleport.
  420. passport.do_teleport = function(name, start_pos, target_pos, func)
  421. passport.players[name] = nil
  422. local player = minetest.get_player_by_name(name)
  423. if player and player:is_player() then
  424. if sheriff.is_cheater(name) then
  425. if sheriff.punish_probability(name) then
  426. sheriff.punish_player(name)
  427. return
  428. end
  429. end
  430. if vector_distance(player:get_pos(), start_pos) < 0.1 then
  431. preload_tp.execute({
  432. player_name = name,
  433. target_position = target_pos,
  434. emerge_radius = 32,
  435. post_teleport_callback = func,
  436. callback_param = name,
  437. send_blocks = true,
  438. particle_effects = true,
  439. })
  440. else
  441. minetest.chat_send_player(name, "# Server: Unable to accurately triangulate beacon position! Aborted.")
  442. easyvend.sound_error(name)
  443. end
  444. end
  445. end
  446. function passport.exec_spawn(name, param)
  447. if passport.player_has_key(name) then
  448. minetest.chat_send_player(name, "# Server: This command is newbies-only.")
  449. easyvend.sound_error(name)
  450. return true
  451. end
  452. if default.player_attached[name] then
  453. minetest.chat_send_player(name, "# Server: Cannot teleport to spawn while attached.")
  454. easyvend.sound_error(name)
  455. return true
  456. end
  457. local player = minetest.get_player_by_name(name)
  458. if not player then return false end
  459. local pos = vector_round(player:get_pos())
  460. if jail.suppress(name) then
  461. return true
  462. end
  463. local target = randspawn.get_respawn_pos(pos, name)
  464. if vector_distance(pos, target) < 20 then
  465. minetest.chat_send_player(name, "# Server: Too close to the spawnpoint!")
  466. easyvend.sound_error(name)
  467. return true
  468. end
  469. if sheriff.is_cheater(name) then
  470. if sheriff.punish_probability(name) then
  471. sheriff.punish_player(name)
  472. return true
  473. end
  474. end
  475. if vector_distance(pos, target) <= 256 then
  476. randspawn.reposition_player(name, pos)
  477. minetest.after(1, function()
  478. minetest.chat_send_player(name, "# Server: You have been returned to the spawnpoint.")
  479. portal_sickness.on_use_portal(name)
  480. end)
  481. else
  482. minetest.chat_send_player(name, "# Server: You are too far from the spawnpoint!")
  483. easyvend.sound_error(name)
  484. end
  485. return true
  486. end
  487. function passport.award_cash(pname, player)
  488. local inv = player:get_inventory()
  489. if not inv then
  490. return
  491. end
  492. local cash_stack = ItemStack("currency:minegeld_20 10")
  493. local prot_stack = ItemStack("protector:protect3")
  494. local cash_left = inv:add_item("main", cash_stack)
  495. local prot_left = inv:add_item("main", prot_stack)
  496. minetest.chat_send_player(pname,
  497. core.get_color_escape_sequence("#ffff00") ..
  498. "# Server: Bank notice - As this is the first recorded time you have obtained a PoC, the Colony grants you 200 minegeld.")
  499. if cash_left:is_empty() and prot_left:is_empty() then
  500. minetest.chat_send_player(pname,
  501. core.get_color_escape_sequence("#ffff00") ..
  502. "# Server: The cash has been directly added to your inventory. Trade wisely and well, Adventurer!")
  503. else
  504. local pos = vector_round(player:get_pos())
  505. pos.y = pos.y + 1
  506. if not cash_left:is_empty() then
  507. minetest.add_item(pos, cash_left)
  508. end
  509. if not prot_left:is_empty() then
  510. minetest.add_item(pos, prot_left)
  511. end
  512. minetest.chat_send_player(pname,
  513. core.get_color_escape_sequence("#ffff00") ..
  514. "# Server: The cash could not be added to your inventory (no space). Check near your position for drops.")
  515. end
  516. end
  517. function passport.on_craft(itemstack, player, old_craft_grid, craft_inv)
  518. local name = itemstack:get_name()
  519. if name == "passport:passport_adv" then
  520. local pname = player:get_player_name()
  521. local meta = itemstack:get_meta()
  522. -- Store owner and data of activation.
  523. meta:set_string("owner", pname)
  524. meta:set_int("date", os.time())
  525. minetest.after(3, function()
  526. minetest.chat_send_player(pname,
  527. "# Server: A newly fashioned Key of Citizenship emits a soft blue glow mere moments after its crafter finishes the device.")
  528. end)
  529. -- Clear cache of player registration.
  530. passport.keyed_players[pname] = nil
  531. passport.registered_players[pname] = nil
  532. elseif name == "passport:passport" then
  533. -- Check if this is the first time this player has crafted a PoC.
  534. local pname = player:get_player_name()
  535. local meta = passport.modstorage
  536. local key = pname .. ":crafted_poc"
  537. if meta:get_int(key) == 0 then
  538. meta:set_int(key, 1)
  539. passport.award_cash(pname, player)
  540. end
  541. -- Clear cache of player registration.
  542. passport.keyed_players[pname] = nil
  543. passport.registered_players[pname] = nil
  544. end
  545. end
  546. if not passport.registered then
  547. -- Obtain modstorage.
  548. passport.modstorage = minetest.get_mod_storage()
  549. -- Keep this in inventory to prevent deletion.
  550. minetest.register_craftitem("passport:passport", {
  551. description = "Proof of Citizenship\n\n" ..
  552. "Keep this in your MAIN inventory at ALL times!\n" ..
  553. "This preserves your Account during server purge - it cannot be stolen or lost by dying.\n" ..
  554. "Can be later upgraded into the KEY, which grants many abilities.",
  555. inventory_image = "default_bronze_block.png^default_tool_steelpick.png",
  556. stack_max = 1,
  557. on_use = function(...) return passport.on_use_simple(...) end,
  558. on_drop = function(itemstack, dropper, pos) return itemstack end,
  559. })
  560. -- Keep this in inventory to prevent deletion.
  561. minetest.register_craftitem("passport:passport_adv", {
  562. description = "Key of Citizenship\n\n" ..
  563. "Keep this in your MAIN inventory at ALL times!\n" ..
  564. "This preserves your Account during server purge.\n" ..
  565. "It cannot be stolen or lost by dying.",
  566. inventory_image = "adv_passport.png",
  567. stack_max = 1,
  568. on_use = function(...) return passport.on_use(...) end,
  569. on_drop = function(itemstack, dropper, pos) return itemstack end,
  570. })
  571. minetest.register_craft({
  572. output = 'passport:passport 1',
  573. recipe = {
  574. {'default:copper_ingot', 'default:copper_ingot', 'default:copper_ingot'},
  575. },
  576. })
  577. minetest.register_craft({
  578. output = 'passport:passport_adv 1',
  579. recipe = {
  580. {'default:mese_crystal_fragment', 'passport:passport', 'quartz:quartz_crystal_piece'},
  581. {'techcrafts:control_logic_unit', 'battery:battery', 'techcrafts:control_logic_unit'},
  582. {'dusts:diamond_shard', 'techcrafts:control_logic_unit', 'default:obsidian_shard'},
  583. },
  584. })
  585. minetest.register_on_player_receive_fields(function(...) return passport.on_receive_fields(...) end)
  586. minetest.register_alias("command_tokens:live_preserver", "passport:passport")
  587. -- It's very common for servers to have a /spawn command. This one is limited.
  588. minetest.register_chatcommand("spawn", {
  589. params = "",
  590. description = "Teleport the player back to the spawnpoint. This only works within 256 meters of spawn.",
  591. privs = {recall=true},
  592. func = function(...)
  593. return passport.exec_spawn(...)
  594. end,
  595. })
  596. -- Let players used to using /recall know that gameplay has changed in this respect.
  597. minetest.register_chatcommand("recall", {
  598. params = "",
  599. description = "Teleport the player back to the spawnpoint. This only works within 256 meters of spawn.",
  600. privs = {recall=true},
  601. func = function(...)
  602. return passport.exec_spawn(...)
  603. end,
  604. })
  605. minetest.register_on_leaveplayer(function(...)
  606. return passport.on_leaveplayer(...)
  607. end)
  608. minetest.register_on_craft(function(...) passport.on_craft(...) end)
  609. passport.registered = true
  610. end
  611. -- This function may be called serveral times on player-login and other times.
  612. -- We cache the result on first call.
  613. passport.player_registered = function(pname)
  614. local all_players = passport.registered_players
  615. -- Read cache if available.
  616. local registered = all_players[pname]
  617. if registered ~= nil then
  618. return registered
  619. end
  620. local player = minetest.get_player_by_name(pname)
  621. if player and player:is_player() then
  622. local inv = player:get_inventory()
  623. if inv then
  624. if inv:contains_item("main", "passport:passport") or inv:contains_item("main", "passport:passport_adv") then
  625. all_players[pname] = true -- Cache for next time.
  626. return true
  627. else
  628. all_players[pname] = false -- Cache for next time.
  629. return false
  630. end
  631. end
  632. end
  633. -- Return false, but don't cache the value -- we could not confirm it!
  634. return false
  635. end
  636. -- This checks (and caches the result!) of whether the player has a KEY OF CITIZENSHIP.
  637. -- Second param is optional, may be nil.
  638. passport.player_has_key = function(pname, player)
  639. local all_players = passport.keyed_players
  640. -- Read cache if available.
  641. local keyed = all_players[pname]
  642. if keyed ~= nil then
  643. return keyed
  644. end
  645. local pref = player or minetest.get_player_by_name(pname)
  646. if pref then
  647. local inv = pref:get_inventory()
  648. if inv then
  649. if inv:contains_item("main", "passport:passport_adv") then
  650. all_players[pname] = true -- Cache for next time.
  651. return true
  652. else
  653. all_players[pname] = false -- Cache for next time.
  654. return false
  655. end
  656. end
  657. end
  658. -- Return false, but don't cache the value -- we could not confirm it!
  659. return false
  660. end
  661. function passport.on_leaveplayer(player, timeout)
  662. local pname = player:get_player_name()
  663. -- Remove cache of player registration.
  664. passport.registered_players[pname] = nil
  665. passport.keyed_players[pname] = nil
  666. end
  667. function passport.inventory_action(player, action, inventory, inventory_info)
  668. local pname = player:get_player_name()
  669. if action == "put" or action == "take" then
  670. local sname = inventory_info.stack:get_name()
  671. if sname == "passport:passport_adv" then
  672. -- Clear cache.
  673. passport.keyed_players[pname] = nil
  674. end
  675. elseif action == "move" then
  676. local movedstack = player:get_inventory():get_stack(inventory_info.to_list, inventory_info.to_index)
  677. local sname = movedstack:get_name()
  678. if sname == "passport:passport_adv" then
  679. -- Clear cache.
  680. passport.keyed_players[pname] = nil
  681. end
  682. end
  683. end
  684. if minetest.get_modpath("reload") then
  685. minetest.register_on_player_inventory_action(function(...)
  686. return passport.inventory_action(...) end)
  687. local c = "passport:core"
  688. local f = passport.modpath .. "/init.lua"
  689. if not reload.file_registered(c) then
  690. reload.register_file(c, f, false)
  691. end
  692. end