waypoint_announce/init.lua

162 lines
6.0 KiB
Lua

-- 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 <hit> where to put it. (<place> to cancel)."
else
return false
end
end,
})