-- TODO show some text on screen for a short while? -- TODO "admin" command version with configurable pos, radius and timeout local announce_command_name = "look" local announce_radius = 256 -- players within this radius will see a waypoint local announce_icon = "exclamation_mark.png" local announce_timeout = 20 -- time until waypoint disappears local waypoint_precision = 1 -- set to 1 to show whole number or 10 for 1 decimal local waypoint_color = 0xFFFFFF local point_range = 180 -- maximun range player can point at (raycast range) local point_to_objects = true local point_to_liquids = true -- return first thing player is pointing at local function raycast_crosshair(player, range) local p_pos = player:get_pos() local p_eye_height = player:get_properties().eye_height local p_eye_pos = { x = p_pos.x, y = p_pos.y + p_eye_height, z = p_pos.z } local to = vector.add(p_eye_pos, vector.multiply(player:get_look_dir(), range)) local ray = minetest.raycast(p_eye_pos, to, point_to_objects, point_to_liquids) local pointed_thing = ray:next() while pointed_thing do if pointed_thing.type == "object" and pointed_thing.ref == player then -- exclude the player themselves from the raycast pointed_thing = ray:next() else return pointed_thing end end end local function get_pointed_position(player, range) local pointed_thing = raycast_crosshair(player, range) local pointed_pos = nil if pointed_thing then if pointed_thing.type == "node" then pointed_pos = vector.multiply(vector.add(pointed_thing.above, pointed_thing.under), 0.5) elseif pointed_thing.type == "object" then pointed_pos = pointed_thing.ref:get_pos() end end return pointed_pos, pointed_thing end local function show_hud_waypoint(player, point_pos, name, icon_name) local hud_id1 = player:hud_add({ hud_elem_type = "waypoint", name = name, text = "m", -- distance suffix precision = waypoint_precision, number = waypoint_color, world_pos = point_pos, offset = {x=0,y=0}, alignment = {x=0, y=1}, }) local hud_id2 = player:hud_add({ hud_elem_type = "image_waypoint", scale = {x=-3,y=-3}, text = icon_name, alignment = {x=0,y=-1}, world_pos = point_pos, offset = {x=0,y=0}, --name = name, --precision = waypoint_precision, --number = waypoint_color, }) local hud_ids = {hud_id1, hud_id2} return hud_ids end local function hide_hud_waypoint(player_name, hud_ids) local player = minetest.get_player_by_name(player_name) if player then for _,hud_id in pairs(hud_ids) do player:hud_remove(hud_id) end end end local prepared_announcements = {} local function prepare_announcement(announcer_name, announce_radius, message, announce_icon, announce_timeout) --prepared_announcements[select(1,...)] = table.pack(...) -- does not work in older lua? prepared_announcements[announcer_name] = {announcer_name, announce_radius, message, announce_icon, announce_timeout} end local function add_announcement(announcer, announcer_name, radius, message, icon_name, timeout) local player_pos = announcer:get_pos() -- TODO use pointed thing to provide some description? local pointed_pos, pointed_thing = get_pointed_position(announcer, point_range) if not pointed_pos then -- player is pointing into the void, use him as target pointed_pos = player_pos end for i, object in pairs(minetest.get_objects_inside_radius(player_pos, radius)) do if object:is_player() then local player = object local player_name = player:get_player_name() local hud_ids = show_hud_waypoint(player, pointed_pos, message, icon_name) minetest.after(announce_timeout, hide_hud_waypoint, player_name, hud_ids) end end local rounded_pos = vector.round(pointed_pos) minetest.chat_send_all(minetest.colorize("#FF90A8", "[ANNOUNCE] (" .. announcer_name .. ") Look: " .. message .. " " .. minetest.pos_to_string(rounded_pos))) end local function check_click_for_prepared_players(dtime) for player_name, prepared_args in pairs(prepared_announcements) do local player = minetest.get_player_by_name(player_name) if player then local player_controls = player:get_player_control() if player_controls['dig'] then -- place prepared announcement at pointed position add_announcement(player, unpack(prepared_args)) prepared_announcements[player_name] = nil elseif player_controls['place'] then -- cancel prepared announcement minetest.chat_send_player(player_name, "Announcement canceled.") prepared_announcements[player_name] = nil end else -- player is not online anymore, remove prepared announcement prepared_announcements[player_name] = nil end end end minetest.register_globalstep(check_click_for_prepared_players) minetest.register_chatcommand(announce_command_name, { privs = { shout = true, }, func = function(name, param) local player = minetest.get_player_by_name(name) if player then -- player is online local player_name = player:get_player_name() -- TODO limit message length? prepare_announcement(player_name, announce_radius, param, announce_icon, announce_timeout) return true, "Prepared announcement: \"" .. param .. "\". Point and where to put it. ( to cancel)." else return false end end, })