1
0
Fork 0
waypoint_compass/waypoint_lib.lua

267 lines
8.1 KiB
Lua

local WAYPOINT_PRECISION = 1 -- set to 1 to show whole number or 10 for 1 decimal
local UPDATE_INTERVAL = 0.47
-- 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 nil
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()
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_distance = nil,
hud_id_label = nil,
hud_id_icon = nil,
}
self.__index = self
setmetatable(w, self)
w.huddef_distance = {
hud_elem_type = "waypoint",
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=0.5}, -- move down
}
w.huddef_label = {
hud_elem_type = "waypoint",
name = label,
precision = 0, -- value of 0 disables the distance display
number = label_color,
world_pos = point_pos,
offset = {x=0,y=0},
alignment = {x=0, y=4}, -- move futher 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_distance = player:hud_add(self.huddef_distance)
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_distance)
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)
point_pos = point_pos or {x=0, y=0, z=0}
local w = {
point_pos = point_pos,
do_auto_update = do_auto_update,
auto_update_job = nil,
}
self.__index = self
setmetatable(w, self)
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)
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