Skip to content
Snippets Groups Projects
Commit eb7207b3 authored by Peter Nerlich's avatar Peter Nerlich
Browse files

extend death markers

- persist through player disconnects and server restarts
- different text and display time for survival and creative
- remove marker when bones are removed
- fade marker from white to black before disappearing, with blue, green and red tint to indicate how old the death is
- make important variables settings
parent baa44b92
No related branches found
No related tags found
No related merge requests found
-- adopted from https://github.com/pandorabox-io/pandorabox_custom/blob/master/death.lua
-- adapted from https://github.com/pandorabox-io/pandorabox_custom/blob/master/death.lua
local WAYPOINT_EXPIRES_SECONDS = tonumber(minetest.settings:get("kif_custom.death.waypoint_expires_seconds")) or 60 * 60
local WAYPOINT_EXPIRES_SECONDS_CREATIVE = tonumber(minetest.settings:get("kif_custom.death.waypoint_expires_seconds_creative")) or 5 * 60
local UPDATE_INTERVAL = tonumber(minetest.settings:get("kif_custom.death.update_interval")) or 5
local WAYPOINT_SATURATION = tonumber(minetest.settings:get("kif_custom.death.waypoint_saturation")) or 1
local MARKERS_FILE = minetest.get_worldpath().."/death_markers.json"
-- table for deaths indexed by player name and pos
local death_markers = {}
local dirty = false
local WRITEBACK_INTERVAL_SEC = 10
-- table to remember which hud elements we know and manage (per player and pos)
local managed_hud_ids = {}
-- color calculation got a bit crazy, so let's cache these...
local color_cache = {}
local read_json_file = function(path)
local file = io.open(path, "r")
local content = {}
if file then
local json = file:read("*a")
content = minetest.parse_json(json or "[]") or {}
file:close()
end
return content
end
local write_json_file = function(path, content)
local file = io.open(path,"w")
local json = minetest.write_json(content)
if file and file:write(json) and file:close() then
return true
else
return false
end
end
death_markers = read_json_file(MARKERS_FILE)
local mark_dirty_and_schedule_writeback = function()
if not dirty then
dirty = true
minetest.after(WRITEBACK_INTERVAL_SEC, function()
if dirty then
write_json_file(MARKERS_FILE, death_markers)
dirty = false
end
end)
end
end
-- add something to the bones on_punch to remove markers when the bones are picked up
local bones_def = minetest.registered_items["bones:bones"]
assert(bones_def)
local bones_on_punch = bones_def.on_punch
assert(bones_on_punch)
assert(type(bones_on_punch) == "function")
minetest.override_item("bones:bones", {
on_punch = function(pos, node, player)
-- call original function
bones_on_punch(pos, node, player)
local player_name = player:get_player_name()
local pos_string = minetest.pos_to_string(pos)
if death_markers[player_name] ~= nil and death_markers[player_name][pos_string] ~= nil then
minetest.after(1, function()
if minetest.get_node(pos).name ~= "bones:bones" then
death_markers[player_name][pos_string] = nil
update_hud_markers(player_name)
end
end)
end
end
})
minetest.register_on_shutdown(function()
if dirty then
write_json_file(MARKERS_FILE, death_markers)
dirty = false
end
-- clean up markers (not sure if this makes sense, but I got weird results before, so...)
for player_name, hud_ids in pairs(managed_hud_ids) do
local player = minetest.get_player_by_name(player_name)
if player ~= nil then
for pos_string, marker in pairs(hud_ids) do
player:hud_remove(hud_id)
end
end
managed_hud_ids[player_name] = nil
end
end)
local interpolate = function(a, b, t)
return t*(b-a) + a
end
local calculate_color = function(t)
-- clamp t between 0 and 1, in steps of 0.01
t = math.max(0, math.min(1, math.floor(t * 100 + 0.5) / 100))
if color_cache[t] == nil then
-- helper variables
local t2 = t^2 -- t squared
local it = 1 - t -- inverse t
local it2 = it^2 -- inverse t squared
local r = t2
local g = 2*t * it
local b = it2
local avg = math.sqrt(r^2 + g^2 + b^2)
r = interpolate(avg, r, WAYPOINT_SATURATION)
g = interpolate(avg, g, WAYPOINT_SATURATION)
b = interpolate(avg, b, WAYPOINT_SATURATION)
local white_start = it2^2
local dimming = 1 - t^8
r = (r * (1-white_start) + white_start) * dimming
g = (g * (1-white_start) + white_start) * dimming
b = (b * (1-white_start) + white_start) * dimming
-- we have 255 steps per subpixel
local base = 0xFF
-- clamp values, discard fractions
r = math.floor(base * math.max(0, math.min(1, r)))
g = math.floor(base * math.max(0, math.min(1, g)))
b = math.floor(base * math.max(0, math.min(1, b)))
-- pack it into one number representing the RGB values
color_cache[t] = r*2^16 + g*2^8 + b
end
return color_cache[t]
end
local update_hud_markers = function(player_name)
if death_markers[player_name] ~= nil then
local player = minetest.get_player_by_name(player_name)
local now = os.time()
local valid_hud_ids = {}
for pos_string, marker in pairs(death_markers[player_name]) do
local t = now - marker.timestamp
if marker.is_creative then
t = t / WAYPOINT_EXPIRES_SECONDS_CREATIVE
else
t = t / WAYPOINT_EXPIRES_SECONDS
end
if managed_hud_ids[player_name] ~= nil and managed_hud_ids[player_name][pos_string] ~= nil then
local hud_id = managed_hud_ids[player_name][pos_string]
valid_hud_ids[hud_id] = hud_id
if t > 1 then
death_markers[player_name][pos_string] = nil
mark_dirty_and_schedule_writeback()
if player ~= nil then
player:hud_remove(hud_id)
managed_hud_ids[player_name][pos_string] = nil
end
else
if player ~= nil then
player:hud_change(hud_id, "name", marker.text)--.." (t = "..tostring(math.floor(t * 100 + 0.5) / 100)..")")
player:hud_change(hud_id, "number", calculate_color(t))
end
end
else
if player ~= nil and t < 1 then
if managed_hud_ids[player_name] == nil then
managed_hud_ids[player_name] = {}
end
local pos = minetest.string_to_pos(pos_string)
local hud_id = player:hud_add({
hud_elem_type = "waypoint",
name = marker.text,--.." (add | t = "..tostring(math.floor(t * 100 + 0.5) / 100)..")",
text = "m",
number = calculate_color(t),
world_pos = pos
})
managed_hud_ids[player_name][pos_string] = hud_id
valid_hud_ids[hud_id] = hud_id
end
end
end
if player ~= nil and managed_hud_ids[player_name] ~= nil then
-- everything that remains should be removed
for pos_string, hud_id in pairs(managed_hud_ids[player_name]) do
if valid_hud_ids[hud_id] == nil then
player:hud_remove(hud_id)
managed_hud_ids[player_name][pos_string] = nil
end
end
end
end
end
minetest.register_on_dieplayer(function(player)
local player_name = player:get_player_name()
local is_creative = creative and creative.is_enabled_for(player_name)
local pos = player:get_pos()
pos.x = math.floor(pos.x + 0.5)
......@@ -12,31 +228,72 @@ minetest.register_on_dieplayer(function(player)
local pos_string = minetest.pos_to_string(pos)
minetest.log("action", "[death] player '" .. player_name .. "' died at " .. pos_string)
minetest.log("action", "[death] player '" .. player_name .. "' died at " .. pos_string .. (is_creative and " (creative)" or ""))
minetest.chat_send_player(player_name, "You died at " .. pos_string)
local bone_string = "Bones"
local msg_string = ""
if not is_creative then
msg_string = "Bones"
if player.get_attribute then
-- [xp_redo] keeps track of deathcount, let's see if it is there
local count = player:get_attribute("died")
if count then
bone_string = "Bone #" .. tostring(count)
msg_string = "Bone #" .. tostring(count)
end
end
else
msg_string = "Death"
end
end -- if not fake player
local hud_id = player:hud_add({
hud_elem_type = "waypoint",
name = bone_string .. " " .. pos_string,
text = "m",
number = 0xFFFFFF,
world_pos = pos
})
minetest.after(BONES_WAYPOINT_EXPIRES_SECONDS, function()
-- retrieve player by name, the "player" object should not be carried across server-steps
player = minetest.get_player_by_name(player_name)
if player then
player:hud_remove(hud_id)
if death_markers[player_name] == nil then
death_markers[player_name] = {}
end
death_markers[player_name][pos_string] = {
text = msg_string,
timestamp = os.time(),
is_creative = is_creative,
}
mark_dirty_and_schedule_writeback()
update_hud_markers(player_name)
end)
minetest.register_on_joinplayer(function(player)
local player_name = player:get_player_name()
update_hud_markers(player_name)
if death_markers[player_name] ~= nil then
minetest.after(2, function()
local markers = {}
for pos_string, marker in pairs(death_markers[player_name]) do
table.insert(markers, pos_string.." "..marker.text..(marker.is_creative and " (creative)" or ""))
end
--minetest.chat_send_player(player_name, "Your death markers ("..tostring(#markers).."): "..table.concat(markers, ", "))
end)
end
end)
minetest.register_on_leaveplayer(function(player)
local player_name = player:get_player_name()
--[[
for pos_string, marker in pairs(managed_hud_ids[player_name]) do
player:hud_remove(hud_id)
end
managed_hud_ids[player_name] = nil
]]--
end)
local update_loop
update_loop = function()
for player_name, markers in pairs(managed_hud_ids) do
update_hud_markers(player_name)
end
minetest.after(UPDATE_INTERVAL, update_loop)
end
update_loop()
[Death Markers]
# Interval at which death markers are updated in seconds
kif_custom.death.update_interval (Update interval) int 5
# The amount of seconds in which waypoints to bones expire.
kif_custom.death.waypoint_expires_seconds (Seconds in which waypoints expire) int 3600
# The amount of seconds in which waypoints for deaths in creative mode expire.
kif_custom.death.waypoint_expires_seconds_creative (Seconds in which waypoints expire, creative) int 600
# The saturation of the waypoint text color. The text fades not only from white to black
# until it disappears, but also changes to a blue, green and finally red tint before disappearing.
# The saturation interpolates between the color and the grey shade of the same value.
kif_custom.death.waypoint_saturation (Color saturation) float 1 0 2
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment