From df5d24104dde611534da8d2cb096c32c6043c60e Mon Sep 17 00:00:00 2001 From: kabou Date: Wed, 2 Mar 2022 14:57:20 +0100 Subject: [PATCH 1/6] Make player burning HUD work without csm. * Add update_hud() function. * Remove the client channels. * Unglobalize animation_frames variable. * Fix bug where player state was not stored on shutdown of singleplayer game. * Remove superfluous sanity_check() function, this code could easily be inlined in its only caller. --- mods/ENTITIES/mcl_burning/api.lua | 30 +++++++++++-- mods/ENTITIES/mcl_burning/init.lua | 68 ++++++++++++++++-------------- 2 files changed, 63 insertions(+), 35 deletions(-) diff --git a/mods/ENTITIES/mcl_burning/api.lua b/mods/ENTITIES/mcl_burning/api.lua index 9699852053..5890d8892c 100644 --- a/mods/ENTITIES/mcl_burning/api.lua +++ b/mods/ENTITIES/mcl_burning/api.lua @@ -33,6 +33,29 @@ function mcl_burning.get_touching_nodes(obj, nodenames, storage) return nodes end +function mcl_burning.update_hud(player) + local animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8 + local hud_flame_animated = "mcl_burning_hud_flame_animated.png^[opacity:180^[verticalframe:" .. animation_frames .. ":" + + local storage = mcl_burning.get_storage(player) + if not storage.fire_hud_id then + storage.animation_frame = 1 + storage.fire_hud_id = player:hud_add({ + hud_elem_type = "image", + position = {x = 0.5, y = 0.5}, + scale = {x = -100, y = -100}, + text = hud_flame_animated .. storage.animation_frame, + z_index = 1000, + }) + else + storage.animation_frame = storage.animation_frame + 1 + if storage.animation_frame > animation_frames - 1 then + storage.animation_frame = 0 + end + player:hud_change(storage.fire_hud_id, "text", hud_flame_animated .. storage.animation_frame) + end +end + function mcl_burning.set_on_fire(obj, burn_time) if obj:get_hp() < 0 then return @@ -68,8 +91,7 @@ function mcl_burning.set_on_fire(obj, burn_time) if not storage.burn_time or burn_time >= storage.burn_time then if obj:is_player() then - mcl_burning.channels[obj]:send_all(tostring(mcl_burning.animation_frames)) - mcl_burning.channels[obj]:send_all("start") + mcl_burning.update_hud(obj) end storage.burn_time = burn_time storage.fire_damage_timer = 0 @@ -105,7 +127,9 @@ function mcl_burning.extinguish(obj) if mcl_burning.is_burning(obj) then local storage = mcl_burning.get_storage(obj) if obj:is_player() then - mcl_burning.channels[obj]:send_all("stop") + if storage.fire_hud_id then + obj:hud_remove(storage.fire_hud_id) + end mcl_burning.storage[obj] = {} else storage.burn_time = nil diff --git a/mods/ENTITIES/mcl_burning/init.lua b/mods/ENTITIES/mcl_burning/init.lua index cc82527cc5..82410dfdd3 100644 --- a/mods/ENTITIES/mcl_burning/init.lua +++ b/mods/ENTITIES/mcl_burning/init.lua @@ -1,14 +1,11 @@ local modpath = minetest.get_modpath(minetest.get_current_modname()) local pairs = pairs - local get_connected_players = minetest.get_connected_players local get_item_group = minetest.get_item_group mcl_burning = { - storage = {}, - channels = {}, - animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8 + storage = {} } dofile(modpath .. "/api.lua") @@ -44,25 +41,33 @@ minetest.register_on_respawnplayer(function(player) mcl_burning.extinguish(player) end) -function mcl_burning.init_player(player) - local meta = player:get_meta() - -- NOTE: mcl_burning:data may be "return nil" (which deserialize into nil) for reasons unknown. - if meta:get_string("mcl_burning:data"):find("return nil", 1, true) then - minetest.log("warning", "[mcl_burning] 'mcl_burning:data' player meta field is invalid! Please report this bug") +minetest.register_on_joinplayer(function(player) + local storage = {} + local burn_data = player:get_meta():get_string("mcl_burning:data") + if burn_data ~= "" then + storage = minetest.deserialize(burn_data) end - mcl_burning.storage[player] = meta:contains("mcl_burning:data") and minetest.deserialize(meta:get_string("mcl_burning:data")) or {} - mcl_burning.channels[player] = minetest.mod_channel_join("mcl_burning:" .. player:get_player_name()) + mcl_burning.storage[player] = storage +end) + +local function on_leaveplayer(player) + local storage = mcl_burning.storage[player] + storage.fire_hud_id = nil + player:get_meta():set_string("mcl_burning:data", minetest.serialize(storage)) + mcl_burning.storage[player] = nil end -minetest.register_on_joinplayer(function(player) - mcl_burning.init_player(player) -end) - minetest.register_on_leaveplayer(function(player) - player:get_meta():set_string("mcl_burning:data", minetest.serialize(mcl_burning.storage[player])) - mcl_burning.storage[player] = nil + on_leaveplayer(player) end) +minetest.register_on_shutdown(function() + for _,player in ipairs(minetest.get_connected_players()) do + on_leaveplayer(player) + end +end) + +local animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8 minetest.register_entity("mcl_burning:fire", { initial_properties = { @@ -73,7 +78,7 @@ minetest.register_entity("mcl_burning:fire", { "mcl_burning_entity_flame_animated.png", "mcl_burning_entity_flame_animated.png" }, - spritediv = {x = 1, y = mcl_burning.animation_frames}, + spritediv = {x = 1, y = animation_frames}, pointable = false, glow = -1, backface_culling = false, @@ -81,26 +86,25 @@ minetest.register_entity("mcl_burning:fire", { animation_frame = 0, animation_timer = 0, on_activate = function(self) - self.object:set_sprite({x = 0, y = 0}, mcl_burning.animation_frames, 1.0 / mcl_burning.animation_frames) + self.object:set_sprite({x = 0, y = 0}, animation_frames, 1.0 / animation_frames) end, - on_step = function(self) - if not self:sanity_check() then - self.object:remove() - end - end, - sanity_check = function(self) + on_step = function(self, dtime) local parent = self.object:get_attach() - if not parent then - return false + self.object:remove() + return end - local storage = mcl_burning.get_storage(parent) - if not storage or not storage.burn_time then - return false + self.object:remove() + return + end + if parent:is_player() then + self.animation_timer = self.animation_timer + dtime + if self.animation_timer >= 0.1 then + self.animation_timer = 0 + mcl_burning.update_hud(parent) + end end - - return true end, }) From 86a4ece7d2fb67e77f414b5016131370d0e55d5c Mon Sep 17 00:00:00 2001 From: kabou Date: Wed, 2 Mar 2022 15:25:03 +0100 Subject: [PATCH 2/6] Add local copy of minetest.find_nodes_in_area. --- mods/ENTITIES/mcl_burning/api.lua | 4 +++- mods/ENTITIES/mcl_burning/init.lua | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/mods/ENTITIES/mcl_burning/api.lua b/mods/ENTITIES/mcl_burning/api.lua index 5890d8892c..8cdeb8b942 100644 --- a/mods/ENTITIES/mcl_burning/api.lua +++ b/mods/ENTITIES/mcl_burning/api.lua @@ -26,10 +26,12 @@ function mcl_burning.get_collisionbox(obj, smaller, storage) end end +local find_nodes_in_area = minetest.find_nodes_in_area + function mcl_burning.get_touching_nodes(obj, nodenames, storage) local pos = obj:get_pos() local minp, maxp = mcl_burning.get_collisionbox(obj, true, storage) - local nodes = minetest.find_nodes_in_area(vector.add(pos, minp), vector.add(pos, maxp), nodenames) + local nodes = find_nodes_in_area(vector.add(pos, minp), vector.add(pos, maxp), nodenames) return nodes end diff --git a/mods/ENTITIES/mcl_burning/init.lua b/mods/ENTITIES/mcl_burning/init.lua index 82410dfdd3..3f3d08af15 100644 --- a/mods/ENTITIES/mcl_burning/init.lua +++ b/mods/ENTITIES/mcl_burning/init.lua @@ -1,15 +1,15 @@ local modpath = minetest.get_modpath(minetest.get_current_modname()) -local pairs = pairs -local get_connected_players = minetest.get_connected_players -local get_item_group = minetest.get_item_group - mcl_burning = { storage = {} } dofile(modpath .. "/api.lua") +local pairs = pairs +local get_connected_players = minetest.get_connected_players +local get_item_group = minetest.get_item_group + minetest.register_globalstep(function(dtime) for _, player in pairs(get_connected_players()) do local storage = mcl_burning.storage[player] From b17776699e1edd8a12483d29a3e5535735b3dc57 Mon Sep 17 00:00:00 2001 From: kabou Date: Wed, 2 Mar 2022 15:40:29 +0100 Subject: [PATCH 3/6] Add comments to "storage" table. * Document attributes of the "storage" table. --- mods/ENTITIES/mcl_burning/init.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mods/ENTITIES/mcl_burning/init.lua b/mods/ENTITIES/mcl_burning/init.lua index 3f3d08af15..3496d7af90 100644 --- a/mods/ENTITIES/mcl_burning/init.lua +++ b/mods/ENTITIES/mcl_burning/init.lua @@ -1,6 +1,18 @@ local modpath = minetest.get_modpath(minetest.get_current_modname()) mcl_burning = { + -- the storage table holds a list of objects (players,luaentities) and tables + -- associated with these objects. These tables have the following attributes: + -- burn_time: + -- Remaining time that object will burn. + -- fire_damage_timer: + -- Timer for dealing damage every second while burning. + -- fire_hud_id: + -- HUD id of the flames animation on a burning player's HUD. + -- animation_frame: + -- The HUD's current animation frame, used by update_hud(). + -- collisionbox_cache: + -- Used by mcl_burning.get_collisionbox() to avoid recalculations. storage = {} } From e80006f4eaa4cdf7e5e65b47bfdba5c7c64c7c11 Mon Sep 17 00:00:00 2001 From: kabou Date: Wed, 2 Mar 2022 16:10:55 +0100 Subject: [PATCH 4/6] Do not set fire to objects that are already burning. Changes to mcl_burning.set_on_fire(): * Add logic that only updates the burn time, but skips adding fire entities to objects that are already burning. * Condense code a little, remove single use variables. * Add a comment to a questionable piece of code. * Add comments to the function. --- mods/ENTITIES/mcl_burning/api.lua | 67 ++++++++++++++++++------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/mods/ENTITIES/mcl_burning/api.lua b/mods/ENTITIES/mcl_burning/api.lua index 8cdeb8b942..8cfdded7ef 100644 --- a/mods/ENTITIES/mcl_burning/api.lua +++ b/mods/ENTITIES/mcl_burning/api.lua @@ -58,13 +58,24 @@ function mcl_burning.update_hud(player) end end +-- Sets and object state as burning and adds a fire animation to the object. +-- +-- Parameters: +-- obj - may be a player or a lua_entity; +-- burn_time - sets the object's burn duration; +-- +-- If obj is a player, adds a fire animation to the HUD, if obj is a +-- lua_entity, adds an animated fire entity to obj. +-- The effective burn duration is modified by obj's armor protection. +-- If obj was already burning, its burn duration is updated if the current +-- duration is less than burn_time. +-- If obj is dead, fireproof or a creative player, this function does nothing. +-- function mcl_burning.set_on_fire(obj, burn_time) if obj:get_hp() < 0 then return end - local storage = mcl_burning.get_storage(obj) - local luaentity = obj:get_luaentity() if luaentity and luaentity.fire_resistant then return @@ -85,42 +96,42 @@ function mcl_burning.set_on_fire(obj, burn_time) end end end - if max_fire_prot_lvl > 0 then burn_time = burn_time - math.floor(burn_time * max_fire_prot_lvl * 0.15) end end - if not storage.burn_time or burn_time >= storage.burn_time then - if obj:is_player() then - mcl_burning.update_hud(obj) + local storage = mcl_burning.get_storage(obj) + if storage.burn_time then + if burn_time > storage.burn_time then + storage.burn_time = burn_time end - storage.burn_time = burn_time - storage.fire_damage_timer = 0 + return + end + storage.burn_time = burn_time + storage.fire_damage_timer = 0 - local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire") - local minp, maxp = mcl_burning.get_collisionbox(obj, false, storage) - local obj_size = obj:get_properties().visual_size + local minp, maxp = mcl_burning.get_collisionbox(obj, false, storage) + local size = vector.subtract(maxp, minp) + size = vector.multiply(size, vector.new(1.1, 1.2, 1.1)) + size = vector.divide(size, obj:get_properties().visual_size) - local vertical_grow_factor = 1.2 - local horizontal_grow_factor = 1.1 - local grow_vector = vector.new(horizontal_grow_factor, vertical_grow_factor, horizontal_grow_factor) + local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire") + fire_entity:set_properties({visual_size = size}) + fire_entity:set_attach(obj, "", vector.new(0, size.y * 5, 0), vector.new(0, 0, 0)) - local size = vector.subtract(maxp, minp) - size = vector.multiply(size, grow_vector) - size = vector.divide(size, obj_size) - local offset = vector.new(0, size.y * 10 / 2, 0) + if obj:is_player() then + mcl_burning.update_hud(obj) + end - fire_entity:set_properties({visual_size = size}) - fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0}) - local fire_luaentity = fire_entity:get_luaentity() - - for _, other in pairs(minetest.get_objects_inside_radius(fire_entity:get_pos(), 0)) do - local other_luaentity = other:get_luaentity() - if other_luaentity and other_luaentity.name == "mcl_burning:fire" and other_luaentity ~= fire_luaentity then - other:remove() - break - end + -- FIXME: does this code make sense? It removes attached fire luaentities from + -- another object that happen to be at the same position. + local fire_luaentity = fire_entity:get_luaentity() + for _, other in pairs(minetest.get_objects_inside_radius(fire_entity:get_pos(), 0)) do + local other_luaentity = other:get_luaentity() + if other_luaentity and other_luaentity.name == "mcl_burning:fire" and other_luaentity ~= fire_luaentity then + other:remove() + break end end end From d424d4f10e8b37bf4181cfb3c18c09c95994bae0 Mon Sep 17 00:00:00 2001 From: kabou Date: Wed, 2 Mar 2022 16:39:27 +0100 Subject: [PATCH 5/6] Add comments to mcl_burning.update_hud(). --- mods/ENTITIES/mcl_burning/api.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mods/ENTITIES/mcl_burning/api.lua b/mods/ENTITIES/mcl_burning/api.lua index 8cfdded7ef..8093d45f98 100644 --- a/mods/ENTITIES/mcl_burning/api.lua +++ b/mods/ENTITIES/mcl_burning/api.lua @@ -35,6 +35,14 @@ function mcl_burning.get_touching_nodes(obj, nodenames, storage) return nodes end +-- Manages the fire animation on a burning player's HUD +-- +-- Parameters: +-- player - a valid player object; +-- +-- If the player already has a fire HUD, updates the burning animation. +-- If the fire does not have a fire HUD, initializes the HUD. +-- function mcl_burning.update_hud(player) local animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8 local hud_flame_animated = "mcl_burning_hud_flame_animated.png^[opacity:180^[verticalframe:" .. animation_frames .. ":" From c3e09969024c4113b9ac772b46c206bd951f547d Mon Sep 17 00:00:00 2001 From: kabou Date: Wed, 2 Mar 2022 17:15:05 +0100 Subject: [PATCH 6/6] Fix fire HUD not showing on join while burning. * Add the fire HUD to a player who joins the server while still burning from the previous session. --- mods/ENTITIES/mcl_burning/init.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mods/ENTITIES/mcl_burning/init.lua b/mods/ENTITIES/mcl_burning/init.lua index 3496d7af90..62bf3f69a9 100644 --- a/mods/ENTITIES/mcl_burning/init.lua +++ b/mods/ENTITIES/mcl_burning/init.lua @@ -60,6 +60,9 @@ minetest.register_on_joinplayer(function(player) storage = minetest.deserialize(burn_data) end mcl_burning.storage[player] = storage + if storage.burn_time and storage.burn_time > 0 then + mcl_burning.update_hud(player) + end end) local function on_leaveplayer(player)