Reasoning is: - you may want to point in general direction. - sometimes you have mapblocks loaded and can see something in the distance, but they are out of default range for the command.
270 lines
8.2 KiB
Lua
270 lines
8.2 KiB
Lua
local WAYPOINT_PRECISION = 1 -- set to 1 to show whole number or 10 for 1 decimal
|
|
local UPDATE_INTERVAL = 0.22
|
|
|
|
|
|
-- get actual eye_pos of the player (including eye_offset)
|
|
local function player_get_eye_pos(player)
|
|
local p_pos = player:get_pos()
|
|
local p_eye_height = player:get_properties().eye_height
|
|
p_pos.y = p_pos.y + p_eye_height
|
|
local p_eye_pos = p_pos
|
|
local p_eye_offset = vector.multiply(player:get_eye_offset(), 0.1)
|
|
local yaw = player:get_look_horizontal()
|
|
p_eye_pos = vector.add(p_eye_pos, vector.rotate_around_axis(p_eye_offset, {x=0,y=1,z=0}, yaw))
|
|
return p_eye_pos
|
|
end
|
|
|
|
|
|
-- return first thing player is pointing at
|
|
local function raycast_crosshair(player, range, point_to_objects, point_to_liquids)
|
|
local p_eye_pos = player_get_eye_pos(player)
|
|
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
|
|
-- FIXME return "nothing" pointed thing?
|
|
return {
|
|
type = 'nothing',
|
|
pos = to, -- return the end of the ray
|
|
}
|
|
end
|
|
|
|
|
|
-- get position and thing that player is pointing at or nil
|
|
local function get_pointed_position(player, range, point_to_objects, point_to_liquids)
|
|
local pointed_thing = raycast_crosshair(player, range, point_to_objects, point_to_liquids)
|
|
local pointed_pos = nil
|
|
if pointed_thing then
|
|
if pointed_thing.type == "node" then
|
|
-- middle between "above" and "under"
|
|
pointed_pos = vector.multiply(vector.add(pointed_thing.above, pointed_thing.under), 0.5)
|
|
elseif pointed_thing.type == "object" then
|
|
-- TODO point at the middle of collision box? (or not, ground may be better)
|
|
pointed_pos = pointed_thing.ref:get_pos()
|
|
elseif pointed_thing.type == "nothing" then
|
|
-- This should allow you to point in general direction, but
|
|
-- it can place the waypoint inside mountains or behind walls
|
|
-- if those were not loaded.
|
|
pointed_pos = pointed_thing.pos -- this is non-standard, but useful
|
|
end
|
|
end
|
|
return pointed_pos, pointed_thing
|
|
end
|
|
|
|
|
|
local function round(x)
|
|
local f = math.floor(x)
|
|
if (x == f) or (x % 2.0 == 0.5) then
|
|
return f
|
|
else
|
|
return math.floor(x + 0.5)
|
|
end
|
|
end
|
|
|
|
|
|
local function get_texture_rotation_for_direction(dir, quad_steps)
|
|
-- angle relative to player position
|
|
local angle = math.atan2(dir.x, dir.z)
|
|
local pi = math.pi
|
|
-- snap/round to increments and convert to degrees
|
|
local bias = 1/quad_steps/2 -- add bias to floor into correct quardant
|
|
local angle_quad = math.floor(angle/(2*pi) * 4 + bias) * (360 / 4) % 360
|
|
local angle_quad_map = {[0] = "I", [90] = "R270", [180] = "R180", [270] = "R90"}
|
|
local step_size = 90/quad_steps
|
|
local angle_quad_step = (round((math.deg(angle) - angle_quad)/step_size) * step_size) % 90
|
|
return angle_quad_step, angle_quad_map[angle_quad]
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- [[ Waypoint HUD element with distance, label an icon above it ]] --
|
|
local IconWaypointHUD = {}
|
|
function IconWaypointHUD:new(point_pos, label, label_color, icon, icon_scale, icon_color)
|
|
local w = {
|
|
hud_id_label = nil,
|
|
hud_id_icon = nil,
|
|
}
|
|
self.__index = self
|
|
setmetatable(w, self)
|
|
|
|
w.huddef_label = {
|
|
hud_elem_type = "waypoint",
|
|
name = label,
|
|
text = "m", -- distance suffix
|
|
precision = WAYPOINT_PRECISION, -- TODO make this configurable?
|
|
number = label_color,
|
|
world_pos = point_pos,
|
|
offset = {x=0,y=0},
|
|
alignment = {x=0, y=1}, -- move down
|
|
}
|
|
|
|
w.huddef_icon = {
|
|
hud_elem_type = "image_waypoint",
|
|
scale = icon_scale,
|
|
text = icon .. "^[multiply:" .. icon_color,
|
|
alignment = {x=0,y=-1},
|
|
world_pos = point_pos,
|
|
offset = {x=0,y=0},
|
|
--name = name,
|
|
--precision = WAYPOINT_PRECISION,
|
|
--number = waypoint_color,
|
|
}
|
|
|
|
return w
|
|
end
|
|
|
|
|
|
function IconWaypointHUD:show(player)
|
|
--self.player_name = player:get_player_name()
|
|
self.hud_id_label = player:hud_add(self.huddef_label)
|
|
self.hud_id_icon = player:hud_add(self.huddef_icon)
|
|
end
|
|
|
|
|
|
function IconWaypointHUD:hide(player)
|
|
player:hud_remove(self.hud_id_label)
|
|
player:hud_remove(self.hud_id_icon)
|
|
end
|
|
|
|
|
|
function IconWaypointHUD:update(player)
|
|
error("No reason to call IconWaypointHUD:update(), fix your code")
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- [[ Just an arrow HUD element. Can be turned any direction with update() ]] --
|
|
local CompassHUD = {}
|
|
function CompassHUD:new(size, alignment, direction, icon_format, icon_color, quad_steps)
|
|
local c = {
|
|
icon_format = icon_format,
|
|
icon_color = icon_color,
|
|
quad_steps = quad_steps,
|
|
-- TODO have a callback to auto-update the direction?
|
|
}
|
|
self.__index = self
|
|
setmetatable(c, self)
|
|
|
|
local texture = c:get_texture(direction)
|
|
|
|
c.huddef_compass = {
|
|
hud_elem_type = "compass",
|
|
size = size,
|
|
text = texture,
|
|
alignment = alignment,
|
|
dir = 1,
|
|
}
|
|
|
|
return c
|
|
end
|
|
|
|
|
|
function CompassHUD:get_texture(direction)
|
|
local angle_quad_step, rotation_t = get_texture_rotation_for_direction(direction, self.quad_steps)
|
|
|
|
local texture = self.icon_format:format(angle_quad_step) .. "^[transform" .. rotation_t .. "^[multiply:" .. self.icon_color
|
|
return texture
|
|
end
|
|
|
|
|
|
function CompassHUD:show(player)
|
|
--self.player_name = player:get_player_name()
|
|
self.hud_id_compass = player:hud_add(self.huddef_compass)
|
|
end
|
|
|
|
|
|
function CompassHUD:hide(player)
|
|
player:hud_remove(self.hud_id_compass)
|
|
end
|
|
|
|
|
|
function CompassHUD:update(player, direction)
|
|
player:hud_change(self.hud_id_compass, "text", self:get_texture(direction))
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
local WaypointHUD = {}
|
|
function WaypointHUD:new(player, point_pos,
|
|
label, label_color,
|
|
point_icon, point_icon_scale, point_color,
|
|
size, alignment, arrow_icon_format, arrow_icon_color, quad_steps,
|
|
do_auto_update)
|
|
local w = {
|
|
point_pos = point_pos,
|
|
do_auto_update = do_auto_update,
|
|
auto_update_job = nil,
|
|
}
|
|
self.__index = self
|
|
setmetatable(w, self)
|
|
|
|
w.background = player:hud_add({
|
|
hud_elem_type = "image",
|
|
scale = size,
|
|
text = "waypoint_announce_background.png",
|
|
alignment = alignment,
|
|
})
|
|
|
|
w.waypoint_hud = IconWaypointHUD:new(point_pos, label, label_color, point_icon, point_icon_scale, point_color)
|
|
|
|
local d = vector.subtract(point_pos, player:get_pos())
|
|
w.compass_hud = CompassHUD:new(size, alignment, d, arrow_icon_format, arrow_icon_color, quad_steps)
|
|
|
|
return w
|
|
end
|
|
|
|
|
|
function WaypointHUD:show(player)
|
|
self.waypoint_hud:show(player)
|
|
self.compass_hud:show(player)
|
|
self:update(player) -- FIXME? needed to start auto update
|
|
end
|
|
|
|
|
|
function WaypointHUD:hide(player)
|
|
self.waypoint_hud:hide(player)
|
|
self.compass_hud:hide(player)
|
|
player:hud_remove(self.background)
|
|
if self.auto_update_job then
|
|
self.auto_update_job:cancel()
|
|
self.auto_update_job = nil
|
|
end
|
|
end
|
|
|
|
|
|
function WaypointHUD:update(player)
|
|
local direction = vector.subtract(self.point_pos, player:get_pos())
|
|
self.compass_hud:update(player, direction)
|
|
if self.do_auto_update then
|
|
local player_name = player:get_player_name()
|
|
local job = minetest.after(UPDATE_INTERVAL,
|
|
function()
|
|
local player = minetest.get_player_by_name(player_name)
|
|
if player then
|
|
self:update(player)
|
|
end
|
|
end
|
|
)
|
|
self.auto_update_job = job
|
|
end
|
|
end
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
waypoint_lib = {
|
|
raycast_crosshair = raycast_crosshair,
|
|
get_pointed_position = get_pointed_position,
|
|
IconWaypointHUD = IconWaypointHUD,
|
|
CompassHUD = CompassHUD,
|
|
WaypointHUD = WaypointHUD,
|
|
}
|
|
|
|
return waypoint_lib
|