From 6e76ef432034e7e8ad7a0d6a076eaea9feb9b782 Mon Sep 17 00:00:00 2001 From: Maksym H Date: Mon, 29 Aug 2022 21:20:56 +0300 Subject: [PATCH] HUD: use the HUD API directly (#92) Co-authored-by: luk3yx --- builtin/game/hud.lua | 167 -------------------------------------- builtin/game/hunger.lua | 74 +++++++++++------ builtin/game/init.lua | 1 - builtin/game/statbars.lua | 125 ++++++++++++++++++---------- 4 files changed, 134 insertions(+), 233 deletions(-) delete mode 100644 builtin/game/hud.lua diff --git a/builtin/game/hud.lua b/builtin/game/hud.lua deleted file mode 100644 index aebb0fd63..000000000 --- a/builtin/game/hud.lua +++ /dev/null @@ -1,167 +0,0 @@ ---[[ - From Better HUD mod - Copyright (C) BlockMen (2013-2016) - Copyright (C) MultiCraft Development Team (2019-2020) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 3.0 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -]] - -hud, hud_id = {}, {} - -local items, sb_bg = {}, {} - -local function throw_error(msg) - core.log("error", "HUD: " .. msg) -end - --- --- API --- - -function hud.register(name, def) - if not name or not def then - throw_error("Not enough parameters given") - return false - end - - if items[name] then - throw_error("A statbar with name " .. name .. " already exists") - return false - end - - -- actually register - -- add background first since draworder is based on id - if def.hud_elem_type == "statbar" and def.background then - sb_bg[name] = table.copy(def) - sb_bg[name].text = def.background - if not def.autohide_bg and def.max then - sb_bg[name].number = def.max - end - end - -- add item itself - items[name] = def - - return true -end - -function hud.change_item(player, name, def) - if not player or not player:is_player() then - return false - end - - if not name or not def then - throw_error("Not enough parameters given") - return false - end - - local i_name = player:get_player_name() .. "_" .. name - local elem = hud_id[i_name] - local item = items[name] - - if not elem then - -- throw_error("Given HUD element " .. dump(name) .. " does not exist") - return false - end - - if def.number then - if item.hud_elem_type ~= "statbar" then - throw_error("Attempted to change an statbar HUD parameter for text HUD element " .. dump(name)) - return false - end - - if item.max and def.number > item.max then - def.number = item.max - end - player:hud_change(elem.id, "number", def.number) - - -- hide background when set - if item.autohide_bg then - local bg = hud_id[i_name .. "_bg"] - if not bg then - throw_error("Given HUD element " .. dump(name) .. "_bg does not exist") - return false - end - - local num = def.number ~= 0 and (item.max or item.number) or 0 - player:hud_change(bg.id, "number", num) - end - elseif def.text then - player:hud_change(elem.id, "text", def.text) - elseif def.offset then - player:hud_change(elem.id, "offset", def.offset) - end - - return true -end - -function hud.remove_item(player, name) - if not player or not player:is_player() then - return false - end - - if not name then - throw_error("Not enough parameters given") - return false - end - - local i_name = player:get_player_name() .. "_" .. name - local elem = hud_id[i_name] - - if not elem then - throw_error("Given HUD element " .. dump(name) .. " does not exist") - return false - end - - player:hud_remove(elem.id) - - return true -end - --- --- Add registered HUD items to joining players --- - --- Following code is placed here to keep HUD ids internal -local function add_hud_item(player, name, def) - if not player or not name or not def then - throw_error("Not enough parameters given") - return false - end - - local i_name = player:get_player_name() .. "_" .. name - hud_id[i_name] = {} - hud_id[i_name].id = player:hud_add(def) -end - -core.register_on_joinplayer(function(player) - -- add the backgrounds for statbars - for name, item in pairs(sb_bg) do - add_hud_item(player, name .. "_bg", item) - end - -- and finally the actual HUD items - for name, item in pairs(items) do - add_hud_item(player, name, item) - end -end) - -core.register_on_leaveplayer(function(player) - local player_name = player:get_player_name() - for name, _ in pairs(sb_bg) do - sb_bg[player_name .. "_" .. name] = nil - end - for name, _ in pairs(items) do - hud_id[player_name .. "_" .. name] = nil - end -end) diff --git a/builtin/game/hunger.lua b/builtin/game/hunger.lua index fcc02b0ae..b46b86b9e 100644 --- a/builtin/game/hunger.lua +++ b/builtin/game/hunger.lua @@ -25,7 +25,9 @@ or not core.settings:get_bool("enable_hunger") then return end -hunger = {} +hunger = { + hud = {} +} local function get_setting(key, default) local setting = core.settings:get("hunger." .. key) @@ -57,6 +59,7 @@ local settings = hunger.settings local min, max = math.min, math.max local vlength = vector.length +local tcopy = table.copy local attribute = { saturation = "hunger:level", @@ -83,9 +86,18 @@ function hunger.get_saturation(player) return tonumber(player:get_meta():get_string(attribute.saturation)) end +function hunger.set_hud_level(player, level) + if not is_player(player) then + return + end + + local hud = hunger.hud[player:get_player_name()] + player:hud_change(hud, "number", min(settings.visual_max, level)) +end + function hunger.set_saturation(player, level) player:get_meta():set_int(attribute.saturation, level) - hud.change_item(player, "hunger", {number = min(settings.visual_max, level)}) + hunger.set_hud_level(player, level) end hunger.registered_on_update_saturations = {} @@ -145,14 +157,23 @@ function hunger.is_poisoned(player) return poisoned and poisoned == "yes" end +function hunger.set_hud_poisoned(player, poisoned) + if not is_player(player) then + return + end + + local hud = hunger.hud[player:get_player_name()] + local texture = poisoned and "hunger_poisen.png" or "hunger.png" + player:hud_change(hud, "text", texture) +end + function hunger.set_poisoned(player, poisoned) if not is_player(player) then return end - local texture = poisoned and "hunger_poisen.png" or "hunger.png" + hunger.set_hud_poisoned(player, poisoned) local attr = poisoned and "yes" or "no" - hud.change_item(player, "hunger", {text = texture}) player:get_meta():set_string(attribute.poisoned, attr) end @@ -298,9 +319,9 @@ local function health_tick() player:set_hp(hp + settings.heal) elseif is_starving then player:set_hp(hp - settings.starve) - hud.change_item(player, "hunger", {number = 0}) + hunger.set_hud_level(player, 0) core.after(0.5, function() - hud.change_item(player, "hunger", {number = min(settings.visual_max, saturation)}) + hunger.set_hud_level(player, min(settings.visual_max, saturation)) end) end end @@ -341,30 +362,37 @@ function hunger.item_eat(hp_change, user, poison) end end -hud.register("hunger", { +hunger.bar_definition = { hud_elem_type = "statbar", - position = {x = 0.5, y = 1}, - offset = {x = 8, y = -94}, - size = {x = 24, y = 24}, - text = "hunger.png", - background = "hunger_gone.png", - number = settings.visual_max -}) + position = {x = 0.5, y = 1}, + text = "hunger.png", + text2 = "hunger_gone.png", + number = settings.visual_max, + item = settings.visual_max, + size = {x = 24, y = 24}, + offset = {x = -265, y = -126} +} core.register_on_joinplayer(function(player) - local level = hunger.get_saturation(player) or settings.level_max + local level = hunger.get_saturation(player) + if not level then + level = settings.level_max + player:get_meta():set_int(attribute.saturation, level) + end + + -- add HUD + if hunger.bar_definition then + local def = tcopy(hunger.bar_definition) + def.number = min(settings.visual_max, level) + hunger.hud[player:get_player_name()] = player:hud_add(def) + end -- reset poisoned hunger.set_poisoned(player, false) - -- set saturation - player:get_meta():set_int(attribute.saturation, level) +end) - -- we must manually update the HUD - if level < settings.visual_max then - core.after(1, function() - hud.change_item(player, "hunger", {number = min(settings.visual_max, level)}) - end) - end +core.register_on_leaveplayer(function(player) + hunger.hud[player:get_player_name()] = nil end) local exhaust = hunger.exhaust_player diff --git a/builtin/game/init.lua b/builtin/game/init.lua index 500e2aa90..92c67862d 100644 --- a/builtin/game/init.lua +++ b/builtin/game/init.lua @@ -31,7 +31,6 @@ assert(loadfile(gamepath .. "falling.lua"))(builtin_shared) dofile(gamepath .. "features.lua") dofile(gamepath .. "voxelarea.lua") dofile(gamepath .. "forceloading.lua") -dofile(gamepath .. "hud.lua") dofile(gamepath .. "statbars.lua") dofile(gamepath .. "knockback.lua") dofile(gamepath .. "hunger.lua") diff --git a/builtin/game/statbars.lua b/builtin/game/statbars.lua index 3cf5b1727..bc33ec619 100644 --- a/builtin/game/statbars.lua +++ b/builtin/game/statbars.lua @@ -1,28 +1,44 @@ -- cache setting local enable_damage = core.settings:get_bool("enable_damage") -local disable_health = false + +local max = math.max +local tcopy = table.copy local health_bar_definition = { hud_elem_type = "statbar", - position = {x = 0.5, y = 1}, - offset = {x = -247, y = -94}, - size = {x = 24, y = 24}, - text = "heart.png", - background = "heart_gone.png", - number = 20 + position = {x = 0.5, y = 1}, + text = "heart.png", + text2 = "heart_gone.png", + number = core.PLAYER_MAX_HP_DEFAULT, + item = core.PLAYER_MAX_HP_DEFAULT, + direction = 0, + size = {x = 24, y = 24}, + offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)}, } local breath_bar_definition = { hud_elem_type = "statbar", - position = {x = 0.5, y = 1}, - offset = {x = 8, y = -120}, - size = {x = 24, y = 24}, - text = "bubble.png", - number = 20 + position = {x = 0.5, y = 1}, + text = "bubble.png", + text2 = "bubble_gone.png", + number = core.PLAYER_MAX_BREATH_DEFAULT, + item = core.PLAYER_MAX_BREATH_DEFAULT * 2, + direction = 0, + size = {x = 24, y = 24}, + offset = {x = 25, y= -(48 + 24 + 16)}, } local hud_ids = {} +local function scaleToDefault(player, field) + -- Scale "hp" or "breath" to the default dimensions + local current = player["get_" .. field](player) + local nominal = core["PLAYER_MAX_" .. field:upper() .. "_DEFAULT"] + local max_display = max(nominal, + max(player:get_properties()[field .. "_max"], current)) + return current / max_display * nominal +end + local function update_builtin_statbars(player) local name = player:get_player_name() @@ -37,24 +53,45 @@ local function update_builtin_statbars(player) -- our current flags are transmitted by sending them actively player:hud_set_flags(flags) end - local hud_id = hud_ids[name] + local hud = hud_ids[name] - if flags.healthbar and not disable_health then - hud.change_item(player, "health", {number = player:get_hp()}) - end - - if flags.breathbar and player:get_breath() < 10 then - local number = player:get_breath() * 2 - if hud_id.id_breathbar == nil then - local hud_def = table.copy(breath_bar_definition) + if flags.healthbar and enable_damage then + local number = scaleToDefault(player, "hp") + if hud.id_healthbar == nil then + local hud_def = tcopy(health_bar_definition) hud_def.number = number - hud_id.id_breathbar = player:hud_add(hud_def) + hud.id_healthbar = player:hud_add(hud_def) else - player:hud_change(hud_id.id_breathbar, "number", number) + player:hud_change(hud.id_healthbar, "number", number) + end + elseif hud.id_healthbar then + player:hud_remove(hud.id_healthbar) + hud.id_healthbar = nil end - elseif hud_id.id_breathbar then - player:hud_remove(hud_id.id_breathbar) - hud_id.id_breathbar = nil + + local show_breathbar = flags.breathbar and enable_damage + + local breath = player:get_breath() + local breath_max = player:get_properties().breath_max + if show_breathbar and breath <= breath_max then + local number = 2 * scaleToDefault(player, "breath") + if not hud.id_breathbar and breath < breath_max then + local hud_def = tcopy(breath_bar_definition) + hud_def.number = number + hud.id_breathbar = player:hud_add(hud_def) + elseif hud.id_breathbar then + player:hud_change(hud.id_breathbar, "number", number) + end + end + + if hud.id_breathbar and (not show_breathbar or breath == breath_max) then + core.after(1, function(player_name, breath_bar) + local player = core.get_player_by_name(player_name) + if player then + player:hud_remove(breath_bar) + end + end, name, hud.id_breathbar) + hud.id_breathbar = nil end end @@ -73,7 +110,7 @@ local function player_event_handler(player,eventname) local name = player:get_player_name() - if name == "" then + if name == "" or not hud_ids[name] then return end @@ -102,16 +139,25 @@ local function player_event_handler(player,eventname) end function core.hud_replace_builtin(hud_name, definition) - if hud_name == "health" then - disable_health = true - return true - end - if type(definition) ~= "table" or definition.hud_elem_type ~= "statbar" then return false end + if hud_name == "health" then + health_bar_definition = definition + + for name, ids in pairs(hud_ids) do + local player = core.get_player_by_name(name) + if player and ids.id_healthbar then + player:hud_remove(ids.id_healthbar) + ids.id_healthbar = nil + update_builtin_statbars(player) + end + end + return true + end + if hud_name == "breath" then breath_bar_definition = definition @@ -131,13 +177,8 @@ end -- Append "update_builtin_statbars" as late as possible -- This ensures that the HUD is hidden when the flags are updated in this callback -if enable_damage then - core.register_on_mods_loaded(function() - if not disable_health then - hud.register("health", health_bar_definition) - core.register_on_joinplayer(update_builtin_statbars) - end - end) - core.register_on_leaveplayer(cleanup_builtin_statbars) - core.register_playerevent(player_event_handler) -end +core.register_on_mods_loaded(function() + core.register_on_joinplayer(update_builtin_statbars) +end) +core.register_on_leaveplayer(cleanup_builtin_statbars) +core.register_playerevent(player_event_handler)