Bar length is now rounded towards nearest integer
[minetest_hudbars.git] / init.lua
blob6a66c90c13661a2354fd630b16ec04d5a49ea03f
1 hb = {}
3 hb.hudtables = {}
5 -- number of registered HUD bars
6 hb.hudbars_count = 0
8 -- table which recoreds which HUD bar slots have been “registered” so far; used for automatic positioning
9 hb.registered_slots = {}
11 hb.settings = {}
13 -- default settings
14 hb.settings.max_bar_length = 160
16 -- statbar positions
17 hb.settings.pos_left = { x=0.5, y=1 }
18 hb.settings.pos_right= { x = 0.5, y = 1 }
19 hb.settings.start_offset_left = { x = -175, y = -86 }
20 hb.settings.start_offset_right = { x = 15, y = -86 }
22 hb.settings.vmargin = 24
23 hb.settings.tick = 0.1
25 --[[
26 - hudbars_alignment_pattern: This setting changes the way the HUD bars are ordered on the display. You can choose
27 between a zig-zag pattern or a vertically stacked pattern.
28 The following values are allowed:
29 zigzag: Starting from the left bottom, the next is right from the first,
30 the next is above the first, the next is right of the third, etc.
31 This is the default.
32 stack_up: The HUD bars are stacked vertically, going upwards.
33 stack_down: The HUD bars are stacked vertically. going downwards.
35 hb.settings.alignment_pattern = "zigzag"
36 local alignment_pattern = minetest.setting_get("hudbars_alignment_pattern")
37 if alignment_pattern ~= nil then
38 hb.settings.alignment_pattern = alignment_pattern
39 if alignment_pattern ~= "zigzag" and alignment_pattern ~= "stack_up" and alignment_pattern ~= "stack_down" then
40 hb.settings.alignment_pattern = "zigzag"
41 minetest.log("error", "[hudbars] Invalid value for hudbars_alignment_pattern! Using default (zigzag).")
42 end
43 end
45 hb.settings.bar_type = "progress_bar"
46 local bar_type = minetest.setting_get("hudbars_bar_type")
47 if bar_type ~= nil then
48 hb.settings.bar_type = bar_type
49 if bar_type ~= "progress_bar" and bar_type ~= "statbar_classic" and bar_type ~= "statbar_modern" then
50 hb.settings.bar_type = "progress_bar"
51 minetest.log("error", "[hudbars] Invalid value for hudbars_bar_type! Using default (progress_bar).")
52 end
53 end
57 hb.settings.autohide_breath = true
58 local autohide_breath = minetest.setting_getbool("hudbars_autohide_breath")
59 if autohide_breath ~= nil then
60 hb.settings.autohide_breath = autohide_breath
61 end
63 local sorting = minetest.setting_get("hudbars_sorting")
64 if sorting ~= nil then
65 hb.settings.sorting = {}
66 hb.settings.sorting_reverse = {}
67 for k,v in string.gmatch(sorting, "(%w+)=(%w+)") do
68 hb.settings.sorting[k] = tonumber(v)
69 hb.settings.sorting_reverse[tonumber(v)] = k
70 end
71 else
72 hb.settings.sorting = { ["health"] = 0, ["breath"] = 1 }
73 hb.settings.sorting_reverse = { [0] = "health", [1] = "breath" }
74 end
76 -- Table which contains all players with active default HUD bars (only for internal use)
77 hb.players = {}
79 function hb.value_to_barlength(value, max)
80 if max == 0 then
81 return 0
82 else
83 if hb.settings.bar_type == "progress_bar" then
84 local x
85 if value < 0 then x=-0.5 else x = 0.5 end
86 local ret = math.modf((value/max) * hb.settings.max_bar_length + x)
87 return ret
88 else
89 local x
90 if value < 0 then x=-0.5 else x = 0.5 end
91 local ret = math.modf((value/max) * 20 + x)
92 return ret
93 end
94 end
95 end
97 function hb.get_hudtable(identifier)
98 return hb.hudtables[identifier]
99 end
101 function hb.get_hudbar_position_index(identifier)
102 if hb.settings.sorting[identifier] ~= nil then
103 return hb.settings.sorting[identifier]
104 else
105 local i = 0
106 while true do
107 if hb.registered_slots[i] ~= true and hb.settings.sorting_reverse[i] == nil then
108 return i
110 i = i + 1
115 function hb.register_hudbar(identifier, text_color, label, textures, default_start_value, default_start_max, default_start_hidden, format_string)
116 local hudtable = {}
117 local pos, offset
118 local index = math.floor(hb.get_hudbar_position_index(identifier))
119 hb.registered_slots[index] = true
120 if hb.settings.alignment_pattern == "stack_up" then
121 pos = hb.settings.pos_left
122 offset = {
123 x = hb.settings.start_offset_left.x,
124 y = hb.settings.start_offset_left.y - hb.settings.vmargin * index
126 elseif hb.settings.alignment_pattern == "stack_down" then
127 pos = hb.settings.pos_left
128 offset = {
129 x = hb.settings.start_offset_left.x,
130 y = hb.settings.start_offset_left.y + hb.settings.vmargin * index
132 else
133 if index % 2 == 0 then
134 pos = hb.settings.pos_left
135 offset = {
136 x = hb.settings.start_offset_left.x,
137 y = hb.settings.start_offset_left.y - hb.settings.vmargin * (index/2)
139 else
140 pos = hb.settings.pos_right
141 offset = {
142 x = hb.settings.start_offset_right.x,
143 y = hb.settings.start_offset_right.y - hb.settings.vmargin * ((index-1)/2)
147 if format_string == nil then
148 format_string = "%s: %d/%d"
151 hudtable.add_all = function(player, hudtable, start_value, start_max, start_hidden)
152 if start_value == nil then start_value = hudtable.default_start_value end
153 if start_max == nil then start_max = hudtable.default_start_max end
154 if start_hidden == nil then start_hidden = hudtable.default_start_hidden end
155 local ids = {}
156 local state = {}
157 local name = player:get_player_name()
158 local bgscale, iconscale, text, barnumber
159 if start_max == 0 or start_hidden then
160 bgscale = { x=0, y=0 }
161 else
162 bgscale = { x=1, y=1 }
164 if start_hidden then
165 iconscale = { x=0, y=0 }
166 barnumber = 0
167 text = ""
168 else
169 iconscale = { x=1, y=1 }
170 barnumber = hb.value_to_barlength(start_value, start_max)
171 text = string.format(format_string, label, start_value, start_max)
173 if hb.settings.bar_type == "progress_bar" then
174 ids.bg = player:hud_add({
175 hud_elem_type = "image",
176 position = pos,
177 scale = bgscale,
178 text = "hudbars_bar_background.png",
179 alignment = {x=1,y=1},
180 offset = { x = offset.x - 1, y = offset.y - 1 },
182 if textures.icon ~= nil then
183 ids.icon = player:hud_add({
184 hud_elem_type = "image",
185 position = pos,
186 scale = iconscale,
187 text = textures.icon,
188 alignment = {x=-1,y=1},
189 offset = { x = offset.x - 3, y = offset.y },
192 elseif hb.settings.bar_type == "statbar_modern" then
193 if textures.bgicon ~= nil then
194 ids.bg = player:hud_add({
195 hud_elem_type = "statbar",
196 position = pos,
197 scale = bgscale,
198 text = textures.bgicon,
199 number = 20,
200 alignment = {x=-1,y=-1},
201 offset = { x = offset.x, y = offset.y },
205 local bar_image
206 if hb.settings.bar_type == "progress_bar" then
207 bar_image = textures.bar
208 elseif hb.settings.bar_type == "statbar_classic" or hb.settings.bar_type == "statbar_modern" then
209 bar_image = textures.icon
211 ids.bar = player:hud_add({
212 hud_elem_type = "statbar",
213 position = pos,
214 text = bar_image,
215 number = barnumber,
216 alignment = {x=-1,y=-1},
217 offset = offset,
219 if hb.settings.bar_type == "progress_bar" then
220 ids.text = player:hud_add({
221 hud_elem_type = "text",
222 position = pos,
223 text = text,
224 alignment = {x=1,y=1},
225 number = text_color,
226 direction = 0,
227 offset = { x = offset.x + 2, y = offset.y },
230 -- Do not forget to update hb.get_hudbar_state if you add new fields to the state table
231 state.hidden = start_hidden
232 state.value = start_value
233 state.max = start_max
234 state.text = text
235 state.barlength = hb.value_to_barlength(start_value, start_max)
237 local main_error_text =
238 "[hudbars] Bad initial values of HUD bar identifier “"..tostring(identifier).."” for player "..name..". "
240 if start_max < start_value then
241 minetest.log("error", main_error_text.."start_max ("..start_max..") is smaller than start_value ("..start_value..")!")
243 if start_max < 0 then
244 minetest.log("error", main_error_text.."start_max ("..start_max..") is smaller than 0!")
246 if start_value < 0 then
247 minetest.log("error", main_error_text.."start_value ("..start_value..") is smaller than 0!")
250 hb.hudtables[identifier].hudids[name] = ids
251 hb.hudtables[identifier].hudstate[name] = state
254 hudtable.identifier = identifier
255 hudtable.format_string = format_string
256 hudtable.label = label
257 hudtable.hudids = {}
258 hudtable.hudstate = {}
259 hudtable.default_start_hidden = default_start_hidden
260 hudtable.default_start_value = default_start_value
261 hudtable.default_start_max = default_start_max
263 hb.hudbars_count= hb.hudbars_count + 1
265 hb.hudtables[identifier] = hudtable
268 function hb.init_hudbar(player, identifier, start_value, start_max, start_hidden)
269 local hudtable = hb.get_hudtable(identifier)
270 hb.hudtables[identifier].add_all(player, hudtable, start_value, start_max, start_hidden)
273 function hb.change_hudbar(player, identifier, new_value, new_max_value)
274 if new_value == nil and new_max_value == nil then
275 return
278 local name = player:get_player_name()
279 local hudtable = hb.get_hudtable(identifier)
280 local value_changed, max_changed = false, false
282 if new_value ~= nil then
283 if new_value ~= hudtable.hudstate[name].value then
284 hudtable.hudstate[name].value = new_value
285 value_changed = true
287 else
288 new_value = hudtable.hudstate[name].value
290 if new_max_value ~= nil then
291 if new_max_value ~= hudtable.hudstate[name].max then
292 hudtable.hudstate[name].max = new_max_value
293 max_changed = true
295 else
296 new_max_value = hudtable.hudstate[name].max
299 local main_error_text =
300 "[hudbars] Bad call to hb.change_hudbar, identifier: “"..tostring(identifier).."”, player name: “"..name.."”. "
301 if new_max_value < new_value then
302 minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than new_value ("..new_value..")!")
304 if new_max_value < 0 then
305 minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than 0!")
307 if new_value < 0 then
308 minetest.log("error", main_error_text.."new_value ("..new_value..") is smaller than 0!")
311 if hudtable.hudstate[name].hidden == false then
312 if max_changed and hb.settings.bar_type == "progress_bar" then
313 if hudtable.hudstate[name].max == 0 then
314 player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
315 else
316 player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
320 if value_changed or max_changed then
321 local new_barlength = hb.value_to_barlength(new_value, new_max_value)
322 if new_barlength ~= hudtable.hudstate[name].barlength then
323 player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(new_value, new_max_value))
324 hudtable.hudstate[name].barlength = new_barlength
327 if hb.settings.bar_type == "progress_bar" then
328 local new_text = string.format(hudtable.format_string, hudtable.label, new_value, new_max_value)
329 if new_text ~= hudtable.hudstate[name].text then
330 player:hud_change(hudtable.hudids[name].text, "text", new_text)
331 hudtable.hudstate[name].text = new_text
338 function hb.hide_hudbar(player, identifier)
339 local name = player:get_player_name()
340 local hudtable = hb.get_hudtable(identifier)
341 if(hudtable.hudstate[name].hidden == false) then
342 if hb.settings.bar_type == "progress_bar" then
343 if hudtable.hudids[name].icon ~= nil then
344 player:hud_change(hudtable.hudids[name].icon, "scale", {x=0,y=0})
346 player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
347 player:hud_change(hudtable.hudids[name].text, "text", "")
349 player:hud_change(hudtable.hudids[name].bar, "number", 0)
350 hudtable.hudstate[name].hidden = true
354 function hb.unhide_hudbar(player, identifier)
355 local name = player:get_player_name()
356 local hudtable = hb.get_hudtable(identifier)
357 if(hudtable.hudstate[name].hidden) then
358 local name = player:get_player_name()
359 local value = hudtable.hudstate[name].value
360 local max = hudtable.hudstate[name].max
361 if hb.settings.bar_type == "progress_bar" then
362 if hudtable.hudids[name].icon ~= nil then
363 player:hud_change(hudtable.hudids[name].icon, "scale", {x=1,y=1})
365 if hudtable.hudstate[name].max ~= 0 then
366 player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
368 player:hud_change(hudtable.hudids[name].text, "text", tostring(string.format(hudtable.format_string, hudtable.label, value, max)))
370 player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(value, max))
371 hudtable.hudstate[name].hidden = false
375 function hb.get_hudbar_state(player, identifier)
376 local ref = hb.get_hudtable(identifier).hudstate[player:get_player_name()]
377 -- Do not forget to update this chunk of code in case the state changes
378 local copy = {
379 hidden = ref.hidden,
380 value = ref.value,
381 max = ref.max,
382 text = ref.text,
383 barlength = ref.barlength,
385 return copy
388 --register built-in HUD bars
389 if minetest.setting_getbool("enable_damage") then
390 hb.register_hudbar("health", 0xFFFFFF, "Health", { bar = "hudbars_bar_health.png", icon = "hudbars_icon_health.png", bgicon = "hudbars_bgicon_health.png" }, 20, 20, false)
391 hb.register_hudbar("breath", 0xFFFFFF, "Breath", { bar = "hudbars_bar_breath.png", icon = "hudbars_icon_breath.png" }, 10, 10, true)
394 --load custom settings
395 local set = io.open(minetest.get_modpath("hudbars").."/hudbars.conf", "r")
396 if set then
397 dofile(minetest.get_modpath("hudbars").."/hudbars.conf")
398 set:close()
401 local function hide_builtin(player)
402 local flags = player:hud_get_flags()
403 flags.healthbar = false
404 flags.breathbar = false
405 player:hud_set_flags(flags)
409 local function custom_hud(player)
410 if minetest.setting_getbool("enable_damage") then
411 hb.init_hudbar(player, "health", player:get_hp())
412 local breath = player:get_breath()
413 local hide_breath
414 if breath == 11 and hb.settings.autohide_breath == true then hide_breath = true else hide_breath = false end
415 hb.init_hudbar(player, "breath", math.min(breath, 10), nil, hide_breath)
420 -- update built-in HUD bars
421 local function update_hud(player)
422 if minetest.setting_getbool("enable_damage") then
423 --air
424 local breath = player:get_breath()
426 if breath == 11 and hb.settings.autohide_breath == true then
427 hb.hide_hudbar(player, "breath")
428 else
429 hb.unhide_hudbar(player, "breath")
430 hb.change_hudbar(player, "breath", math.min(breath, 10))
433 --health
434 hb.change_hudbar(player, "health", player:get_hp())
438 minetest.register_on_joinplayer(function(player)
439 hide_builtin(player)
440 custom_hud(player)
441 hb.players[player:get_player_name()] = player
442 end)
444 minetest.register_on_leaveplayer(function(player)
445 hb.players[player:get_player_name()] = nil
446 end)
448 local main_timer = 0
449 local timer = 0
450 minetest.register_globalstep(function(dtime)
451 main_timer = main_timer + dtime
452 timer = timer + dtime
453 if main_timer > hb.settings.tick or timer > 4 then
454 if main_timer > hb.settings.tick then main_timer = 0 end
455 for playername, player in pairs(hb.players) do
456 -- only proceed if damage is enabled
457 if minetest.setting_getbool("enable_damage") then
458 -- update all hud elements
459 update_hud(player)
463 if timer > 4 then timer = 0 end
464 end)