Fix breath_bar scaling; delay breath_bar hiding by one second (#8271)
[oweals/minetest.git] / builtin / game / statbars.lua
1 -- cache setting
2 local enable_damage = core.settings:get_bool("enable_damage")
3
4 local health_bar_definition = {
5         hud_elem_type = "statbar",
6         position = {x = 0.5, y = 1},
7         text = "heart.png",
8         number = core.PLAYER_MAX_HP_DEFAULT,
9         direction = 0,
10         size = {x = 24, y = 24},
11         offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)},
12 }
13
14 local breath_bar_definition = {
15         hud_elem_type = "statbar",
16         position = {x = 0.5, y = 1},
17         text = "bubble.png",
18         number = core.PLAYER_MAX_BREATH_DEFAULT,
19         direction = 0,
20         size = {x = 24, y = 24},
21         offset = {x = 25, y= -(48 + 24 + 16)},
22 }
23
24 local hud_ids = {}
25
26 local function scaleToDefault(player, field)
27         -- Scale "hp" or "breath" to the default dimensions
28         local current = player["get_" .. field](player)
29         local nominal = core["PLAYER_MAX_" .. field:upper() .. "_DEFAULT"]
30         local max_display = math.max(nominal,
31                 math.max(player:get_properties()[field .. "_max"], current))
32         return current / max_display * nominal
33 end
34
35 local function update_builtin_statbars(player)
36         local name = player:get_player_name()
37
38         if name == "" then
39                 return
40         end
41
42         local flags = player:hud_get_flags()
43         if not hud_ids[name] then
44                 hud_ids[name] = {}
45                 -- flags are not transmitted to client on connect, we need to make sure
46                 -- our current flags are transmitted by sending them actively
47                 player:hud_set_flags(flags)
48         end
49         local hud = hud_ids[name]
50
51         local immortal = player:get_armor_groups().immortal == 1
52
53         if flags.healthbar and enable_damage and not immortal then
54                 local number = scaleToDefault(player, "hp")
55                 if hud.id_healthbar == nil then
56                         local hud_def = table.copy(health_bar_definition)
57                         hud_def.number = number
58                         hud.id_healthbar = player:hud_add(hud_def)
59                 else
60                         player:hud_change(hud.id_healthbar, "number", number)
61                 end
62         elseif hud.id_healthbar then
63                 player:hud_remove(hud.id_healthbar)
64                 hud.id_healthbar = nil
65         end
66
67         local show_breathbar = flags.breathbar and enable_damage and not immortal
68
69         local breath     = player:get_breath()
70         local breath_max = player:get_properties().breath_max
71         if show_breathbar and breath <= breath_max then
72                 local number = 2 * scaleToDefault(player, "breath")
73                 if not hud.id_breathbar and breath < breath_max then
74                         local hud_def = table.copy(breath_bar_definition)
75                         hud_def.number = number
76                         hud.id_breathbar = player:hud_add(hud_def)
77                 elseif hud.id_breathbar then
78                         player:hud_change(hud.id_breathbar, "number", number)
79                 end
80         end
81
82         if hud.id_breathbar and (not show_breathbar or breath == breath_max) then
83                 minetest.after(1, function(player_name, breath_bar)
84                         local player = minetest.get_player_by_name(player_name)
85                         if player then
86                                 player:hud_remove(breath_bar)
87                         end
88                 end, name, hud.id_breathbar)
89                 hud.id_breathbar = nil
90         end
91 end
92
93 local function cleanup_builtin_statbars(player)
94         local name = player:get_player_name()
95
96         if name == "" then
97                 return
98         end
99
100         hud_ids[name] = nil
101 end
102
103 local function player_event_handler(player,eventname)
104         assert(player:is_player())
105
106         local name = player:get_player_name()
107
108         if name == "" or not hud_ids[name] then
109                 return
110         end
111
112         if eventname == "health_changed" then
113                 update_builtin_statbars(player)
114
115                 if hud_ids[name].id_healthbar then
116                         return true
117                 end
118         end
119
120         if eventname == "breath_changed" then
121                 update_builtin_statbars(player)
122
123                 if hud_ids[name].id_breathbar then
124                         return true
125                 end
126         end
127
128         if eventname == "hud_changed" or eventname == "properties_changed" then
129                 update_builtin_statbars(player)
130                 return true
131         end
132
133         return false
134 end
135
136 function core.hud_replace_builtin(hud_name, definition)
137
138         if type(definition) ~= "table" or
139                         definition.hud_elem_type ~= "statbar" then
140                 return false
141         end
142
143         if hud_name == "health" then
144                 health_bar_definition = definition
145
146                 for name, ids in pairs(hud_ids) do
147                         local player = core.get_player_by_name(name)
148                         if player and ids.id_healthbar then
149                                 player:hud_remove(ids.id_healthbar)
150                                 ids.id_healthbar = nil
151                                 update_builtin_statbars(player)
152                         end
153                 end
154                 return true
155         end
156
157         if hud_name == "breath" then
158                 breath_bar_definition = definition
159
160                 for name, ids in pairs(hud_ids) do
161                         local player = core.get_player_by_name(name)
162                         if player and ids.id_breathbar then
163                                 player:hud_remove(ids.id_breathbar)
164                                 ids.id_breathbar = nil
165                                 update_builtin_statbars(player)
166                         end
167                 end
168                 return true
169         end
170
171         return false
172 end
173
174 -- Append "update_builtin_statbars" as late as possible
175 -- This ensures that the HUD is hidden when the flags are updated in this callback
176 core.register_on_mods_loaded(function()
177         core.register_on_joinplayer(update_builtin_statbars)
178 end)
179 core.register_on_leaveplayer(cleanup_builtin_statbars)
180 core.register_playerevent(player_event_handler)