123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871 |
- marker = marker or {}
- marker.modpath = minetest.get_modpath("marker")
- marker.players = marker.players or {}
- marker.gui = marker.gui or {}
- marker.steptime = 1
- marker.max_waypoints = 100
- marker.max_lists = 50
- -- Max distance of players who wish to share marker lists.
- marker.proximity_range = 10
- -- Min/max ranges for visual particles.
- marker.pr_min = 40
- marker.pr_max = 50
- -- Min/max ranges for HUD text objects.
- marker.ht_min = 12
- marker.ht_max1 = 100
- marker.ht_max2 = 110
- -- Localize for performance.
- local vector_distance = vector.distance
- local vector_round = vector.round
- local timer = 0
- local delay = marker.steptime
- function marker.on_globalstep(dtime)
- timer = timer + dtime
- if timer < delay then return end
- timer = 0
- marker.update_huds()
- end
- function marker.update_huds()
- -- localize
- -- create gui for player if not created already
- local players = marker.players
- for k, v in pairs(players) do
- marker.update_single_hud(k)
- end
- end
- function marker.on_leaveplayer(pref, timeout)
- local pname = pref:get_player_name()
- marker.players[pname] = nil
- marker.gui[pname] = nil
- end
- function marker.update_single_hud(player)
- local allgui = marker.gui
- if not allgui[player] then
- marker.create_gui(player)
- end
- local gui = allgui[player]
- local waypoints = gui.waypoints
- local pref = minetest.get_player_by_name(player)
- if not pref or not pref:is_player() then
- return
- end
- local d = vector.distance
- local p2 = pref:get_pos()
- for i = 1, #waypoints, 1 do
- local data = waypoints[i]
- local dist = d(data.pos, p2)
- if dist > marker.ht_min and dist < marker.ht_max1 then
- -- add hud element if nearby and not already added
- if not data.hnd then
- local wp = vector.add(data.pos, {x=0, y=1, z=0})
- local number = "7902626"
- if data.highlight then
- number = "8739932"
- end
- data.hnd = pref:hud_add({
- hud_elem_type = "waypoint",
- name = "Marker",
- number = number,
- world_pos = wp,
- })
- end
- elseif dist < marker.ht_min or dist > marker.ht_max2 then
- -- remove hud element if too far and not yet removed
- if data.hnd then
- pref:hud_remove(data.hnd)
- data.hnd = nil
- end
- end
- if dist < marker.pr_min then
- if not data.pts then
- local wp = vector.add(data.pos, {x=0, y=0.5, z=0})
- local particles = {
- amount = 20,
- time = 0,
- minpos = vector.add(wp, {x=-0.1, y=-0.1, z=-0.1}),
- maxpos = vector.add(wp, {x=0.1, y=0.1, z=0.1}),
- minvel = vector.new(-0.5, -0.5, -0.5),
- maxvel = vector.new(0.5, 0.5, 0.5),
- minacc = {x=0, y=0, z=0},
- maxacc = {x=0, y=0, z=0},
- minexptime = 0.5,
- maxexptime = 2.0,
- minsize = 1,
- maxsize = 1,
- collisiondetection = false,
- collision_removal = false,
- vertical = false,
- texture = "quartz_crystal_piece.png",
- glow = 14,
- playername = player,
- }
- data.pts = minetest.add_particlespawner_single(particles)
- end
- elseif dist > marker.pr_max then
- if data.pts then
- minetest.delete_particlespawner(data.pts, player)
- data.pts = nil
- end
- end
- end
- end
- function marker.update_hud_markers(player, list, highlight)
- -- localize
- local allgui = marker.gui
- if not allgui[player] then
- marker.create_gui(player)
- end
- local gui = allgui[player]
- local waypoints = gui.waypoints
- -- remove all existing hud elements
- local pref = minetest.get_player_by_name(player)
- if not pref or not pref:is_player() then
- return
- end
- for i = 1, #waypoints, 1 do
- local data = waypoints[i]
- if data.hnd then
- pref:hud_remove(data.hnd)
- data.hnd = nil
- end
- if data.pts then
- minetest.delete_particlespawner(data.pts, player)
- data.pts = nil
- end
- if data.highlight then
- data.highlight = nil
- end
- end
- -- update waypoint list data
- gui.waypoints = {}
- waypoints = gui.waypoints
- if gui.show and list and list ~= "" then
- local data = marker.get_list(player, list)
- for i = 1, #data, 1 do
- waypoints[#waypoints + 1] = {pos=table.copy(data[i])}
- if highlight and highlight == i then
- waypoints[#waypoints].highlight = true
- end
- end
- end
- marker.update_single_hud(player)
- end
- function marker.highlight_marker(player, index)
- -- localize
- local allgui = marker.gui
- if not allgui[player] then
- marker.create_gui(player)
- end
- local gui = allgui[player]
- local waypoints = gui.waypoints
- for i = 1, #waypoints, 1 do
- local data = waypoints[i]
- if data.highlight then
- data.highlight = nil
- -- return marker to regular color
- if data.hnd then
- local pref = minetest.get_player_by_name(player)
- if pref and pref:is_player() then
- pref:hud_change(data.hnd, "number", "7902626")
- end
- end
- end
- end
- if index >= 1 and index <= #waypoints then
- local data = waypoints[index]
- if not data.highlight then
- data.highlight = true
- -- change (highlight) marker color
- if data.hnd then
- local pref = minetest.get_player_by_name(player)
- if pref and pref:is_player() then
- pref:hud_change(data.hnd, "number", "8739932")
- end
- end
- end
- end
- end
- -- private: load player data
- function marker.load_player(player)
- -- localize
- local players = marker.players
- -- load player data from mod storage
- local str = marker.storage:get_string(player)
- assert(type(str) == "string")
- if str == "" then
- players[player] = {}
- return
- end
- local lists = minetest.deserialize(str)
- if not lists then
- players[player] = {}
- return
- end
- -- data is now loaded
- assert(type(lists) == "table")
- players[player] = lists
- end
- -- private: save player data
- function marker.save_player(player)
- -- localize
- local players = marker.players
- -- localize
- local lists = players[player] or {}
- local str = minetest.serialize(lists)
- assert(type(str) == "string")
- -- send data to mod storage
- marker.storage:set_string(player, str)
- end
- -- api: player name, position, list name
- function marker.add_waypoint(player, pos, list)
- -- localize
- local players = marker.players
- -- load data for player if not loaded already
- if not players[player] then
- marker.load_player(player)
- end
- -- localize
- local lists = players[player]
- -- create waypoint list if not created already
- local found = false
- local positions
- for i = 1, #lists, 1 do
- if lists[i].name == list then
- found = true
- positions = lists[i].data
- break
- end
- end
- if not found then
- lists[#lists + 1] = {name=list, data={}}
- positions = lists[#lists].data
- end
- -- add position
- positions[#positions + 1] = vector_round(pos)
- -- save changes
- marker.save_player(player)
- end
- -- api: player name, index, list name
- function marker.remove_waypoint(player, index, list)
- -- localize
- local players = marker.players
- -- load data for player if not loaded already
- if not players[player] then
- marker.load_player(player)
- end
- -- localize
- local lists = players[player]
- -- search player data for named list
- -- ignore if list doesn't exist
- local found = false
- local positions
- for i = 1, #lists, 1 do
- if lists[i].name == list then
- found = true
- positions = lists[i].data
- break
- end
- end
- if not found then
- return
- end
- -- localize
- local equals = vector.equals
- local changed = false
- local pos
- -- erase position from positions list
- if index >= 1 and index <= #positions then
- pos = positions[index]
- table.remove(positions, index)
- changed = true
- end
- -- save changes if needed
- if changed then
- marker.save_player(player)
- end
- -- return pos or nil
- return pos
- end
- -- private: get the list of positions for a given list name
- --
- -- important: the list can be modified, and if modified, must be saved afterward
- -- by calling marker.save_player(), otherwise changes will be lost eventually
- function marker.get_list(player, list)
- assert(type(list) == "string" and list ~= "")
- -- localize
- local players = marker.players
- -- load data for player if not loaded already
- if not players[player] then
- marker.load_player(player)
- end
- -- localize
- local lists = players[player]
- -- if named list doesn't exist, create it
- local found = false
- local positions
- for i = 1, #lists, 1 do
- if lists[i].name == list then
- found = true
- positions = lists[i].data
- break
- end
- end
- if not found then
- lists[#lists + 1] = {name=list, data={}}
- positions = lists[#lists].data
- -- a new list was added, need to save data
- marker.save_player(player)
- end
- -- return the named list
- -- the list can be modifed
- -- remember to save the data
- return positions
- end
- -- api: player name, list index
- function marker.get_list_name(player, index)
- -- localize
- local players = marker.players
- -- load data for player if not loaded already
- if not players[player] then
- marker.load_player(player)
- end
- -- localize
- local lists = players[player]
- local name = ""
- for i = 1, #lists, 1 do
- if lists[i].name == "default" then
- if index >= i then
- index = index + 1
- -- fix corner case
- if index > #lists then
- index = #lists
- end
- end
- end
- if i == index then
- name = lists[i].name
- break
- end
- end
- return name
- end
- -- api: player name, list name
- function marker.have_list(player, list)
- local players = marker.players
- if not players[player] then
- return false
- end
- local alists = players[player]
- local found = false
- for i = 1, #alists, 1 do
- if alists[i].name == list then
- found = true
- break
- end
- end
- return found
- end
- -- api: player name, list name
- function marker.list_size(player, list)
- local players = marker.players
- if not players[player] then
- return 0
- end
- local alists = players[player]
- local size = 0
- for i = 1, #alists, 1 do
- if alists[i].name == list then
- size = #(alists[i].data)
- break
- end
- end
- return size
- end
- function marker.list_count(player)
- local players = marker.players
- if not players[player] then
- return 0
- end
- local alists = players[player]
- local size = 0
- for i = 1, #alists, 1 do
- if alists[i].name ~= "default" then
- size = size + 1
- end
- end
- return size
- end
- -- api: player name, list name
- function marker.remove_list(player, list)
- local players = marker.players
- if not players[player] then
- return
- end
- local changed = false
- local alists = players[player]
- for i = 1, #alists, 1 do
- if alists[i].name == list then
- table.remove(alists, i)
- changed = true
- break
- end
- end
- if changed then
- marker.save_player(player)
- end
- end
- -- private: create gui-data for player
- function marker.create_gui(player)
- marker.gui[player] = {
- index1 = -1,
- index2 = -1,
- index3 = -1,
- listname = "",
- playername = "",
- waypoints = {},
- }
- end
- -- private: assemble a formspec string
- function marker.get_formspec(player)
- -- localize
- local players = marker.players
- local allgui = marker.gui
- -- load data for player if not loaded already
- if not players[player] then
- marker.load_player(player)
- end
- local alists = players[player]
- -- create gui for player if not created already
- if not allgui[player] then
- marker.create_gui(player)
- end
- local gui = allgui[player]
- local deflist = marker.get_list(player, "default")
- local formspec = "size[9,7]" ..
- default.gui_bg ..
- default.gui_bg_img ..
- default.gui_slots
- formspec = formspec ..
- "item_image[0,0;1,1;passport:passport_adv]" ..
- "label[1,0;Key Device Marker System]" ..
- "field[0.3,1.3;2.9,1;listname;;" .. minetest.formspec_escape(gui.listname) .. "]" ..
- "field[0.3,2.15;2.9,1;player;;" .. minetest.formspec_escape(gui.playername) .. "]"
- formspec = formspec ..
- "textlist[5.0,0.0;3.7,2.6;lists;"
- local comma = ""
- --minetest.log(dump(alists))
- for i = 1, #alists, 1 do
- local k = alists[i].name
- if k ~= "default" then
- formspec = formspec .. comma .. k
- comma = ","
- end
- end
- formspec = formspec .. ";" .. gui.index1 .. "]" ..
- "textlist[0.0,3.0;3.7,3.0;markers;"
- for i = 1, #deflist, 1 do
- local s = rc.pos_to_namestr(deflist[i])
- formspec = formspec .. minetest.formspec_escape(s)
- if i < #deflist then
- formspec = formspec .. ","
- end
- end
- formspec = formspec .. ";" .. gui.index2 .. "]" ..
- "textlist[5.0,3.0;3.7,4.0;positions;"
- local lname = marker.get_list_name(player, gui.index1)
- if lname and lname ~= "" then
- local data = marker.get_list(player, lname)
- comma = ""
- for i = 1, #data, 1 do
- local s = minetest.formspec_escape(rc.pos_to_namestr(data[i]))
- formspec = formspec .. comma .. s
- comma = ","
- end
- end
- formspec = formspec .. ";" .. gui.index3 .. "]"
- formspec = formspec ..
- "button[3.0,1.0;1,1;addlist;>]" ..
- "button[4.0,1.0;1,1;dellist;X]" ..
- "button[3.0,1.85;2,1;sendlist;Send List]" ..
- "button[4.0,3.0;1,1;ls;<]" ..
- "button[4.0,4.0;1,1;mark;Mark]" ..
- "button[4.0,5.0;1,1;rs;>]" ..
- "button[0.0,6.25;1,1;done;Done]" ..
- "button[1.0,6.25;1,1;delete;Erase]"
- if not gui.show then
- formspec = formspec .. "button[2.0,6.25;3,1;show;Enable Scan]"
- else
- formspec = formspec .. "button[2.0,6.25;3,1;hide;Disable Scan]"
- end
- return formspec
- end
- -- api: show formspec to player
- function marker.show_formspec(player)
- local formspec = marker.get_formspec(player)
- minetest.show_formspec(player, "marker:fs", formspec)
- end
- marker.on_receive_fields = function(player, formname, fields)
- if formname ~= "marker:fs" then return end
- local pname = player:get_player_name()
- -- security check to make sure player can use this feature
- local inv = player:get_inventory()
- if not inv:contains_item("main", "passport:passport_adv") then
- return true
- end
- -- ensure user-data is loaded and available
- if not marker.players[pname] then
- marker.load_player(pname)
- end
- -- localize
- -- create gui for player if not created already
- local allgui = marker.gui
- if not allgui[pname] then
- marker.create_gui(pname)
- end
- local gui = allgui[pname]
- if fields.done then
- passport.show_formspec(pname)
- return true
- end
- if fields.quit then
- return true
- end
- if fields.listname then
- gui.listname = fields.listname
- end
- if fields.player then
- gui.playername = fields.player
- end
- if fields.show then
- gui.show = true
- if gui.listname and gui.listname ~= "" then
- marker.update_hud_markers(pname, gui.listname)
- end
- end
- if fields.hide then
- gui.show = nil
- marker.update_hud_markers(pname)
- end
- if fields.addlist then
- if marker.list_count(pname) < marker.max_lists then
- local name = fields.listname or ""
- name = name:trim()
- if name == "" then
- minetest.chat_send_player(pname, "# Server: Cannot add list with empty name.")
- elseif name == "default" then
- minetest.chat_send_player(pname, "# Server: Cannot add list with reserved name.")
- else
- if marker.have_list(pname, name) then
- minetest.chat_send_player(pname, "# Server: Cannot add list, it already exists.")
- else
- -- this automatically adds the list if it doesn't exist
- local pos = player:get_pos()
- pos.y = pos.y + 1
- marker.add_waypoint(pname, pos, name)
- gui.index1 = #(marker.players[pname])
- gui.index3 = -1
- marker.update_hud_markers(pname, name)
- minetest.chat_send_player(pname, "# Server: Added list.")
- end
- end
- else
- minetest.chat_send_player(pname, "# Server: Marker list roster is full, cannot create a new marker list.")
- end
- elseif fields.dellist then
- local name = fields.listname or ""
- name = name:trim()
- if name == "" then
- minetest.chat_send_player(pname, "# Server: Cannot remove list with empty name.")
- elseif name == "default" then
- minetest.chat_send_player(pname, "# Server: Cannot remove list with reserved name.")
- else
- if marker.have_list(pname, name) then
- marker.remove_list(pname, name)
- gui.index1 = -1
- gui.listname = ""
- marker.update_hud_markers(pname)
- minetest.chat_send_player(pname, "# Server: Removed marker list.")
- else
- minetest.chat_send_player(pname, "# Server: Cannot remove non-existent list.")
- end
- end
- elseif fields.sendlist then
- local targetname = fields.player or ""
- targetname = targetname:trim()
- if targetname ~= "" then
- targetname = rename.grn(targetname)
- if targetname ~= pname then
- local ptarget = minetest.get_player_by_name(targetname)
- if ptarget and ptarget:is_player() then
- if vector_distance(ptarget:get_pos(), player:get_pos()) < marker.proximity_range then
- local inv = ptarget:get_inventory()
- if inv:contains_item("main", "passport:passport_adv") then
- local name = marker.get_list_name(pname, gui.index1)
- if name and name ~= "" then
- -- load data for target player if not loaded already
- -- otherwise checking to see if player already has list could fail
- -- in a very wrong way
- if not marker.players[targetname] then
- marker.load_player(targetname)
- end
- if marker.have_list(targetname, name) then
- minetest.chat_send_player(pname, "# Server: The other Key already hosts a marker list with that name. Cannot transfer data!")
- else
- if marker.list_count(targetname) < marker.max_lists then
- local datafrom = marker.get_list(pname, name)
- -- because we checked to make sure the target key doesn't have this list already,
- -- we have assured that this will create it for the first time, and it will be empty
- local datato = marker.get_list(targetname, name)
- for i = 1, #datafrom, 1 do
- datato[#datato + 1] = table.copy(datafrom[i])
- end
- minetest.chat_send_player(pname, "# Server: Marker list sent!")
- minetest.chat_send_player(targetname, "# Server: <" .. rename.gpn(pname) .. "> sent a marker list to your Key!")
- else
- minetest.chat_send_player(pname, "# Server: The other Key's marker list storage is full! Cannot transfer data.")
- end
- end
- else
- minetest.chat_send_player(pname, "# Server: You must select a marker list, first.")
- end
- else
- minetest.chat_send_player(pname, "# Server: The other player does not have a Key!")
- end
- else
- minetest.chat_send_player(pname, "# Server: You need to stand close to the other player's Key to transfer a marker list.")
- end
- else
- minetest.chat_send_player(pname, "# Server: The specified player is not available.")
- end
- else
- minetest.chat_send_player(pname, "# Server: Cannot send marker list to your own Key.")
- end
- else
- minetest.chat_send_player(pname, "# Server: You must specify the name of a player to send a marker list to.")
- end
- elseif fields.ls then
- local name = marker.get_list_name(pname, gui.index1)
- if name and name ~= "" then
- if marker.list_size(pname, "default") < marker.max_waypoints then
- local data = marker.get_list(pname, name)
- if gui.index3 >= 1 and gui.index3 <= #data then
- local pos = marker.remove_waypoint(pname, gui.index3, name)
- if pos then
- marker.add_waypoint(pname, pos, "default")
- local deflist = marker.get_list(pname, "default")
- gui.index2 = #deflist
- gui.index3 = -1
- marker.update_hud_markers(pname, name)
- minetest.chat_send_player(pname, "# Server: Moved marker to free-list.")
- else
- minetest.chat_send_player(pname, "# Server: Could not remove marker from list.")
- end
- else
- minetest.chat_send_player(pname, "# Server: You need to have selected a marker to be moved.")
- end
- else
- minetest.chat_send_player(pname, "# Server: Cannot remove marker from list, free-list storage is full.")
- end
- else
- minetest.chat_send_player(pname, "# Server: You must select a marker list, first.")
- end
- elseif fields.rs then
- local name = marker.get_list_name(pname, gui.index1)
- if name and name ~= "" then
- if marker.list_size(pname, name) < marker.max_waypoints then
- local data = marker.get_list(pname, "default")
- if gui.index2 >= 1 and gui.index2 <= #data then
- local pos = marker.remove_waypoint(pname, gui.index2, "default")
- if pos then
- marker.add_waypoint(pname, pos, name)
- local thelist = marker.get_list(pname, name)
- gui.index3 = #thelist
- gui.index2 = -1
- marker.update_hud_markers(pname, name, gui.index3)
- minetest.chat_send_player(pname, "# Server: Moved unattached marker to list.")
- else
- minetest.chat_send_player(pname, "# Server: Could not remove marker from free-list.")
- end
- else
- minetest.chat_send_player(pname, "# Server: You need to have selected a marker to be moved.")
- end
- else
- minetest.chat_send_player(pname, "# Server: Cannot add marker to list, list storage is full.")
- end
- else
- minetest.chat_send_player(pname, "# Server: You must select a marker list, first.")
- end
- elseif fields.mark then
- if marker.list_size(pname, "default") < marker.max_waypoints then
- local pos = player:get_pos()
- pos.y = pos.y + 1
- marker.add_waypoint(pname, pos, "default")
- local deflist = marker.get_list(pname, "default")
- gui.index2 = #deflist
- minetest.chat_send_player(pname, "# Server: Placed marker at " .. rc.pos_to_namestr(vector_round(pos)) .. ".")
- else
- minetest.chat_send_player(pname, "# Server: Cannot place a new marker, storage is full.")
- end
- elseif fields.delete then
- local index = gui.index2
- local deflist = marker.get_list(pname, "default")
- if index >= 1 and index <= #deflist then
- local s = rc.pos_to_namestr(deflist[index])
- marker.remove_waypoint(pname, index, "default")
- gui.index2 = -1
- minetest.chat_send_player(pname, "# Server: Removed marker: " .. s .. ".")
- else
- minetest.chat_send_player(pname, "# Server: You must select a marker from the marker list, to erase it.")
- end
- elseif fields.lists then
- local event = minetest.explode_textlist_event(fields.lists)
- if event.type == "CHG" then
- local index = event.index
- gui.index1 = index
- gui.index3 = -1
- gui.listname = marker.get_list_name(pname, index)
- marker.update_hud_markers(pname, gui.listname)
- end
- elseif fields.markers then
- local event = minetest.explode_textlist_event(fields.markers)
- if event.type == "CHG" then
- local index = event.index
- gui.index2 = index
- end
- elseif fields.positions then
- local event = minetest.explode_textlist_event(fields.positions)
- if event.type == "CHG" then
- local index = event.index
- gui.index3 = index
- marker.highlight_marker(pname, index)
- end
- elseif fields.listname then
- -- nothing here atm
- elseif fields.player then
- -- nothing here atm
- end
- marker.show_formspec(pname)
- return true
- end
- if not marker.registered then
- marker.storage = minetest.get_mod_storage()
- minetest.register_on_player_receive_fields(function(...)
- return marker.on_receive_fields(...)
- end)
- minetest.register_globalstep(function(...)
- return marker.on_globalstep(...)
- end)
- minetest.register_on_leaveplayer(function(...)
- return marker.on_leaveplayer(...)
- end)
- local c = "marker:core"
- local f = marker.modpath .. "/init.lua"
- reload.register_file(c, f, false)
- marker.registered = true
- end
|