Aaron Suen 4de1867d75 Remove health descriptions from player HUD text.
We can already see these plainly on the model now.
2019-03-02 10:17:33 -05:00

202 lines
5.5 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local math, minetest, nodecore, pairs, string, tonumber
= math, minetest, nodecore, pairs, string, tonumber
local math_sqrt, string_format
= math.sqrt, string.format
-- LUALOCALS > ---------------------------------------------------------
local modname = minetest.get_current_modname()
-- Maximum distance at which custom nametags are visible.
local distance = tonumber(minetest.setting_get(modname .. "_distance")) or 8
-- Precision (number of steps) for line-of-sight check for displaying nametags
local precision = tonumber(minetest.setting_get(modname .. "_precision")) or 50
-- Keep track of active player HUDs.
local huds = {}
------------------------------------------------------------------------
-- PLAYER JOIN/LEAVE
-- On player joining, disable the built-in nametag by setting its
-- text to whitespace and color to transparent.
minetest.register_on_joinplayer(function(player)
player:set_nametag_attributes({
text = " ",
color = {a = 0, r = 0, g = 0, b = 0}
})
end)
-- On player leaving, clean up any associated HUDs.
minetest.register_on_leaveplayer(function(player)
-- Garbage-collect player's own HUDs.
local pn = player:get_player_name()
huds[pn] = nil
-- Remove HUDs for this player's name
-- from other players
for k, v in pairs(huds) do
local i = v[pn]
if i then
i.o:hud_remove(i.i)
v[pn] = nil
end
end
end)
------------------------------------------------------------------------
-- GLOBAL TICK HUD MANAGEMENT
-- Get custom text for a visible HUD.
local function gettext(p2, n2)
-- First line: distance units, player HP.
local t = "m"
-- Check for a wielded item, and add its description
-- to a line below if available.
local w = p2:get_wielded_item()
if w then
w = w:get_name()
local r = minetest.registered_items[w]
t = t .. "\n" .. (r and r.description or w)
end
if not nodecore.interact(n2) then
t = t .. "\nSPECTATOR"
end
return t
end
-- Determine if two players can see one another, using a
-- shared cache for commutitivity.
local function cansee(p1, n1, p2, n2, los)
-- Sight is communitive: if p1 can see p2, p2 can see p1.
-- Compute a single shared cache key for both players that's
-- independent of order, and check for a cached result for
-- this player pair.
local loskey = (n1 < n2)
and (string_format("%q", n1) .. "|" .. string_format("%q", n2))
or (string_format("%q", n2) .. "|" .. string_format("%q", n1))
local l = los[loskey]
if l ~= nil then return l end
-- Dead players neither see, nor are recognizable.
if p1:get_hp() < 1 or p2:get_hp() < 1 then
los[loskey] = false
return
end
-- Players must be within max distance of one another.
local o1 = p1:getpos()
local o2 = p2:getpos()
local dx = o1.x - o2.x
local dy = o1.y - o2.y
local dz = o1.z - o2.z
if (dx * dx + dy * dy + dz * dz) > (distance * distance) then
los[loskey] = false
return
end
-- Check for line of sight from approximage eye level
-- of one player to the other.
o1.y = o1.y + 1.5
o2.y = o2.y + 1.5
l = minetest.line_of_sight(o1, o2, distance / precision) or false
-- Cache result (for checking opposite direction).
los[loskey] = l
return l
end
-- Determine if player 1 can see player 2's face, including
-- checks for distance, line-of-sight, and facing direction.
local function canseeface(p1, n1, p2, n2, los)
-- Checks for reciprocal line-of-sight.
if not cansee(p1, n1, p2, n2, los) then return end
-- Players must be facing each other; cannot identify another
-- player's face when their back is turned. Note that
-- minetest models don't show pitch, so ignore the y component.
-- Compute normalized 2d vector from one player to another.
local o1 = p1:getpos()
local o2 = p2:getpos()
local ll = minetest.get_node_light({x = o2.x, y = o2.y + 1.6, z = o2.z})
if ll < 5 then return end
local dx = o1.x - o2.x
local dz = o1.z - o2.z
local d = dx * dx + dz * dz
if d == 0 then return end
d = math_sqrt(d)
dx = dx / d
dz = dz / d
-- Compute normalized 2d facing direction vector for target player.
local l2 = p2:get_look_dir()
d = l2.x * l2.x + l2.z * l2.z
if d == 0 then return end
d = math_sqrt(d)
l2.x = l2.x / d
l2.z = l2.z / d
-- Compare directions via dot product.
if (dx * l2.x + dz * l2.z) <= 0.5 then return end
return true
end
-- On each global step, check all player visibility, and create/remove/update
-- each player's HUDs accordingly.
minetest.register_globalstep(function()
local los = {}
local conn = minetest.get_connected_players()
for _, p1 in pairs(conn) do
local n1 = p1:get_player_name()
local h = huds[n1]
if not h then
h = {}
huds[n1] = h
end
for _, p2 in pairs(conn) do
if p2 ~= p1 then
local n2 = p2:get_player_name()
local i = h[n2]
if canseeface(p1, n1, p2, n2, los) then
local p = p2:getpos()
p.y = p.y + 1.25
local t = gettext(p2, n2)
-- Create a new HUD if not present.
if not i then
i = {o = p1, t = t, p = p}
i.i = p1:hud_add({
hud_elem_type = "waypoint",
world_pos = p,
name = n2,
text = t,
number = 0xffffff
})
h[n2] = i
end
-- Update HUD if outdated.
if p.x ~= i.p.x or p.y ~= i.p.y or p.z ~= i.p.z then
p1:hud_change(i.i, "world_pos", p)
i.p = p
end
if i.t ~= t then
p1:hud_change(i.i, "text", t)
i.t = t
end
elseif i then
-- Remove HUD if visibility lost.
p1:hud_remove(i.i)
h[n2] = nil
end
end
end
end
end)