1 local S
= minetest
.get_translator("hudbars")
2 local N
= function(s
) return s
end
8 -- number of registered HUD bars
11 -- table which records which HUD bar slots have been “registered” so far; used for automatic positioning
12 hb
.registered_slots
= {}
16 function hb
.load_setting(sname
, stype
, defaultval
, valid_values
)
18 if stype
== "string" then
19 sval
= minetest
.settings
:get(sname
)
20 elseif stype
== "bool" then
21 sval
= minetest
.settings
:get_bool(sname
)
22 elseif stype
== "number" then
23 sval
= tonumber(minetest
.settings
:get(sname
))
26 if valid_values
~= nil then
28 for i
=1,#valid_values
do
29 if sval
== valid_values
[i
] then
34 minetest
.log("error", "[hudbars] Invalid value for "..sname
.."! Using default value ("..tostring(defaultval
)..").")
47 -- Load default settings
48 dofile(minetest
.get_modpath("hudbars").."/default_settings.lua")
50 local function player_exists(player
)
51 return player
~= nil and player
:is_player()
54 local function make_label(format_string
, format_string_config
, label
, start_value
, max_value
)
56 local order
= format_string_config
.order
58 if order
[o
] == "label" then
59 table.insert(params
, label
)
60 elseif order
[o
] == "value" then
61 if format_string_config
.format_value
then
62 table.insert(params
, string.format(format_string_config
.format_value
, start_value
))
64 table.insert(params
, start_value
)
66 elseif order
[o
] == "max_value" then
67 if format_string_config
.format_max_value
then
68 table.insert(params
, string.format(format_string_config
.format_max_value
, max_value
))
70 table.insert(params
, max_value
)
75 if format_string_config
.textdomain
then
76 ret
= minetest
.translate(format_string_config
.textdomain
, format_string
, unpack(params
))
78 ret
= S(format_string
, unpack(params
))
83 -- Table which contains all players with active default HUD bars (only for internal use)
86 function hb
.value_to_barlength(value
, max)
90 if hb
.settings
.bar_type
== "progress_bar" then
92 if value
< 0 then x
=-0.5 else x
= 0.5 end
93 local ret
= math
.modf((value
/max) * hb
.settings
.max_bar_length
+ x
)
97 if value
< 0 then x
=-0.5 else x
= 0.5 end
98 local ret
= math
.modf((value
/max) * hb
.settings
.statbar_length
+ x
)
104 function hb
.get_hudtable(identifier
)
105 return hb
.hudtables
[identifier
]
108 function hb
.get_hudbar_position_index(identifier
)
109 if hb
.settings
.sorting
[identifier
] ~= nil then
110 return hb
.settings
.sorting
[identifier
]
114 if hb
.registered_slots
[i
] ~= true and hb
.settings
.sorting_reverse
[i
] == nil then
122 function hb
.register_hudbar(identifier
, text_color
, label
, textures
, default_start_value
, default_start_max
, default_start_hidden
, format_string
, format_string_config
)
123 minetest
.log("action", "hb.register_hudbar: "..tostring(identifier
))
126 local index
= math
.floor(hb
.get_hudbar_position_index(identifier
))
127 hb
.registered_slots
[index
] = true
128 if hb
.settings
.alignment_pattern
== "stack_up" then
129 pos
= hb
.settings
.pos_left
131 x
= hb
.settings
.start_offset_left
.x
,
132 y
= hb
.settings
.start_offset_left
.y
- hb
.settings
.vmargin
* index
134 elseif hb
.settings
.alignment_pattern
== "stack_down" then
135 pos
= hb
.settings
.pos_left
137 x
= hb
.settings
.start_offset_left
.x
,
138 y
= hb
.settings
.start_offset_left
.y
+ hb
.settings
.vmargin
* index
141 if index
% 2 == 0 then
142 pos
= hb
.settings
.pos_left
144 x
= hb
.settings
.start_offset_left
.x
,
145 y
= hb
.settings
.start_offset_left
.y
- hb
.settings
.vmargin
* (index
/2)
148 pos
= hb
.settings
.pos_right
150 x
= hb
.settings
.start_offset_right
.x
,
151 y
= hb
.settings
.start_offset_right
.y
- hb
.settings
.vmargin
* ((index
-1)/2)
155 if format_string
== nil then
156 format_string
= N("@1: @2/@3")
158 if format_string_config
== nil then
159 format_string_config
= {}
161 if format_string_config
.order
== nil then
162 format_string_config
.order
= { "label", "value", "max_value" }
164 if format_string_config
.format_value
== nil then
165 format_string_config
.format_value
= "%d"
167 if format_string_config
.format_max_value
== nil then
168 format_string_config
.format_max_value
= "%d"
171 hudtable
.add_all
= function(player
, hudtable
, start_value
, start_max
, start_hidden
)
172 if start_value
== nil then start_value
= hudtable
.default_start_value
end
173 if start_max
== nil then start_max
= hudtable
.default_start_max
end
174 if start_hidden
== nil then start_hidden
= hudtable
.default_start_hidden
end
177 local name
= player
:get_player_name()
178 local bgscale
, iconscale
, text
, barnumber
, bgiconnumber
179 if start_max
== 0 or start_hidden
then
180 bgscale
= { x
=0, y
=0 }
182 bgscale
= { x
=1, y
=1 }
185 iconscale
= { x
=0, y
=0 }
190 iconscale
= { x
=1, y
=1 }
191 barnumber
= hb
.value_to_barlength(start_value
, start_max
)
192 bgiconnumber
= hb
.settings
.statbar_length
193 text
= make_label(format_string
, format_string_config
, label
, start_value
, start_max
)
195 if hb
.settings
.bar_type
== "progress_bar" then
196 ids
.bg
= player
:hud_add({
197 hud_elem_type
= "image",
200 text
= "hudbars_bar_background.png",
201 alignment
= {x
=1,y
=1},
202 offset
= { x
= offset
.x
- 1, y
= offset
.y
- 1 },
204 if textures
.icon
~= nil then
205 ids
.icon
= player
:hud_add({
206 hud_elem_type
= "image",
209 text
= textures
.icon
,
210 alignment
= {x
=-1,y
=1},
211 offset
= { x
= offset
.x
- 3, y
= offset
.y
},
214 elseif hb
.settings
.bar_type
== "statbar_modern" then
215 if textures
.bgicon
~= nil then
216 ids
.bg
= player
:hud_add({
217 hud_elem_type
= "statbar",
219 text
= textures
.bgicon
,
220 number = bgiconnumber
,
221 alignment
= {x
=-1,y
=-1},
222 offset
= { x
= offset
.x
, y
= offset
.y
},
228 local bar_image
, bar_size
229 if hb
.settings
.bar_type
== "progress_bar" then
230 bar_image
= textures
.bar
231 -- NOTE: Intentionally set to nil. For some reason, on some systems,
232 -- the progress bar is displaced when the bar_size is set explicitly here.
233 -- On the other hand, setting this to nil is deprecated in MT 5.0.0 due to
234 -- a debug log warning, but nothing is explained in lua_api.txt.
235 -- This section is a potential bug magnet, please watch with care!
236 -- The size of the bar image is expected to be exactly 2×16 pixels.
238 elseif hb
.settings
.bar_type
== "statbar_classic" or hb
.settings
.bar_type
== "statbar_modern" then
239 bar_image
= textures
.icon
240 bar_size
= {x
=24, y
=24}
242 ids
.bar
= player
:hud_add({
243 hud_elem_type
= "statbar",
247 alignment
= {x
=-1,y
=-1},
252 if hb
.settings
.bar_type
== "progress_bar" then
253 ids
.text
= player
:hud_add({
254 hud_elem_type
= "text",
257 alignment
= {x
=1,y
=1},
260 offset
= { x
= offset
.x
+ 2, y
= offset
.y
- 1},
263 -- Do not forget to update hb.get_hudbar_state if you add new fields to the state table
264 state
.hidden
= start_hidden
265 state
.value
= start_value
266 state
.max = start_max
268 state
.barlength
= hb
.value_to_barlength(start_value
, start_max
)
270 local main_error_text
=
271 "[hudbars] Bad initial values of HUD bar identifier “"..tostring(identifier
).."” for player "..name
..". "
273 if start_max
< start_value
then
274 minetest
.log("error", main_error_text
.."start_max ("..start_max
..") is smaller than start_value ("..start_value
..")!")
276 if start_max
< 0 then
277 minetest
.log("error", main_error_text
.."start_max ("..start_max
..") is smaller than 0!")
279 if start_value
< 0 then
280 minetest
.log("error", main_error_text
.."start_value ("..start_value
..") is smaller than 0!")
283 hb
.hudtables
[identifier
].hudids
[name
] = ids
284 hb
.hudtables
[identifier
].hudstate
[name
] = state
287 hudtable
.identifier
= identifier
288 hudtable
.format_string
= format_string
289 hudtable
.format_string_config
= format_string_config
290 hudtable
.label
= label
292 hudtable
.hudstate
= {}
293 hudtable
.default_start_hidden
= default_start_hidden
294 hudtable
.default_start_value
= default_start_value
295 hudtable
.default_start_max
= default_start_max
297 hb
.hudbars_count
= hb
.hudbars_count
+ 1
299 hb
.hudtables
[identifier
] = hudtable
302 function hb
.init_hudbar(player
, identifier
, start_value
, start_max
, start_hidden
)
303 if not player_exists(player
) then return false end
304 local hudtable
= hb
.get_hudtable(identifier
)
305 hb
.hudtables
[identifier
].add_all(player
, hudtable
, start_value
, start_max
, start_hidden
)
309 function hb
.change_hudbar(player
, identifier
, new_value
, new_max_value
, new_icon
, new_bgicon
, new_bar
, new_label
, new_text_color
)
310 if new_value
== nil and new_max_value
== nil and new_icon
== nil and new_bgicon
== nil and new_bar
== nil and new_label
== nil and new_text_color
== nil then
313 if not player_exists(player
) then
317 local name
= player
:get_player_name()
318 local hudtable
= hb
.get_hudtable(identifier
)
319 if not hudtable
.hudstate
[name
] then
322 local value_changed
, max_changed
= false, false
324 if new_value
~= nil then
325 if new_value
~= hudtable
.hudstate
[name
].value
then
326 hudtable
.hudstate
[name
].value
= new_value
330 new_value
= hudtable
.hudstate
[name
].value
332 if new_max_value
~= nil then
333 if new_max_value
~= hudtable
.hudstate
[name
].max then
334 hudtable
.hudstate
[name
].max = new_max_value
338 new_max_value
= hudtable
.hudstate
[name
].max
341 if hb
.settings
.bar_type
== "progress_bar" then
342 if new_icon
~= nil and hudtable
.hudids
[name
].icon
~= nil then
343 player
:hud_change(hudtable
.hudids
[name
].icon
, "text", new_icon
)
345 if new_bgicon
~= nil and hudtable
.hudids
[name
].bgicon
~= nil then
346 player
:hud_change(hudtable
.hudids
[name
].bgicon
, "text", new_bgicon
)
348 if new_bar
~= nil then
349 player
:hud_change(hudtable
.hudids
[name
].bar
, "text", new_bar
)
351 if new_label
~= nil then
352 hudtable
.label
= new_label
353 local new_text
= make_label(hudtable
.format_string
, hudtable
.format_string_config
, new_label
, hudtable
.hudstate
[name
].value
, hudtable
.hudstate
[name
].max)
354 player
:hud_change(hudtable
.hudids
[name
].text
, "text", new_text
)
356 if new_text_color
~= nil then
357 player
:hud_change(hudtable
.hudids
[name
].text
, "number", new_text_color
)
360 if new_icon
~= nil and hudtable
.hudids
[name
].bar
~= nil then
361 player
:hud_change(hudtable
.hudids
[name
].bar
, "text", new_icon
)
363 if new_bgicon
~= nil and hudtable
.hudids
[name
].bg
~= nil then
364 player
:hud_change(hudtable
.hudids
[name
].bg
, "text", new_bgicon
)
368 local main_error_text
=
369 "[hudbars] Bad call to hb.change_hudbar, identifier: “"..tostring(identifier
).."”, player name: “"..name
.."”. "
370 if new_max_value
< new_value
then
371 minetest
.log("error", main_error_text
.."new_max_value ("..new_max_value
..") is smaller than new_value ("..new_value
..")!")
373 if new_max_value
< 0 then
374 minetest
.log("error", main_error_text
.."new_max_value ("..new_max_value
..") is smaller than 0!")
376 if new_value
< 0 then
377 minetest
.log("error", main_error_text
.."new_value ("..new_value
..") is smaller than 0!")
380 if hudtable
.hudstate
[name
].hidden
== false then
381 if max_changed
and hb
.settings
.bar_type
== "progress_bar" then
382 if hudtable
.hudstate
[name
].max == 0 then
383 player
:hud_change(hudtable
.hudids
[name
].bg
, "scale", {x
=0,y
=0})
385 player
:hud_change(hudtable
.hudids
[name
].bg
, "scale", {x
=1,y
=1})
389 if value_changed
or max_changed
then
390 local new_barlength
= hb
.value_to_barlength(new_value
, new_max_value
)
391 if new_barlength
~= hudtable
.hudstate
[name
].barlength
then
392 player
:hud_change(hudtable
.hudids
[name
].bar
, "number", hb
.value_to_barlength(new_value
, new_max_value
))
393 hudtable
.hudstate
[name
].barlength
= new_barlength
396 if hb
.settings
.bar_type
== "progress_bar" then
397 local new_text
= make_label(hudtable
.format_string
, hudtable
.format_string_config
, hudtable
.label
, new_value
, new_max_value
)
398 if new_text
~= hudtable
.hudstate
[name
].text
then
399 player
:hud_change(hudtable
.hudids
[name
].text
, "text", new_text
)
400 hudtable
.hudstate
[name
].text
= new_text
408 function hb
.hide_hudbar(player
, identifier
)
409 if not player_exists(player
) then return false end
410 local name
= player
:get_player_name()
411 local hudtable
= hb
.get_hudtable(identifier
)
412 if hudtable
== nil then return false end
413 if hb
.settings
.bar_type
== "progress_bar" then
414 if hudtable
.hudids
[name
].icon
~= nil then
415 player
:hud_change(hudtable
.hudids
[name
].icon
, "scale", {x
=0,y
=0})
417 player
:hud_change(hudtable
.hudids
[name
].bg
, "scale", {x
=0,y
=0})
418 player
:hud_change(hudtable
.hudids
[name
].text
, "text", "")
419 elseif hb
.settings
.bar_type
== "statbar_modern" then
420 player
:hud_change(hudtable
.hudids
[name
].bg
, "number", 0)
422 player
:hud_change(hudtable
.hudids
[name
].bar
, "number", 0)
423 hudtable
.hudstate
[name
].hidden
= true
427 function hb
.unhide_hudbar(player
, identifier
)
428 if not player_exists(player
) then return false end
429 local name
= player
:get_player_name()
430 local hudtable
= hb
.get_hudtable(identifier
)
431 if hudtable
== nil then return false end
432 local value
= hudtable
.hudstate
[name
].value
433 local max = hudtable
.hudstate
[name
].max
434 if hb
.settings
.bar_type
== "progress_bar" then
435 if hudtable
.hudids
[name
].icon
~= nil then
436 player
:hud_change(hudtable
.hudids
[name
].icon
, "scale", {x
=1,y
=1})
438 if hudtable
.hudstate
[name
].max ~= 0 then
439 player
:hud_change(hudtable
.hudids
[name
].bg
, "scale", {x
=1,y
=1})
441 player
:hud_change(hudtable
.hudids
[name
].text
, "text", make_label(hudtable
.format_string
, hudtable
.format_string_config
, hudtable
.label
, value
, max))
442 elseif hb
.settings
.bar_type
== "statbar_modern" then
443 player
:hud_change(hudtable
.hudids
[name
].bg
, "number", hb
.settings
.statbar_length
)
445 player
:hud_change(hudtable
.hudids
[name
].bar
, "number", hb
.value_to_barlength(value
, max))
446 hudtable
.hudstate
[name
].hidden
= false
450 function hb
.get_hudbar_state(player
, identifier
)
451 if not player_exists(player
) then return nil end
452 local ref
= hb
.get_hudtable(identifier
).hudstate
[player
:get_player_name()]
453 -- Do not forget to update this chunk of code in case the state changes
459 barlength
= ref
.barlength
,
464 function hb
.get_hudbar_identifiers()
466 for id
, _
in pairs(hb
.hudtables
) do
467 table.insert(ids
, id
)
472 --register built-in HUD bars
473 if minetest
.settings
:get_bool("enable_damage") or hb
.settings
.forceload_default_hudbars
then
474 hb
.register_hudbar("health", 0xFFFFFF, S("Health"), { bar
= "hudbars_bar_health.png", icon
= "hudbars_icon_health.png", bgicon
= "hudbars_bgicon_health.png" }, 20, 20, false)
475 hb
.register_hudbar("breath", 0xFFFFFF, S("Breath"), { bar
= "hudbars_bar_breath.png", icon
= "hudbars_icon_breath.png", bgicon
= "hudbars_bgicon_breath.png" }, 10, 10, true)
478 local function hide_builtin(player
)
479 local flags
= player
:hud_get_flags()
480 flags
.healthbar
= false
481 flags
.breathbar
= false
482 player
:hud_set_flags(flags
)
486 local function custom_hud(player
)
487 if minetest
.settings
:get_bool("enable_damage") or hb
.settings
.forceload_default_hudbars
then
489 if minetest
.settings
:get_bool("enable_damage") then
494 local hp
= player
:get_hp()
495 local hp_max
= player
:get_properties().hp_max
496 hb
.init_hudbar(player
, "health", math
.min(hp
, hp_max
), hp_max
, hide
)
497 local breath
= player
:get_breath()
498 local breath_max
= player
:get_properties().breath_max
500 if breath
>= breath_max
and hb
.settings
.autohide_breath
== true then hide_breath
= true else hide_breath
= false end
501 hb
.init_hudbar(player
, "breath", math
.min(breath
, breath_max
-1), breath_max
-1, hide_breath
or hide
)
505 local function update_health(player
)
506 local hp_max
= player
:get_properties().hp_max
507 hb
.change_hudbar(player
, "health", player
:get_hp(), hp_max
)
510 -- update built-in HUD bars
511 local function update_hud(player
)
512 if not player_exists(player
) then return end
513 if minetest
.settings
:get_bool("enable_damage") then
514 if hb
.settings
.forceload_default_hudbars
then
515 hb
.unhide_hudbar(player
, "health")
518 local breath_max
= player
:get_properties().breath_max
519 local breath
= player
:get_breath()
521 if breath
>= breath_max
and hb
.settings
.autohide_breath
== true then
522 hb
.hide_hudbar(player
, "breath")
524 hb
.unhide_hudbar(player
, "breath")
525 hb
.change_hudbar(player
, "breath", math
.min(breath
, breath_max
-1), breath_max
-1)
528 update_health(player
)
529 elseif hb
.settings
.forceload_default_hudbars
then
530 hb
.hide_hudbar(player
, "health")
531 hb
.hide_hudbar(player
, "breath")
535 minetest
.register_on_player_hpchange(function(player
)
536 if hb
.players
[player
:get_player_name()] ~= nil then
537 update_health(player
)
541 minetest
.register_on_respawnplayer(function(player
)
542 update_health(player
)
543 hb
.hide_hudbar(player
, "breath")
546 minetest
.register_on_joinplayer(function(player
)
549 hb
.players
[player
:get_player_name()] = player
552 minetest
.register_on_leaveplayer(function(player
)
553 hb
.players
[player
:get_player_name()] = nil
558 minetest
.register_globalstep(function(dtime
)
559 main_timer
= main_timer
+ dtime
560 timer
= timer
+ dtime
561 if main_timer
> hb
.settings
.tick
or timer
> 4 then
562 if main_timer
> hb
.settings
.tick
then main_timer
= 0 end
563 -- only proceed if damage is enabled
564 if minetest
.settings
:get_bool("enable_damage") or hb
.settings
.forceload_default_hudbars
then
565 for _
, player
in pairs(hb
.players
) do
566 -- update all hud elements
571 if timer
> 4 then timer
= 0 end