Fix description of hudbars_tick
[minetest_hudbars.git] / init.lua
blob3aba7e11e20be951804b2f0e5e4befb2bcb5a0ae
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)
285 if new_value == nil and new_max_value == 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 local main_error_text =
311 "[hudbars] Bad call to hb.change_hudbar, identifier: “"..tostring(identifier).."”, player name: “"..name.."”. "
312 if new_max_value < new_value then
313 minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than new_value ("..new_value..")!")
315 if new_max_value < 0 then
316 minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than 0!")
318 if new_value < 0 then
319 minetest.log("error", main_error_text.."new_value ("..new_value..") is smaller than 0!")
322 if hudtable.hudstate[name].hidden == false then
323 if max_changed and hb.settings.bar_type == "progress_bar" then
324 if hudtable.hudstate[name].max == 0 then
325 player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
326 else
327 player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
331 if value_changed or max_changed then
332 local new_barlength = hb.value_to_barlength(new_value, new_max_value)
333 if new_barlength ~= hudtable.hudstate[name].barlength then
334 player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(new_value, new_max_value))
335 hudtable.hudstate[name].barlength = new_barlength
338 if hb.settings.bar_type == "progress_bar" then
339 local new_text = string.format(hudtable.format_string, hudtable.label, new_value, new_max_value)
340 if new_text ~= hudtable.hudstate[name].text then
341 player:hud_change(hudtable.hudids[name].text, "text", new_text)
342 hudtable.hudstate[name].text = new_text
349 function hb.hide_hudbar(player, identifier)
350 local name = player:get_player_name()
351 local hudtable = hb.get_hudtable(identifier)
352 if(hudtable.hudstate[name].hidden == false) then
353 if hb.settings.bar_type == "progress_bar" then
354 if hudtable.hudids[name].icon ~= nil then
355 player:hud_change(hudtable.hudids[name].icon, "scale", {x=0,y=0})
357 player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
358 player:hud_change(hudtable.hudids[name].text, "text", "")
360 player:hud_change(hudtable.hudids[name].bar, "number", 0)
361 hudtable.hudstate[name].hidden = true
365 function hb.unhide_hudbar(player, identifier)
366 local name = player:get_player_name()
367 local hudtable = hb.get_hudtable(identifier)
368 if(hudtable.hudstate[name].hidden) then
369 local name = player:get_player_name()
370 local value = hudtable.hudstate[name].value
371 local max = hudtable.hudstate[name].max
372 if hb.settings.bar_type == "progress_bar" then
373 if hudtable.hudids[name].icon ~= nil then
374 player:hud_change(hudtable.hudids[name].icon, "scale", {x=1,y=1})
376 if hudtable.hudstate[name].max ~= 0 then
377 player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
379 player:hud_change(hudtable.hudids[name].text, "text", tostring(string.format(hudtable.format_string, hudtable.label, value, max)))
381 player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(value, max))
382 hudtable.hudstate[name].hidden = false
386 function hb.get_hudbar_state(player, identifier)
387 local ref = hb.get_hudtable(identifier).hudstate[player:get_player_name()]
388 -- Do not forget to update this chunk of code in case the state changes
389 local copy = {
390 hidden = ref.hidden,
391 value = ref.value,
392 max = ref.max,
393 text = ref.text,
394 barlength = ref.barlength,
396 return copy
399 --register built-in HUD bars
400 if minetest.setting_getbool("enable_damage") or hb.settings.forceload_default_hudbars then
401 hb.register_hudbar("health", 0xFFFFFF, "Health", { bar = "hudbars_bar_health.png", icon = "hudbars_icon_health.png", bgicon = "hudbars_bgicon_health.png" }, 20, 20, false)
402 hb.register_hudbar("breath", 0xFFFFFF, "Breath", { bar = "hudbars_bar_breath.png", icon = "hudbars_icon_breath.png" }, 10, 10, true)
405 local function hide_builtin(player)
406 local flags = player:hud_get_flags()
407 flags.healthbar = false
408 flags.breathbar = false
409 player:hud_set_flags(flags)
413 local function custom_hud(player)
414 if minetest.setting_getbool("enable_damage") or hb.settings.forceload_default_hudbars then
415 local hide
416 if minetest.setting_getbool("enable_damage") then
417 hide = false
418 else
419 hide = true
421 hb.init_hudbar(player, "health", player:get_hp(), nil, hide)
422 local breath = player:get_breath()
423 local hide_breath
424 if breath == 11 and hb.settings.autohide_breath == true then hide_breath = true else hide_breath = false end
425 hb.init_hudbar(player, "breath", math.min(breath, 10), nil, hide_breath or hide)
430 -- update built-in HUD bars
431 local function update_hud(player)
432 if minetest.setting_getbool("enable_damage") then
433 if hb.settings.forceload_default_hudbars then
434 hb.unhide_hudbar(player, "health")
436 --air
437 local breath = player:get_breath()
439 if breath == 11 and hb.settings.autohide_breath == true then
440 hb.hide_hudbar(player, "breath")
441 else
442 hb.unhide_hudbar(player, "breath")
443 hb.change_hudbar(player, "breath", math.min(breath, 10))
446 --health
447 hb.change_hudbar(player, "health", player:get_hp())
448 elseif hb.settings.forceload_default_hudbars then
449 hb.hide_hudbar(player, "health")
450 hb.hide_hudbar(player, "breath")
454 minetest.register_on_joinplayer(function(player)
455 hide_builtin(player)
456 custom_hud(player)
457 hb.players[player:get_player_name()] = player
458 end)
460 minetest.register_on_leaveplayer(function(player)
461 hb.players[player:get_player_name()] = nil
462 end)
464 local main_timer = 0
465 local timer = 0
466 minetest.register_globalstep(function(dtime)
467 main_timer = main_timer + dtime
468 timer = timer + dtime
469 if main_timer > hb.settings.tick or timer > 4 then
470 if main_timer > hb.settings.tick then main_timer = 0 end
471 -- only proceed if damage is enabled
472 if minetest.setting_getbool("enable_damage") or hb.settings.forceload_default_hudbars then
473 for playername, player in pairs(hb.players) do
474 -- update all hud elements
475 update_hud(player)
479 if timer > 4 then timer = 0 end
480 end)