-- guider/init.lua -- HUD markers for navigation -- Copyright (C) 2025 1F616EMO -- SPDX-License-Identifier: LGPL-3.0-or-later guider = {} -- player name: handler: data guider.markers_by_player_name = {} function guider.destroy_waypoint(player, handler) assert(player and player.is_player and player:is_player(), "Non-player object passed into guider.add_waypoint") local name = player:get_player_name() local player_data = guider.markers_by_player_name[name] local data = player_data and player_data[handler] if not data then return false end player:hud_remove(data.text_hud_id) player:hud_remove(data.image_hud_id) player_data[handler] = nil if not next(player_data) then guider.markers_by_player_name[name] = nil end return true end function guider.add_waypoint(player, def) assert(player and player.is_player and player:is_player(), "Non-player object passed into guider.add_waypoint") assert(type(def) == "table", "Invalid type of def (table expected, got " .. type(def) .. ")") assert(type(def.name) == "string" or def.name == nil, "Invalid type of def.name (string or nil expected, got " .. type(def.name) .. ")") assert(type(def.image) == "string" or def.image == nil, "Invalid type of def.image (string or nil expected, got " .. type(def.image) .. ")") assert(type(def.suffix) == "string" or def.suffix == nil, "Invalid type of def.suffix (string or nil expected, got " .. type(def.suffix) .. ")") assert(type(def.percision) == "number" or def.percision == nil, "Invalid type of def.percision (number or nil expected, got " .. type(def.percision) .. ")") assert(type(def.text_color) == "number" or def.text_color == nil, "Invalid type of def.text_color (number or nil expected, got " .. type(def.text_color) .. ")") assert(type(def.scale) == "number" or type(def.scale) == "table" or def.scale == nil, "Invalid type of def.scale (number, table or nil expected, got " .. type(def.scale) .. ")") assert(type(def.image_height) == "number" or def.image_height == nil, "Invalid type of def.image_height (number or nil expected, got " .. type(def.image_height) .. ")") assert(type(def.pos) == "table", "Invalid type of def.pos (table expected, got " .. type(def.pos) .. ")") assert(type(def.pos.x) == "number", "Invalid type of def.pos.x (number expected, got " .. type(def.pos.x) .. ")") assert(type(def.pos.y) == "number", "Invalid type of def.pos.y (number expected, got " .. type(def.pos.y) .. ")") assert(type(def.pos.z) == "number", "Invalid type of def.pos.z (number expected, got " .. type(def.pos.z) .. ")") assert(type(def.on_approach) == "function" or def.on_approach == nil, "Invalid type of def.on_approach (function or nil expected, got " .. type(def.on_approach) .. ")") assert(type(def.approach_distance) == "number" or def.approach_distance == nil, "Invalid type of def.approach_distance (number or nil expected, got " .. type(def.approach_distance) .. ")") if def.scale == nil then def.scale = { x = 2, y = 2 } elseif type(def.scale) == "number" then def.scale = { x = def.scale, y = def.scale } end local data = {} data.text_hud_id = player:hud_add({ type = "waypoint", name = def.name or core.pos_to_string(def.pos, 0), text = def.suffix or "", precision = def.percision or 10, number = def.text_color or 0xffffff, world_pos = def.pos, offset = {x = 0, y = def.scale.y * (def.image_height or 16) * -1 - 7}, z_index = -300, }) data.image_hud_id = player:hud_add({ type = "image_waypoint", scale = def.scale, text = def.image or "guider_marker_default.png", world_pos = def.pos, z_index = -300, }) data.def = def local name = player:get_player_name() guider.markers_by_player_name[name] = guider.markers_by_player_name[name] or {} local handler = 1 while guider.markers_by_player_name[name][handler] do handler = handler + 1 end guider.markers_by_player_name[name][handler] = data return handler end do local passed = 0.1 core.register_globalstep(function(dtime) passed = passed + dtime if passed < 0.3 then return end passed = 0 for _, player in ipairs(core.get_connected_players()) do local name = player:get_player_name() local pos = player:get_pos() local player_data = guider.markers_by_player_name[name] for handler, data in pairs(player_data or {}) do if data.def.approach_distance and data.def.on_approach then if vector.distance(pos, data.def.pos) <= data.def.approach_distance then core.after(0, data.def.on_approach, player, handler) end end end end end) end core.register_on_leaveplayer(function(player) local name = player:get_player_name() guider.markers_by_player_name[name] = nil end) core.register_chatcommand("guider_test_waypoint", { privs = { server = true }, description = "Test Guider Waypoints: Create a waypoint at (0, 0, 0)", param = "[]", func = function(name, param) local player = core.get_player_by_name(name) if not player then return false, "This command can only be executed when you're online." end if param == "" then param = nil end local handler = guider.add_waypoint(player, { name = param, suffix = " m", pos = vector.new(0, 0, 0), approach_distance = 5, on_approach = guider.destroy_waypoint, }) return true, "Created a waypoint with handler: " .. tostring(handler) .. ". Use /guider_test_waypoint_destruct " .. tostring(handler) .. " to remove it." end, }) core.register_chatcommand("guider_test_waypoint_destruct", { privs = { server = true }, description = "Test Guider Waypoints: Destruct a waypoint with the given handler.", param = "", func = function(name, param) local player = core.get_player_by_name(name) if not player then return false, "This command can only be executed when you're online." end local handler = tonumber(param) if guider.destroy_waypoint(player, handler) then return true, "Successfully destroyed waypoint with handler " .. param .. "." else return false, "Failed to destroy waypoint with handler " .. param .. "." end end, })