Fix hudbars_tick = hudbars_vmargin
[minetest_hudbars.git] / init.lua
blobd9465980927a2a30f529d942ff495b2112c4aa43
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 --[[
69 - hudbars_alignment_pattern: This setting changes the way the HUD bars are ordered on the display. You can choose
70 between a zig-zag pattern or a vertically stacked pattern.
71 The following values are allowed:
72 zigzag: Starting from the left bottom, the next is right from the first,
73 the next is above the first, the next is right of the third, etc.
74 This is the default.
75 stack_up: The HUD bars are stacked vertically, going upwards.
76 stack_down: The HUD bars are stacked vertically, going downwards.
79 -- Misc. settings
80 hb.settings.alignment_pattern = hb.load_setting("hudbars_alignment_pattern", "string", "zigzag", {"zigzag", "stack_up", "stack_down"})
81 hb.settings.bar_type = hb.load_setting("hudbars_bar_type", "string", "progress_bar", {"progress_bar", "statbar_classic", "statbar_modern"})
82 hb.settings.autohide_breath = hb.load_setting("hudbars_autohide_breath", "bool", true)
84 local sorting = minetest.setting_get("hudbars_sorting")
85 if sorting ~= nil then
86 hb.settings.sorting = {}
87 hb.settings.sorting_reverse = {}
88 for k,v in string.gmatch(sorting, "(%w+)=(%w+)") do
89 hb.settings.sorting[k] = tonumber(v)
90 hb.settings.sorting_reverse[tonumber(v)] = k
91 end
92 else
93 hb.settings.sorting = { ["health"] = 0, ["breath"] = 1 }
94 hb.settings.sorting_reverse = { [0] = "health", [1] = "breath" }
95 end
97 -- Table which contains all players with active default HUD bars (only for internal use)
98 hb.players = {}
100 function hb.value_to_barlength(value, max)
101 if max == 0 then
102 return 0
103 else
104 if hb.settings.bar_type == "progress_bar" then
105 local x
106 if value < 0 then x=-0.5 else x = 0.5 end
107 local ret = math.modf((value/max) * hb.settings.max_bar_length + x)
108 return ret
109 else
110 local x
111 if value < 0 then x=-0.5 else x = 0.5 end
112 local ret = math.modf((value/max) * hb.settings.statbar_length + x)
113 return ret
118 function hb.get_hudtable(identifier)
119 return hb.hudtables[identifier]
122 function hb.get_hudbar_position_index(identifier)
123 if hb.settings.sorting[identifier] ~= nil then
124 return hb.settings.sorting[identifier]
125 else
126 local i = 0
127 while true do
128 if hb.registered_slots[i] ~= true and hb.settings.sorting_reverse[i] == nil then
129 return i
131 i = i + 1
136 function hb.register_hudbar(identifier, text_color, label, textures, default_start_value, default_start_max, default_start_hidden, format_string)
137 minetest.log("action", "hb.register_hudbar: "..tostring(identifier))
138 local hudtable = {}
139 local pos, offset
140 local index = math.floor(hb.get_hudbar_position_index(identifier))
141 hb.registered_slots[index] = true
142 if hb.settings.alignment_pattern == "stack_up" then
143 pos = hb.settings.pos_left
144 offset = {
145 x = hb.settings.start_offset_left.x,
146 y = hb.settings.start_offset_left.y - hb.settings.vmargin * index
148 elseif hb.settings.alignment_pattern == "stack_down" then
149 pos = hb.settings.pos_left
150 offset = {
151 x = hb.settings.start_offset_left.x,
152 y = hb.settings.start_offset_left.y + hb.settings.vmargin * index
154 else
155 if index % 2 == 0 then
156 pos = hb.settings.pos_left
157 offset = {
158 x = hb.settings.start_offset_left.x,
159 y = hb.settings.start_offset_left.y - hb.settings.vmargin * (index/2)
161 else
162 pos = hb.settings.pos_right
163 offset = {
164 x = hb.settings.start_offset_right.x,
165 y = hb.settings.start_offset_right.y - hb.settings.vmargin * ((index-1)/2)
169 if format_string == nil then
170 format_string = "%s: %d/%d"
173 hudtable.add_all = function(player, hudtable, start_value, start_max, start_hidden)
174 if start_value == nil then start_value = hudtable.default_start_value end
175 if start_max == nil then start_max = hudtable.default_start_max end
176 if start_hidden == nil then start_hidden = hudtable.default_start_hidden end
177 local ids = {}
178 local state = {}
179 local name = player:get_player_name()
180 local bgscale, iconscale, text, barnumber
181 if start_max == 0 or start_hidden then
182 bgscale = { x=0, y=0 }
183 else
184 bgscale = { x=1, y=1 }
186 if start_hidden then
187 iconscale = { x=0, y=0 }
188 barnumber = 0
189 text = ""
190 else
191 iconscale = { x=1, y=1 }
192 barnumber = hb.value_to_barlength(start_value, start_max)
193 text = string.format(format_string, 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",
198 position = pos,
199 scale = bgscale,
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",
207 position = pos,
208 scale = iconscale,
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",
218 position = pos,
219 scale = bgscale,
220 text = textures.bgicon,
221 number = hb.settings.statbar_length,
222 alignment = {x=-1,y=-1},
223 offset = { x = offset.x, y = offset.y },
227 local bar_image
228 if hb.settings.bar_type == "progress_bar" then
229 bar_image = textures.bar
230 elseif hb.settings.bar_type == "statbar_classic" or hb.settings.bar_type == "statbar_modern" then
231 bar_image = textures.icon
233 ids.bar = player:hud_add({
234 hud_elem_type = "statbar",
235 position = pos,
236 text = bar_image,
237 number = barnumber,
238 alignment = {x=-1,y=-1},
239 offset = offset,
241 if hb.settings.bar_type == "progress_bar" then
242 ids.text = player:hud_add({
243 hud_elem_type = "text",
244 position = pos,
245 text = text,
246 alignment = {x=1,y=1},
247 number = text_color,
248 direction = 0,
249 offset = { x = offset.x + 2, y = offset.y },
252 -- Do not forget to update hb.get_hudbar_state if you add new fields to the state table
253 state.hidden = start_hidden
254 state.value = start_value
255 state.max = start_max
256 state.text = text
257 state.barlength = hb.value_to_barlength(start_value, start_max)
259 local main_error_text =
260 "[hudbars] Bad initial values of HUD bar identifier “"..tostring(identifier).."” for player "..name..". "
262 if start_max < start_value then
263 minetest.log("error", main_error_text.."start_max ("..start_max..") is smaller than start_value ("..start_value..")!")
265 if start_max < 0 then
266 minetest.log("error", main_error_text.."start_max ("..start_max..") is smaller than 0!")
268 if start_value < 0 then
269 minetest.log("error", main_error_text.."start_value ("..start_value..") is smaller than 0!")
272 hb.hudtables[identifier].hudids[name] = ids
273 hb.hudtables[identifier].hudstate[name] = state
276 hudtable.identifier = identifier
277 hudtable.format_string = format_string
278 hudtable.label = label
279 hudtable.hudids = {}
280 hudtable.hudstate = {}
281 hudtable.default_start_hidden = default_start_hidden
282 hudtable.default_start_value = default_start_value
283 hudtable.default_start_max = default_start_max
285 hb.hudbars_count= hb.hudbars_count + 1
287 hb.hudtables[identifier] = hudtable
290 function hb.init_hudbar(player, identifier, start_value, start_max, start_hidden)
291 local hudtable = hb.get_hudtable(identifier)
292 hb.hudtables[identifier].add_all(player, hudtable, start_value, start_max, start_hidden)
295 function hb.change_hudbar(player, identifier, new_value, new_max_value)
296 if new_value == nil and new_max_value == nil then
297 return
300 local name = player:get_player_name()
301 local hudtable = hb.get_hudtable(identifier)
302 local value_changed, max_changed = false, false
304 if new_value ~= nil then
305 if new_value ~= hudtable.hudstate[name].value then
306 hudtable.hudstate[name].value = new_value
307 value_changed = true
309 else
310 new_value = hudtable.hudstate[name].value
312 if new_max_value ~= nil then
313 if new_max_value ~= hudtable.hudstate[name].max then
314 hudtable.hudstate[name].max = new_max_value
315 max_changed = true
317 else
318 new_max_value = hudtable.hudstate[name].max
321 local main_error_text =
322 "[hudbars] Bad call to hb.change_hudbar, identifier: “"..tostring(identifier).."”, player name: “"..name.."”. "
323 if new_max_value < new_value then
324 minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than new_value ("..new_value..")!")
326 if new_max_value < 0 then
327 minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than 0!")
329 if new_value < 0 then
330 minetest.log("error", main_error_text.."new_value ("..new_value..") is smaller than 0!")
333 if hudtable.hudstate[name].hidden == false then
334 if max_changed and hb.settings.bar_type == "progress_bar" then
335 if hudtable.hudstate[name].max == 0 then
336 player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
337 else
338 player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
342 if value_changed or max_changed then
343 local new_barlength = hb.value_to_barlength(new_value, new_max_value)
344 if new_barlength ~= hudtable.hudstate[name].barlength then
345 player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(new_value, new_max_value))
346 hudtable.hudstate[name].barlength = new_barlength
349 if hb.settings.bar_type == "progress_bar" then
350 local new_text = string.format(hudtable.format_string, hudtable.label, new_value, new_max_value)
351 if new_text ~= hudtable.hudstate[name].text then
352 player:hud_change(hudtable.hudids[name].text, "text", new_text)
353 hudtable.hudstate[name].text = new_text
360 function hb.hide_hudbar(player, identifier)
361 local name = player:get_player_name()
362 local hudtable = hb.get_hudtable(identifier)
363 if(hudtable.hudstate[name].hidden == false) then
364 if hb.settings.bar_type == "progress_bar" then
365 if hudtable.hudids[name].icon ~= nil then
366 player:hud_change(hudtable.hudids[name].icon, "scale", {x=0,y=0})
368 player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
369 player:hud_change(hudtable.hudids[name].text, "text", "")
371 player:hud_change(hudtable.hudids[name].bar, "number", 0)
372 hudtable.hudstate[name].hidden = true
376 function hb.unhide_hudbar(player, identifier)
377 local name = player:get_player_name()
378 local hudtable = hb.get_hudtable(identifier)
379 if(hudtable.hudstate[name].hidden) then
380 local name = player:get_player_name()
381 local value = hudtable.hudstate[name].value
382 local max = hudtable.hudstate[name].max
383 if hb.settings.bar_type == "progress_bar" then
384 if hudtable.hudids[name].icon ~= nil then
385 player:hud_change(hudtable.hudids[name].icon, "scale", {x=1,y=1})
387 if hudtable.hudstate[name].max ~= 0 then
388 player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
390 player:hud_change(hudtable.hudids[name].text, "text", tostring(string.format(hudtable.format_string, hudtable.label, value, max)))
392 player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(value, max))
393 hudtable.hudstate[name].hidden = false
397 function hb.get_hudbar_state(player, identifier)
398 local ref = hb.get_hudtable(identifier).hudstate[player:get_player_name()]
399 -- Do not forget to update this chunk of code in case the state changes
400 local copy = {
401 hidden = ref.hidden,
402 value = ref.value,
403 max = ref.max,
404 text = ref.text,
405 barlength = ref.barlength,
407 return copy
410 --register built-in HUD bars
411 if minetest.setting_getbool("enable_damage") or hb.settings.forceload_default_hudbars then
412 hb.register_hudbar("health", 0xFFFFFF, "Health", { bar = "hudbars_bar_health.png", icon = "hudbars_icon_health.png", bgicon = "hudbars_bgicon_health.png" }, 20, 20, false)
413 hb.register_hudbar("breath", 0xFFFFFF, "Breath", { bar = "hudbars_bar_breath.png", icon = "hudbars_icon_breath.png" }, 10, 10, true)
416 local function hide_builtin(player)
417 local flags = player:hud_get_flags()
418 flags.healthbar = false
419 flags.breathbar = false
420 player:hud_set_flags(flags)
424 local function custom_hud(player)
425 if minetest.setting_getbool("enable_damage") or hb.settings.forceload_default_hudbars then
426 local hide
427 if minetest.setting_getbool("enable_damage") then
428 hide = false
429 else
430 hide = true
432 hb.init_hudbar(player, "health", player:get_hp(), nil, hide)
433 local breath = player:get_breath()
434 local hide_breath
435 if breath == 11 and hb.settings.autohide_breath == true then hide_breath = true else hide_breath = false end
436 hb.init_hudbar(player, "breath", math.min(breath, 10), nil, hide_breath or hide)
441 -- update built-in HUD bars
442 local function update_hud(player)
443 if minetest.setting_getbool("enable_damage") then
444 if hb.settings.forceload_default_hudbars then
445 hb.unhide_hudbar(player, "health")
447 --air
448 local breath = player:get_breath()
450 if breath == 11 and hb.settings.autohide_breath == true then
451 hb.hide_hudbar(player, "breath")
452 else
453 hb.unhide_hudbar(player, "breath")
454 hb.change_hudbar(player, "breath", math.min(breath, 10))
457 --health
458 hb.change_hudbar(player, "health", player:get_hp())
459 elseif hb.settings.forceload_default_hudbars then
460 hb.hide_hudbar(player, "health")
461 hb.hide_hudbar(player, "breath")
465 minetest.register_on_joinplayer(function(player)
466 hide_builtin(player)
467 custom_hud(player)
468 hb.players[player:get_player_name()] = player
469 end)
471 minetest.register_on_leaveplayer(function(player)
472 hb.players[player:get_player_name()] = nil
473 end)
475 local main_timer = 0
476 local timer = 0
477 minetest.register_globalstep(function(dtime)
478 main_timer = main_timer + dtime
479 timer = timer + dtime
480 if main_timer > hb.settings.tick or timer > 4 then
481 if main_timer > hb.settings.tick then main_timer = 0 end
482 -- only proceed if damage is enabled
483 if minetest.setting_getbool("enable_damage") or hb.settings.forceload_default_hudbars then
484 for playername, player in pairs(hb.players) do
485 -- update all hud elements
486 update_hud(player)
490 if timer > 4 then timer = 0 end
491 end)