123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778 |
- -- This mod provides an item which enables players to teleport to registered locations without the need of a teleporter.
- -- This item doubles as the means by which the server control scripts decide which playerfiles to delete and which to keep.
- passport = passport or {}
- passport.recalls = passport.recalls or {}
- passport.players = passport.players or {}
- passport.player_recalls = passport.player_recalls or {}
- passport.registered_players = passport.registered_players or {} -- Cache of registered players.
- passport.keyed_players = passport.keyed_players or {}
- passport.modpath = minetest.get_modpath("passport")
- -- List of players with open keys.
- -- On formspec close, playername should be removed and close-sound played.
- passport.open_keys = passport.open_keys or {}
- -- Localize for performance.
- local vector_distance = vector.distance
- local vector_round = vector.round
- local vector_add = vector.add
- local math_floor = math.floor
- local math_random = math.random
- local PASSPORT_TELEPORT_RANGE = 750
- minetest.register_privilege("recall", {
- description = "Player can request a teleport to nearby recall beacons.",
- give_to_singleplayer = false,
- })
- function passport.is_passport(name)
- if name == "passport:passport" then
- return true
- end
- if name == "passport:passport_adv" then
- return true
- end
- return false
- end
- -- Public API function. Only used by jail mod.
- passport.register_recall = function(recalldef)
- local name = recalldef.name
- local position = recalldef.position
- local min_dist = recalldef.min_dist
- local on_success = recalldef.on_success
- local on_failure = recalldef.on_failure
- local tname = recalldef.codename
- local suppress = recalldef.suppress
- local idx = #(passport.recalls) + 1
- local code = "z" .. idx .. ""
- passport.recalls[idx] = {
- name = name,
- position = position,
- code = code,
- on_success = on_success,
- min_dist = min_dist,
- on_failure = on_failure,
- tname = tname,
- suppress = suppress,
- }
- end
- function passport.beacons_to_recalls(beacons)
- local recalls = {}
- for k, v in ipairs(beacons) do
- local idx = #recalls + 1
- local real_label = rc.pos_to_string(v.pos)
- if v.name ~= nil and v.name ~= "" then
- real_label = v.name
- end
- recalls[idx] = {
- name = real_label,
- position = function() return vector_add(v.pos, {x=0, y=1, z=0}) end,
- code = "v" .. idx .. "",
- min_dist = 30,
- }
- end
- return recalls
- end
- passport.compose_formspec = function(pname)
- local buttons = ""
-
- local i = 1
- for k, v in pairs(passport.recalls) do
- local n = v.name
- local c = v.code
- if v.tname == "jail:jail" then
- buttons = buttons .. "button_exit[3,5.7;2,1;" .. c .. ";" .. n .. "]"
- else
- buttons = buttons .. "button_exit[6," .. (i-0.3) .. ";3,1;" .. c .. ";" .. n .. "]"
- i = i + 1
- end
- end
- local pref = minetest.get_player_by_name(pname)
- local beacons = {}
- if pref then
- local player_pos = pref:get_pos()
- -- Shall return an empty table if there are no beacons.
- beacons = teleports.nearest_beacons_to_position(player_pos, 6, 1000)
- end
- passport.player_recalls[pname] = passport.beacons_to_recalls(beacons)
-
- local h = 1
- for k, v in ipairs(passport.player_recalls[pname]) do
- local n = v.name
- local c = v.code
- buttons = buttons .. "button_exit[6," .. (h-0.3) .. ";3,1;" .. c .. ";" .. n .. "]"
- h = h + 1
- end
- local boolecho = 'true'
- local echo = chat_echo.get_echo(pname)
- if echo == true then boolecho = 'true' end
- if echo == false then boolecho = 'false' end
-
- local boolparticle = 'true'
- local particle = default.particles_enabled_for(pname)
- if particle == true then boolparticle = 'true' end
- if particle == false then boolparticle = 'false' end
- local formspec = "size[10,7]" ..
- default.gui_bg ..
- default.gui_bg_img ..
- default.gui_slots ..
- "label[1,0.0;" ..
- minetest.formspec_escape("Key Of Citizenship Interface") .. "]" ..
- "label[6,0.0;" ..
- minetest.formspec_escape("Recalls Nearby (" .. #beacons .. ")") .. "]" ..
- buttons ..
- "button_exit[1,5.7;2,1;exit;Close]" ..
- "button_exit[1,2.7;2,1;mapfix;Fix Map]" ..
- "button[3,0.7;2,1;email;Mail]" ..
- "button[1,1.7;2,1;survivalist;Survivalist]" ..
- "button[3,1.7;2,1;rename;Nickname]" ..
- "button[3,2.7;2,1;chatfilter;Chat Filter]" ..
- "button[1,0.7;2,1;marker;Markers]" ..
- "tooltip[email;Hold 'E' while using the Key to access directly.]" ..
- "tooltip[marker;Hold 'sneak' while using the Key to access directly.]" ..
-
- "checkbox[3,5.0;togglechat;Text Echo;" ..
- boolecho .. "]" ..
- "checkbox[1,5.0;toggleparticles;Particles;" ..
- boolparticle .. "]" ..
- "tooltip[togglechat;" ..
- minetest.formspec_escape(
- "Toggle whether the server should echo your chat back to your client.\n" ..
- "Newer clients should keep this checked.") .. "]" ..
- "tooltip[toggleparticles;" ..
- minetest.formspec_escape(
- "Toggle whether the server should send game-enhancing particle effects to your client.\n" ..
- "Sometimes these are purely for visual effect, sometimes they have gameplay meaning ...") .. "]"
- -- Special abilities are revoked for cheaters.
- if not sheriff.is_cheater(pname) then
- if survivalist.player_beat_cave_challenge(pname) then
- formspec = formspec .. "button[1,3.7;2,1;jaunt;Jaunt]"
- end
- if survivalist.player_beat_nether_challenge(pname) then
- if cloaking.is_cloaked(pname) then
- formspec = formspec .. "button[3,3.7;2,1;cloak;Uncloak]"
- else
- formspec = formspec .. "button[3,3.7;2,1;cloak;Cloak]"
- end
- end
- end
- for i=1, 7, 1 do
- local name = "xdecor:ivy"
- if i == 1 then
- name = "passport:passport_adv"
- elseif i == 7 then
- name = "default:sword_steel"
- end
- formspec = formspec .. "item_image[0," .. i-1 .. ";1,1;" .. name .. "]"
- formspec = formspec .. "item_image[9," .. i-1 .. ";1,1;" .. name .. "]"
- end
-
- return formspec
- end
- passport.show_formspec = function(pname)
- local formspec = passport.compose_formspec(pname)
- minetest.show_formspec(pname, "passport:passport", formspec)
- end
- passport.on_use = function(itemstack, user, pointed)
- local changed = false
- if user and user:is_player() then
- local pname = user:get_player_name()
- -- Check (and if needed, set) owner.
- local meta = itemstack:get_meta()
- local owner = meta:get_string("owner")
- if owner == "" then
- owner = pname
- -- Store owner and data of activation.
- meta:set_string("owner", owner)
- meta:set_int("date", os.time())
- minetest.after(3, function()
- minetest.chat_send_player(pname, "# Server: A newly initialized Key of Citizenship begins to emit a soft blue glow.")
- end)
- changed = true
- end
- -- Initialize data if not set.
- if meta:get_int("date") == 0 then
- meta:set_int("date", os.time())
- changed = true
- end
- if owner ~= pname then
- minetest.chat_send_player(pname, "# Server: This Key was initialized by someone else! You cannot access it.")
- easyvend.sound_error(pname)
- return
- end
- -- Record number of uses.
- meta:set_int("uses", meta:get_int("uses") + 1)
- changed = true
- local control = user:get_player_control()
- if control.sneak then
- marker.show_formspec(pname)
- elseif control.aux1 then
- mailgui.show_formspec(pname)
- else
- -- Show KoC interface.
- passport.show_formspec(pname)
- end
- passport.open_keys[pname] = true
- local ppos = user:get_pos()
- minetest.after(0, ambiance.sound_play, "fancy_chime1", ppos, 1.0, 20, "", false)
- end
- if changed then
- return itemstack
- end
- end
- passport.on_use_simple = function(itemstack, user, pointed)
- if user and user:is_player() then
- minetest.chat_send_player(user:get_player_name(),
- "# Server: This awkward chunk of reflective metal seems to mock you, " ..
- "yet remains strangely inert. Perhaps it can be upgraded?")
- end
- return itemstack
- end
- passport.on_receive_fields = function(player, formname, fields)
- if formname ~= "passport:passport" then return end
-
- local pname = player:get_player_name()
-
- if fields.mapfix then
- mapfix.command(pname, "")
- return true
- end
-
- if fields.email then
- mailgui.show_formspec(pname)
- return true
- end
- if fields.chatfilter then
- chat_controls.show_formspec(pname)
- return true
- end
- if fields.marker then
- marker.show_formspec(pname)
- return true
- end
- if fields.jaunt and survivalist.player_beat_cave_challenge(pname) then
- -- Jaunt code performs its own security validation.
- jaunt.show_formspec(pname)
- return true
- end
-
- if fields.cloak and survivalist.player_beat_nether_challenge(pname) then
- -- Security check to make sure player can use this feature.
- if not passport.player_has_key(pname) then
- return true
- end
- if not survivalist.player_beat_nether_challenge(pname) then
- return true
- end
- -- Cloaking ability is revoked for cheaters.
- if sheriff.is_cheater(pname) then
- return true
- end
- cloaking.toggle_cloak(pname)
- passport.show_formspec(pname) -- Reshow formspec.
- return true
- end
- if fields.survivalist then
- survivalist.show_formspec(pname)
- return true
- end
- if fields.rename then
- rename.show_formspec(pname)
- return true
- end
-
- if fields.togglechat then
- if fields.togglechat == 'true' then
- chat_echo.set_echo(pname, true)
- elseif fields.togglechat == 'false' then
- chat_echo.set_echo(pname, false)
- end
- passport.show_formspec(pname) -- Reshow formspec.
- return true
- end
-
- if fields.toggleparticles then
- if fields.toggleparticles == 'true' then
- default.enable_particles_for(pname, true)
- elseif fields.toggleparticles == 'false' then
- default.enable_particles_for(pname, false)
- end
- passport.show_formspec(pname) -- Reshow formspec.
- return true
- end
- if passport.player_recalls[pname] then
- for k, v in ipairs(passport.player_recalls[pname]) do
- local c = v.code
- if fields[c] then
- if not minetest.check_player_privs(pname, {recall=true}) then
- minetest.chat_send_player(pname, "# Server: You are not authorized to request transport.")
- easyvend.sound_error(pname)
- return true
- end
- passport.attempt_teleport(player, v)
- return true
- end
- end
- end
-
- for k, v in ipairs(passport.recalls) do
- local c = v.code
- if fields[c] then
- if not minetest.check_player_privs(pname, {recall=true}) then
- minetest.chat_send_player(pname, "# Server: You are not authorized to request transport.")
- easyvend.sound_error(pname)
- return true
- end
- passport.attempt_teleport(player, v)
- return true
- end
- end
- return true
- end
- passport.attempt_teleport = function(player, data)
- local pp = player:get_pos()
- local nn = player:get_player_name()
- local tg = data.position(player) -- May return nil.
- local recalls = passport.player_recalls[nn]
- if not recalls then
- minetest.chat_send_player(nn, "# Server: No data associated with beacon signal.")
- return
- end
- if not tg then
- minetest.chat_send_player(nn, "# Server: Beacon does not provide position data. Aborting.")
- return
- end
- if rc.current_realm_at_pos(tg) ~= rc.current_realm_at_pos(pp) then
- minetest.chat_send_player(nn, "# Server: Beacon signal is in another dimension!")
- -- Wrong realm.
- return
- end
-
- for k, v in ipairs(recalls) do
- if v.suppress then
- if v.suppress(nn) then
- minetest.chat_send_player(nn, "# Server: Beacon signal is jammed and cannot be triangulated.")
- easyvend.sound_error(nn)
- return -- Someone suppressed the ability to teleport.
- end
- end
- end
-
- -- Is player too close to custom (player-built) recalls?
- for k, v in ipairs(recalls) do
- local vpp = v.position(player) -- May return nil.
- if vpp then
- if vector_distance(pp, vpp) < v.min_dist then
- if data.on_failure then data.on_failure(nn, "too_close", v.tname) end
- minetest.chat_send_player(nn, "# Server: You are too close to a nearby beacon signal.")
- easyvend.sound_error(nn)
- return -- Too close to a beacon.
- end
- end
- end
-
- -- Is player too close to builtin (server) recalls?
- for k, v in ipairs(passport.recalls) do
- local vpp = v.position(player) -- May return nil.
- if vpp then
- if vector_distance(pp, vpp) < v.min_dist then
- if data.on_failure then data.on_failure(nn, "too_close", v.tname) end
- minetest.chat_send_player(nn, "# Server: You are too close to a nearby beacon signal.")
- easyvend.sound_error(nn)
- return -- Too close to a beacon.
- end
- end
- end
- if vector_distance(pp, tg) > PASSPORT_TELEPORT_RANGE then
- if data.on_failure then data.on_failure(nn, "too_far", data.tname) end
- local dist = math_floor(vector_distance(pp, tg))
- minetest.chat_send_player(nn, "# Server: Beacon signal is too weak. You are out of range: distance " .. dist/1000 .. " kilometers.")
- easyvend.sound_error(nn)
- return -- To far from requested beacon.
- end
-
- if passport.players[nn] then
- if data.on_failure then data.on_failure(nn, "in_progress", data.tname) end
- minetest.chat_send_player(nn, "# Server: Signal triangulation already underway; stand by.")
- return -- Teleport already in progress.
- end
-
- -- Everything satisfied. Let's teleport!
- local dist = vector_distance(pp, tg)
- local time = math.ceil(math.sqrt(dist / 10))
-
- minetest.chat_send_player(nn, "# Server: Recall beacon signal requires " .. time .. " seconds to triangulate; please hold still.")
- passport.players[nn] = true
- local pos = vector.add(tg, {x=math_random(-1, 1), y=0, z=math_random(-1, 1)})
- minetest.after(time, passport.do_teleport, nn, pp, pos, data.on_success)
- end
- -- Called from minetest.after() to actually execute a teleport.
- passport.do_teleport = function(name, start_pos, target_pos, func)
- passport.players[name] = nil
- local player = minetest.get_player_by_name(name)
- if player and player:is_player() then
- if sheriff.is_cheater(name) then
- if sheriff.punish_probability(name) then
- sheriff.punish_player(name)
- return
- end
- end
- if vector_distance(player:getpos(), start_pos) < 0.1 then
- preload_tp.execute({
- player_name = name,
- target_position = target_pos,
- emerge_radius = 32,
- post_teleport_callback = func,
- callback_param = name,
- send_blocks = true,
- particle_effects = true,
- })
- else
- minetest.chat_send_player(name, "# Server: Unable to accurately triangulate beacon position! Aborted.")
- easyvend.sound_error(name)
- end
- end
- end
- function passport.exec_spawn(name, param)
- local player = minetest.get_player_by_name(name)
- if not player then return false end
- local pos = vector_round(player:get_pos())
- if jail.suppress(name) then
- return true
- end
- local target = randspawn.get_respawn_pos(pos, name)
- if vector_distance(pos, target) < 20 then
- minetest.chat_send_player(name, "# Server: Too close to the spawnpoint!")
- easyvend.sound_error(name)
- return true
- end
- if sheriff.is_cheater(name) then
- if sheriff.punish_probability(name) then
- sheriff.punish_player(name)
- return true
- end
- end
- if vector_distance(pos, target) <= 256 then
- randspawn.reposition_player(name, pos)
- minetest.after(1, function()
- minetest.chat_send_player(name, "# Server: You have been returned to the spawnpoint.")
- portal_sickness.on_use_portal(name)
- end)
- else
- minetest.chat_send_player(name, "# Server: You are too far from the spawnpoint!")
- easyvend.sound_error(name)
- end
- return true
- end
- function passport.award_cash(pname, player)
- local inv = player:get_inventory()
- if not inv then
- return
- end
- local cash_stack = ItemStack("currency:minegeld_20 10")
- local prot_stack = ItemStack("protector:protect3")
- local cash_left = inv:add_item("main", cash_stack)
- local prot_left = inv:add_item("main", prot_stack)
- minetest.chat_send_player(pname,
- core.get_color_escape_sequence("#ffff00") ..
- "# Server: Bank notice - As this is the first recorded time you have obtained a PoC, the Colony grants you 200 minegeld.")
- minetest.chat_send_player(pname,
- core.get_color_escape_sequence("#ffff00") ..
- "# Server: This is roughly equivalent to 8 gold ingots according to the Guild of Weights and Measures.")
- if cash_left:is_empty() and prot_left:is_empty() then
- minetest.chat_send_player(pname,
- core.get_color_escape_sequence("#ffff00") ..
- "# Server: The cash has been directly added to your inventory. Trade wisely and well, Adventurer!")
- else
- local pos = vector_round(player:get_pos())
- pos.y = pos.y + 1
- if not cash_left:is_empty() then
- minetest.add_item(pos, cash_left)
- end
- if not prot_left:is_empty() then
- minetest.add_item(pos, prot_left)
- end
- minetest.chat_send_player(pname,
- core.get_color_escape_sequence("#ffff00") ..
- "# Server: The cash could not be added to your inventory (no space). Check near your position for drops.")
- end
- end
- function passport.on_craft(itemstack, player, old_craft_grid, craft_inv)
- local name = itemstack:get_name()
- if name == "passport:passport_adv" then
- local pname = player:get_player_name()
- local meta = itemstack:get_meta()
- -- Store owner and data of activation.
- meta:set_string("owner", pname)
- meta:set_int("date", os.time())
- minetest.after(3, function()
- minetest.chat_send_player(pname,
- "# Server: A newly fashioned Key of Citizenship emits a soft blue glow mere moments after its crafter finishes the device.")
- end)
- -- Clear cache of player registration.
- passport.keyed_players[pname] = nil
- passport.registered_players[pname] = nil
- elseif name == "passport:passport" then
- -- Check if this is the first time this player has crafted a PoC.
- local pname = player:get_player_name()
- local meta = passport.modstorage
- local key = pname .. ":crafted_poc"
- if meta:get_int(key) == 0 then
- meta:set_int(key, 1)
- passport.award_cash(pname, player)
- end
- -- Clear cache of player registration.
- passport.keyed_players[pname] = nil
- passport.registered_players[pname] = nil
- end
- end
- if not passport.registered then
- -- Obtain modstorage.
- passport.modstorage = minetest.get_mod_storage()
-
- -- Keep this in inventory to prevent deletion.
- minetest.register_craftitem("passport:passport", {
- description = "Proof Of Citizenship\n\n" ..
- "Keep this in your MAIN inventory at ALL times!\n" ..
- "This preserves your Account during server purge.\n" ..
- "It cannot be stolen or lost by dying.",
- inventory_image = "default_bronze_block.png^default_tool_steelpick.png",
- stack_max = 1,
- on_use = function(...) return passport.on_use_simple(...) end,
- })
- -- Keep this in inventory to prevent deletion.
- minetest.register_craftitem("passport:passport_adv", {
- description = "Key Of Citizenship\n\n" ..
- "Keep this in your MAIN inventory at ALL times!\n" ..
- "This preserves your Account during server purge.\n" ..
- "It cannot be stolen or lost by dying.",
- inventory_image = "adv_passport.png",
- stack_max = 1,
- on_use = function(...) return passport.on_use(...) end,
- })
- minetest.register_craft({
- output = 'passport:passport 1',
- recipe = {
- {'default:copper_ingot', 'default:copper_ingot', 'default:copper_ingot'},
- },
- })
-
- minetest.register_craft({
- output = 'passport:passport_adv 1',
- recipe = {
- {'mese_crystals:zentamine', 'passport:passport', 'mese_crystals:zentamine'},
- {'techcrafts:control_logic_unit', 'quartz:quartz_crystal_piece', 'techcrafts:control_logic_unit'},
- {'dusts:diamond_shard', 'techcrafts:control_logic_unit', 'default:obsidian_shard'},
- },
- })
- minetest.register_on_player_receive_fields(function(...) return passport.on_receive_fields(...) end)
- minetest.register_alias("command_tokens:live_preserver", "passport:passport")
-
- -- It's very common for servers to have a /spawn command. This one is limited.
- minetest.register_chatcommand("spawn", {
- params = "",
- description = "Teleport the player back to the spawnpoint. This only works within 256 meters of spawn.",
- privs = {recall=true},
- func = function(...)
- return passport.exec_spawn(...)
- end,
- })
- -- Let players used to using /recall know that gameplay has changed in this respect.
- minetest.register_chatcommand("recall", {
- params = "",
- description = "Teleport the player back to the spawnpoint. This only works within 256 meters of spawn.",
- privs = {recall=true},
- func = function(...)
- return passport.exec_spawn(...)
- end,
- })
- minetest.register_on_leaveplayer(function(...)
- return passport.on_leaveplayer(...)
- end)
- minetest.register_on_craft(function(...) passport.on_craft(...) end)
- passport.registered = true
- end
- -- This function may be called serveral times on player-login and other times.
- -- We cache the result on first call.
- passport.player_registered = function(pname)
- local all_players = passport.registered_players
- -- Read cache if available.
- local registered = all_players[pname]
- if registered ~= nil then
- return registered
- end
- local player = minetest.get_player_by_name(pname)
- if player and player:is_player() then
- local inv = player:get_inventory()
- if inv then
- if inv:contains_item("main", "passport:passport") or inv:contains_item("main", "passport:passport_adv") then
- all_players[pname] = true -- Cache for next time.
- return true
- else
- all_players[pname] = false -- Cache for next time.
- return false
- end
- end
- end
- -- Return false, but don't cache the value -- we could not confirm it!
- return false
- end
- -- This checks (and caches the result!) of whether the player has a KEY OF CITIZENSHIP.
- -- Second param is optional, may be nil.
- passport.player_has_key = function(pname, player)
- local all_players = passport.keyed_players
- -- Read cache if available.
- local keyed = all_players[pname]
- if keyed ~= nil then
- return keyed
- end
- local pref = player or minetest.get_player_by_name(pname)
- if pref then
- local inv = pref:get_inventory()
- if inv then
- if inv:contains_item("main", "passport:passport_adv") then
- all_players[pname] = true -- Cache for next time.
- return true
- else
- all_players[pname] = false -- Cache for next time.
- return false
- end
- end
- end
- -- Return false, but don't cache the value -- we could not confirm it!
- return false
- end
- function passport.on_leaveplayer(player, timeout)
- local pname = player:get_player_name()
- -- Remove cache of player registration.
- passport.registered_players[pname] = nil
- passport.keyed_players[pname] = nil
- end
- if minetest.get_modpath("reload") then
- local c = "passport:core"
- local f = passport.modpath .. "/init.lua"
- if not reload.file_registered(c) then
- reload.register_file(c, f, false)
- end
- end
|