1.4.1: Fix bug in hb.change_hudbar being a no-op
[minetest_hudbars.git] / init.lua
blob300778a121eb1edb2d419a90a700a29e2bf70940
1 hb = {}
3 hb.hudtables = {}
5 -- number of registered HUD bars
6 hb.hudbars_count = 0
8 -- table which records which HUD bar slots have been “registered” so far; used for automatic positioning
9 hb.registered_slots = {}
11 hb.settings = {}
13 function hb.load_setting(sname, stype, defaultval, valid_values)
14 local sval
15 if stype == "string" then
16 sval = minetest.setting_get(sname)
17 elseif stype == "bool" then
18 sval = minetest.setting_getbool(sname)
19 elseif stype == "number" then
20 sval = tonumber(minetest.setting_get(sname))
21 end
22 if sval ~= nil then
23 if valid_values ~= nil then
24 local valid = false
25 for i=1,#valid_values do
26 if sval == valid_values[i] then
27 valid = true
28 end
29 end
30 if not valid then
31 minetest.log("error", "[hudbars] Invalid value for "..sname.."! Using default value ("..tostring(defaultval)..").")
32 return defaultval
33 else
34 return sval
35 end
36 else
37 return sval
38 end
39 else
40 return defaultval
41 end
42 end
44 -- (hardcoded) default settings
45 hb.settings.max_bar_length = 160
46 hb.settings.statbar_length = 20
48 -- statbar positions
49 hb.settings.pos_left = {}
50 hb.settings.pos_right = {}
51 hb.settings.start_offset_left = {}
52 hb.settings.start_offset_right= {}
53 hb.settings.pos_left.x = hb.load_setting("hudbars_pos_left_x", "number", 0.5)
54 hb.settings.pos_left.y = hb.load_setting("hudbars_pos_left_y", "number", 1)
55 hb.settings.pos_right.x = hb.load_setting("hudbars_pos_right_x", "number", 0.5)
56 hb.settings.pos_right.y = hb.load_setting("hudbars_pos_right_y", "number", 1)
57 hb.settings.start_offset_left.x = hb.load_setting("hudbars_start_offset_left_x", "number", -175)
58 hb.settings.start_offset_left.y = hb.load_setting("hudbars_start_offset_left_y", "number", -86)
59 hb.settings.start_offset_right.x = hb.load_setting("hudbars_start_offset_right_x", "number", 15)
60 hb.settings.start_offset_right.y = hb.load_setting("hudbars_start_offset_right_y", "number", -86)
62 hb.settings.vmargin = hb.load_setting("hudbars_vmargin", "number", 24)
63 hb.settings.tick = hb.load_setting("hudbars_tick", "number", 0.1)
65 -- experimental setting: Changing this setting is not officially supported, do NOT rely on it!
66 hb.settings.forceload_default_hudbars = hb.load_setting("hudbars_forceload_default_hudbars", "bool", true)
68 -- Misc. settings
69 hb.settings.alignment_pattern = hb.load_setting("hudbars_alignment_pattern", "string", "zigzag", {"zigzag", "stack_up", "stack_down"})
70 hb.settings.bar_type = hb.load_setting("hudbars_bar_type", "string", "progress_bar", {"progress_bar", "statbar_classic", "statbar_modern"})
71 hb.settings.autohide_breath = hb.load_setting("hudbars_autohide_breath", "bool", true)
73 local sorting = minetest.setting_get("hudbars_sorting")
74 if sorting ~= nil then
75 hb.settings.sorting = {}
76 hb.settings.sorting_reverse = {}
77 for k,v in string.gmatch(sorting, "(%w+)=(%w+)") do
78 hb.settings.sorting[k] = tonumber(v)
79 hb.settings.sorting_reverse[tonumber(v)] = k
80 end
81 else
82 hb.settings.sorting = { ["health"] = 0, ["breath"] = 1 }
83 hb.settings.sorting_reverse = { [0] = "health", [1] = "breath" }
84 end
86 -- Table which contains all players with active default HUD bars (only for internal use)
87 hb.players = {}
89 function hb.value_to_barlength(value, max)
90 if max == 0 then
91 return 0
92 else
93 if hb.settings.bar_type == "progress_bar" then
94 local x
95 if value < 0 then x=-0.5 else x = 0.5 end
96 local ret = math.modf((value/max) * hb.settings.max_bar_length + x)
97 return ret
98 else
99 local x
100 if value < 0 then x=-0.5 else x = 0.5 end
101 local ret = math.modf((value/max) * hb.settings.statbar_length + x)
102 return ret
107 function hb.get_hudtable(identifier)
108 return hb.hudtables[identifier]
111 function hb.get_hudbar_position_index(identifier)
112 if hb.settings.sorting[identifier] ~= nil then
113 return hb.settings.sorting[identifier]
114 else
115 local i = 0
116 while true do
117 if hb.registered_slots[i] ~= true and hb.settings.sorting_reverse[i] == nil then
118 return i
120 i = i + 1
125 function hb.register_hudbar(identifier, text_color, label, textures, default_start_value, default_start_max, default_start_hidden, format_string)
126 minetest.log("action", "hb.register_hudbar: "..tostring(identifier))
127 local hudtable = {}
128 local pos, offset
129 local index = math.floor(hb.get_hudbar_position_index(identifier))
130 hb.registered_slots[index] = true
131 if hb.settings.alignment_pattern == "stack_up" then
132 pos = hb.settings.pos_left
133 offset = {
134 x = hb.settings.start_offset_left.x,
135 y = hb.settings.start_offset_left.y - hb.settings.vmargin * index
137 elseif hb.settings.alignment_pattern == "stack_down" then
138 pos = hb.settings.pos_left
139 offset = {
140 x = hb.settings.start_offset_left.x,
141 y = hb.settings.start_offset_left.y + hb.settings.vmargin * index
143 else
144 if index % 2 == 0 then
145 pos = hb.settings.pos_left
146 offset = {
147 x = hb.settings.start_offset_left.x,
148 y = hb.settings.start_offset_left.y - hb.settings.vmargin * (index/2)
150 else
151 pos = hb.settings.pos_right
152 offset = {
153 x = hb.settings.start_offset_right.x,
154 y = hb.settings.start_offset_right.y - hb.settings.vmargin * ((index-1)/2)
158 if format_string == nil then
159 format_string = "%s: %d/%d"
162 hudtable.add_all = function(player, hudtable, start_value, start_max, start_hidden)
163 if start_value == nil then start_value = hudtable.default_start_value end
164 if start_max == nil then start_max = hudtable.default_start_max end
165 if start_hidden == nil then start_hidden = hudtable.default_start_hidden end
166 local ids = {}
167 local state = {}
168 local name = player:get_player_name()
169 local bgscale, iconscale, text, barnumber
170 if start_max == 0 or start_hidden then
171 bgscale = { x=0, y=0 }
172 else
173 bgscale = { x=1, y=1 }
175 if start_hidden then
176 iconscale = { x=0, y=0 }
177 barnumber = 0
178 text = ""
179 else
180 iconscale = { x=1, y=1 }
181 barnumber = hb.value_to_barlength(start_value, start_max)
182 text = string.format(format_string, label, start_value, start_max)
184 if hb.settings.bar_type == "progress_bar" then
185 ids.bg = player:hud_add({
186 hud_elem_type = "image",
187 position = pos,
188 scale = bgscale,
189 text = "hudbars_bar_background.png",
190 alignment = {x=1,y=1},
191 offset = { x = offset.x - 1, y = offset.y - 1 },
193 if textures.icon ~= nil then
194 ids.icon = player:hud_add({
195 hud_elem_type = "image",
196 position = pos,
197 scale = iconscale,
198 text = textures.icon,
199 alignment = {x=-1,y=1},
200 offset = { x = offset.x - 3, y = offset.y },
203 elseif hb.settings.bar_type == "statbar_modern" then
204 if textures.bgicon ~= nil then
205 ids.bg = player:hud_add({
206 hud_elem_type = "statbar",
207 position = pos,
208 scale = bgscale,
209 text = textures.bgicon,
210 number = hb.settings.statbar_length,
211 alignment = {x=-1,y=-1},
212 offset = { x = offset.x, y = offset.y },
216 local bar_image
217 if hb.settings.bar_type == "progress_bar" then
218 bar_image = textures.bar
219 elseif hb.settings.bar_type == "statbar_classic" or hb.settings.bar_type == "statbar_modern" then
220 bar_image = textures.icon
222 ids.bar = player:hud_add({
223 hud_elem_type = "statbar",
224 position = pos,
225 text = bar_image,
226 number = barnumber,
227 alignment = {x=-1,y=-1},
228 offset = offset,
230 if hb.settings.bar_type == "progress_bar" then
231 ids.text = player:hud_add({
232 hud_elem_type = "text",
233 position = pos,
234 text = text,
235 alignment = {x=1,y=1},
236 number = text_color,
237 direction = 0,
238 offset = { x = offset.x + 2, y = offset.y },
241 -- Do not forget to update hb.get_hudbar_state if you add new fields to the state table
242 state.hidden = start_hidden
243 state.value = start_value
244 state.max = start_max
245 state.text = text
246 state.barlength = hb.value_to_barlength(start_value, start_max)
248 local main_error_text =
249 "[hudbars] Bad initial values of HUD bar identifier “"..tostring(identifier).."” for player "..name..". "
251 if start_max < start_value then
252 minetest.log("error", main_error_text.."start_max ("..start_max..") is smaller than start_value ("..start_value..")!")
254 if start_max < 0 then
255 minetest.log("error", main_error_text.."start_max ("..start_max..") is smaller than 0!")
257 if start_value < 0 then
258 minetest.log("error", main_error_text.."start_value ("..start_value..") is smaller than 0!")
261 hb.hudtables[identifier].hudids[name] = ids
262 hb.hudtables[identifier].hudstate[name] = state
265 hudtable.identifier = identifier
266 hudtable.format_string = format_string
267 hudtable.label = label
268 hudtable.hudids = {}
269 hudtable.hudstate = {}
270 hudtable.default_start_hidden = default_start_hidden
271 hudtable.default_start_value = default_start_value
272 hudtable.default_start_max = default_start_max
274 hb.hudbars_count= hb.hudbars_count + 1
276 hb.hudtables[identifier] = hudtable
279 function hb.init_hudbar(player, identifier, start_value, start_max, start_hidden)
280 local hudtable = hb.get_hudtable(identifier)
281 hb.hudtables[identifier].add_all(player, hudtable, start_value, start_max, start_hidden)
284 function hb.change_hudbar(player, identifier, new_value, new_max_value, new_icon, new_bgicon, new_bar, new_label, new_text_color)
285 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
286 return
289 local name = player:get_player_name()
290 local hudtable = hb.get_hudtable(identifier)
291 local value_changed, max_changed = false, false
293 if new_value ~= nil then
294 if new_value ~= hudtable.hudstate[name].value then
295 hudtable.hudstate[name].value = new_value
296 value_changed = true
298 else
299 new_value = hudtable.hudstate[name].value
301 if new_max_value ~= nil then
302 if new_max_value ~= hudtable.hudstate[name].max then
303 hudtable.hudstate[name].max = new_max_value
304 max_changed = true
306 else
307 new_max_value = hudtable.hudstate[name].max
310 if hb.settings.bar_type == "progress_bar" then
311 if new_icon ~= nil and hudtable.hudids[name].icon ~= nil then
312 player:hud_change(hudtable.hudids[name].icon, "text", new_icon)
314 if new_bgicon ~= nil and hudtable.hudids[name].bgicon ~= nil then
315 player:hud_change(hudtable.hudids[name].bgicon, "text", new_bgicon)
317 if new_bar ~= nil then
318 player:hud_change(hudtable.hudids[name].bar , "text", new_bar)
320 if new_label ~= nil then
321 hudtable.label = new_label
322 local new_text = string.format(hudtable.format_string, new_label, hudtable.hudstate[name].value, hudtable.hudstate[name].max)
323 player:hud_change(hudtable.hudids[name].text, "text", new_text)
325 if new_text_color ~= nil then
326 player:hud_change(hudtable.hudids[name].text, "number", new_text_color)
328 else
329 if new_icon ~= nil and hudtable.hudids[name].bar ~= nil then
330 player:hud_change(hudtable.hudids[name].bar, "text", new_icon)
332 if new_bgicon ~= nil and hudtable.hudids[name].bg ~= nil then
333 player:hud_change(hudtable.hudids[name].bg, "text", new_bgicon)
337 local main_error_text =
338 "[hudbars] Bad call to hb.change_hudbar, identifier: “"..tostring(identifier).."”, player name: “"..name.."”. "
339 if new_max_value < new_value then
340 minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than new_value ("..new_value..")!")
342 if new_max_value < 0 then
343 minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than 0!")
345 if new_value < 0 then
346 minetest.log("error", main_error_text.."new_value ("..new_value..") is smaller than 0!")
349 if hudtable.hudstate[name].hidden == false then
350 if max_changed and hb.settings.bar_type == "progress_bar" then
351 if hudtable.hudstate[name].max == 0 then
352 player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
353 else
354 player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
358 if value_changed or max_changed then
359 local new_barlength = hb.value_to_barlength(new_value, new_max_value)
360 if new_barlength ~= hudtable.hudstate[name].barlength then
361 player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(new_value, new_max_value))
362 hudtable.hudstate[name].barlength = new_barlength
365 if hb.settings.bar_type == "progress_bar" then
366 local new_text = string.format(hudtable.format_string, hudtable.label, new_value, new_max_value)
367 if new_text ~= hudtable.hudstate[name].text then
368 player:hud_change(hudtable.hudids[name].text, "text", new_text)
369 hudtable.hudstate[name].text = new_text
376 function hb.hide_hudbar(player, identifier)
377 local name = player:get_player_name()
378 local hudtable = hb.get_hudtable(identifier)
379 if(hudtable.hudstate[name].hidden == false) then
380 if hb.settings.bar_type == "progress_bar" then
381 if hudtable.hudids[name].icon ~= nil then
382 player:hud_change(hudtable.hudids[name].icon, "scale", {x=0,y=0})
384 player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
385 player:hud_change(hudtable.hudids[name].text, "text", "")
387 player:hud_change(hudtable.hudids[name].bar, "number", 0)
388 hudtable.hudstate[name].hidden = true
392 function hb.unhide_hudbar(player, identifier)
393 local name = player:get_player_name()
394 local hudtable = hb.get_hudtable(identifier)
395 if(hudtable.hudstate[name].hidden) then
396 local name = player:get_player_name()
397 local value = hudtable.hudstate[name].value
398 local max = hudtable.hudstate[name].max
399 if hb.settings.bar_type == "progress_bar" then
400 if hudtable.hudids[name].icon ~= nil then
401 player:hud_change(hudtable.hudids[name].icon, "scale", {x=1,y=1})
403 if hudtable.hudstate[name].max ~= 0 then
404 player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
406 player:hud_change(hudtable.hudids[name].text, "text", tostring(string.format(hudtable.format_string, hudtable.label, value, max)))
408 player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(value, max))
409 hudtable.hudstate[name].hidden = false
413 function hb.get_hudbar_state(player, identifier)
414 local ref = hb.get_hudtable(identifier).hudstate[player:get_player_name()]
415 -- Do not forget to update this chunk of code in case the state changes
416 local copy = {
417 hidden = ref.hidden,
418 value = ref.value,
419 max = ref.max,
420 text = ref.text,
421 barlength = ref.barlength,
423 return copy
426 --register built-in HUD bars
427 if minetest.setting_getbool("enable_damage") or hb.settings.forceload_default_hudbars then
428 hb.register_hudbar("health", 0xFFFFFF, "Health", { bar = "hudbars_bar_health.png", icon = "hudbars_icon_health.png", bgicon = "hudbars_bgicon_health.png" }, 20, 20, false)
429 hb.register_hudbar("breath", 0xFFFFFF, "Breath", { bar = "hudbars_bar_breath.png", icon = "hudbars_icon_breath.png" }, 10, 10, true)
432 local function hide_builtin(player)
433 local flags = player:hud_get_flags()
434 flags.healthbar = false
435 flags.breathbar = false
436 player:hud_set_flags(flags)
440 local function custom_hud(player)
441 if minetest.setting_getbool("enable_damage") or hb.settings.forceload_default_hudbars then
442 local hide
443 if minetest.setting_getbool("enable_damage") then
444 hide = false
445 else
446 hide = true
448 hb.init_hudbar(player, "health", player:get_hp(), nil, hide)
449 local breath = player:get_breath()
450 local hide_breath
451 if breath == 11 and hb.settings.autohide_breath == true then hide_breath = true else hide_breath = false end
452 hb.init_hudbar(player, "breath", math.min(breath, 10), nil, hide_breath or hide)
457 -- update built-in HUD bars
458 local function update_hud(player)
459 if minetest.setting_getbool("enable_damage") then
460 if hb.settings.forceload_default_hudbars then
461 hb.unhide_hudbar(player, "health")
463 --air
464 local breath = player:get_breath()
466 if breath == 11 and hb.settings.autohide_breath == true then
467 hb.hide_hudbar(player, "breath")
468 else
469 hb.unhide_hudbar(player, "breath")
470 hb.change_hudbar(player, "breath", math.min(breath, 10))
473 --health
474 hb.change_hudbar(player, "health", player:get_hp())
475 elseif hb.settings.forceload_default_hudbars then
476 hb.hide_hudbar(player, "health")
477 hb.hide_hudbar(player, "breath")
481 minetest.register_on_joinplayer(function(player)
482 hide_builtin(player)
483 custom_hud(player)
484 hb.players[player:get_player_name()] = player
485 end)
487 minetest.register_on_leaveplayer(function(player)
488 hb.players[player:get_player_name()] = nil
489 end)
491 local main_timer = 0
492 local timer = 0
493 minetest.register_globalstep(function(dtime)
494 main_timer = main_timer + dtime
495 timer = timer + dtime
496 if main_timer > hb.settings.tick or timer > 4 then
497 if main_timer > hb.settings.tick then main_timer = 0 end
498 -- only proceed if damage is enabled
499 if minetest.setting_getbool("enable_damage") or hb.settings.forceload_default_hudbars then
500 for playername, player in pairs(hb.players) do
501 -- update all hud elements
502 update_hud(player)
506 if timer > 4 then timer = 0 end
507 end)