Add mailmap for Wuzzy
[minetest_hudbars.git] / init.lua
blob8a4e1fccfd9936cab4d0bcb59be08d5869e70192
1 local S = minetest.get_translator("hudbars")
2 local N = function(s) return s end
4 hb = {}
6 hb.hudtables = {}
8 -- number of registered HUD bars
9 hb.hudbars_count = 0
11 -- table which records which HUD bar slots have been “registered” so far; used for automatic positioning
12 hb.registered_slots = {}
14 hb.settings = {}
16 function hb.load_setting(sname, stype, defaultval, valid_values)
17 local sval
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))
24 end
25 if sval ~= nil then
26 if valid_values ~= nil then
27 local valid = false
28 for i=1,#valid_values do
29 if sval == valid_values[i] then
30 valid = true
31 end
32 end
33 if not valid then
34 minetest.log("error", "[hudbars] Invalid value for "..sname.."! Using default value ("..tostring(defaultval)..").")
35 return defaultval
36 else
37 return sval
38 end
39 else
40 return sval
41 end
42 else
43 return defaultval
44 end
45 end
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()
52 end
54 local function make_label(format_string, format_string_config, label, start_value, max_value)
55 local params = {}
56 local order = format_string_config.order
57 for o=1, #order do
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))
63 else
64 table.insert(params, start_value)
65 end
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))
69 else
70 table.insert(params, max_value)
71 end
72 end
73 end
74 local ret
75 if format_string_config.textdomain then
76 ret = minetest.translate(format_string_config.textdomain, format_string, unpack(params))
77 else
78 ret = S(format_string, unpack(params))
79 end
80 return ret
81 end
83 -- Table which contains all players with active default HUD bars (only for internal use)
84 hb.players = {}
86 function hb.value_to_barlength(value, max)
87 if max == 0 then
88 return 0
89 else
90 if hb.settings.bar_type == "progress_bar" then
91 local x
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)
94 return ret
95 else
96 local 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)
99 return ret
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]
111 else
112 local i = 0
113 while true do
114 if hb.registered_slots[i] ~= true and hb.settings.sorting_reverse[i] == nil then
115 return i
117 i = i + 1
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))
124 local hudtable = {}
125 local pos, offset
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
130 offset = {
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
136 offset = {
137 x = hb.settings.start_offset_left.x,
138 y = hb.settings.start_offset_left.y + hb.settings.vmargin * index
140 else
141 if index % 2 == 0 then
142 pos = hb.settings.pos_left
143 offset = {
144 x = hb.settings.start_offset_left.x,
145 y = hb.settings.start_offset_left.y - hb.settings.vmargin * (index/2)
147 else
148 pos = hb.settings.pos_right
149 offset = {
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
175 local ids = {}
176 local state = {}
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 }
181 else
182 bgscale = { x=1, y=1 }
184 if start_hidden then
185 iconscale = { x=0, y=0 }
186 barnumber = 0
187 bgiconnumber = 0
188 text = ""
189 else
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",
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 },
203 z_index = 0,
205 if textures.icon ~= nil then
206 ids.icon = player:hud_add({
207 hud_elem_type = "image",
208 position = pos,
209 scale = iconscale,
210 text = textures.icon,
211 alignment = {x=-1,y=1},
212 offset = { x = offset.x - 3, y = offset.y },
213 z_index = 1,
217 local bar_image, bgicon, bar_size
218 if hb.settings.bar_type == "progress_bar" then
219 bar_image = textures.bar
220 -- NOTE: Intentionally set to nil. For some reason, on some systems,
221 -- the progress bar is displaced when the bar_size is set explicitly here.
222 -- On the other hand, setting this to nil is deprecated in MT 5.0.0 due to
223 -- a debug log warning, but nothing is explained in lua_api.txt.
224 -- This section is a potential bug magnet, please watch with care!
225 -- The size of the bar image is expected to be exactly 2×16 pixels.
226 bar_size = nil
227 elseif hb.settings.bar_type == "statbar_classic" or hb.settings.bar_type == "statbar_modern" then
228 bar_image = textures.icon
229 bgicon = textures.bgicon
230 bar_size = {x=24, y=24}
232 ids.bar = player:hud_add({
233 hud_elem_type = "statbar",
234 position = pos,
235 text = bar_image,
236 text2 = bgicon,
237 number = barnumber,
238 item = bgiconnumber,
239 alignment = {x=-1,y=-1},
240 offset = offset,
241 direction = 0,
242 size = bar_size,
243 z_index = 1,
245 if hb.settings.bar_type == "progress_bar" then
246 ids.text = player:hud_add({
247 hud_elem_type = "text",
248 position = pos,
249 text = text,
250 alignment = {x=1,y=1},
251 number = text_color,
252 direction = 0,
253 offset = { x = offset.x + 2, y = offset.y - 1},
254 z_index = 2,
257 -- Do not forget to update hb.get_hudbar_state if you add new fields to the state table
258 state.hidden = start_hidden
259 state.value = start_value
260 state.max = start_max
261 state.text = text
262 state.barlength = hb.value_to_barlength(start_value, start_max)
264 local main_error_text =
265 "[hudbars] Bad initial values of HUD bar identifier “"..tostring(identifier).."” for player "..name..". "
267 if start_max < start_value then
268 minetest.log("error", main_error_text.."start_max ("..start_max..") is smaller than start_value ("..start_value..")!")
270 if start_max < 0 then
271 minetest.log("error", main_error_text.."start_max ("..start_max..") is smaller than 0!")
273 if start_value < 0 then
274 minetest.log("error", main_error_text.."start_value ("..start_value..") is smaller than 0!")
277 hb.hudtables[identifier].hudids[name] = ids
278 hb.hudtables[identifier].hudstate[name] = state
281 hudtable.identifier = identifier
282 hudtable.format_string = format_string
283 hudtable.format_string_config = format_string_config
284 hudtable.label = label
285 hudtable.hudids = {}
286 hudtable.hudstate = {}
287 hudtable.default_start_hidden = default_start_hidden
288 hudtable.default_start_value = default_start_value
289 hudtable.default_start_max = default_start_max
291 hb.hudbars_count= hb.hudbars_count + 1
293 hb.hudtables[identifier] = hudtable
296 function hb.init_hudbar(player, identifier, start_value, start_max, start_hidden)
297 if not player_exists(player) then return false end
298 local hudtable = hb.get_hudtable(identifier)
299 hb.hudtables[identifier].add_all(player, hudtable, start_value, start_max, start_hidden)
300 return true
303 function hb.change_hudbar(player, identifier, new_value, new_max_value, new_icon, new_bgicon, new_bar, new_label, new_text_color)
304 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
305 return true
307 if not player_exists(player) then
308 return false
311 local name = player:get_player_name()
312 local hudtable = hb.get_hudtable(identifier)
313 if not hudtable.hudstate[name] then
314 return false
316 local value_changed, max_changed = false, false
318 if new_value ~= nil then
319 if new_value ~= hudtable.hudstate[name].value then
320 hudtable.hudstate[name].value = new_value
321 value_changed = true
323 else
324 new_value = hudtable.hudstate[name].value
326 if new_max_value ~= nil then
327 if new_max_value ~= hudtable.hudstate[name].max then
328 hudtable.hudstate[name].max = new_max_value
329 max_changed = true
331 else
332 new_max_value = hudtable.hudstate[name].max
335 if hb.settings.bar_type == "progress_bar" then
336 if new_icon ~= nil and hudtable.hudids[name].icon ~= nil then
337 player:hud_change(hudtable.hudids[name].icon, "text", new_icon)
339 if new_bgicon ~= nil and hudtable.hudids[name].bgicon ~= nil then
340 player:hud_change(hudtable.hudids[name].bgicon, "text", new_bgicon)
342 if new_bar ~= nil then
343 player:hud_change(hudtable.hudids[name].bar , "text", new_bar)
345 if new_label ~= nil then
346 hudtable.label = new_label
347 local new_text = make_label(hudtable.format_string, hudtable.format_string_config, new_label, hudtable.hudstate[name].value, hudtable.hudstate[name].max)
348 player:hud_change(hudtable.hudids[name].text, "text", new_text)
350 if new_text_color ~= nil then
351 player:hud_change(hudtable.hudids[name].text, "number", new_text_color)
353 else
354 if new_icon ~= nil and hudtable.hudids[name].bar ~= nil then
355 player:hud_change(hudtable.hudids[name].bar, "text", new_icon)
357 if new_bgicon ~= nil and hudtable.hudids[name].bg ~= nil then
358 player:hud_change(hudtable.hudids[name].bg, "text", new_bgicon)
362 local main_error_text =
363 "[hudbars] Bad call to hb.change_hudbar, identifier: “"..tostring(identifier).."”, player name: “"..name.."”. "
364 if new_max_value < new_value then
365 minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than new_value ("..new_value..")!")
367 if new_max_value < 0 then
368 minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than 0!")
370 if new_value < 0 then
371 minetest.log("error", main_error_text.."new_value ("..new_value..") is smaller than 0!")
374 if hudtable.hudstate[name].hidden == false then
375 if max_changed and hb.settings.bar_type == "progress_bar" then
376 if hudtable.hudstate[name].max == 0 then
377 player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
378 else
379 player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
383 if value_changed or max_changed then
384 local new_barlength = hb.value_to_barlength(new_value, new_max_value)
385 if new_barlength ~= hudtable.hudstate[name].barlength then
386 player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(new_value, new_max_value))
387 hudtable.hudstate[name].barlength = new_barlength
390 if hb.settings.bar_type == "progress_bar" then
391 local new_text = make_label(hudtable.format_string, hudtable.format_string_config, hudtable.label, new_value, new_max_value)
392 if new_text ~= hudtable.hudstate[name].text then
393 player:hud_change(hudtable.hudids[name].text, "text", new_text)
394 hudtable.hudstate[name].text = new_text
399 return true
402 function hb.hide_hudbar(player, identifier)
403 if not player_exists(player) then return false end
404 local name = player:get_player_name()
405 local hudtable = hb.get_hudtable(identifier)
406 if hudtable == nil then return false end
407 if hudtable.hudstate[name].hidden == true then return true end
408 if hb.settings.bar_type == "progress_bar" then
409 if hudtable.hudids[name].icon ~= nil then
410 player:hud_change(hudtable.hudids[name].icon, "scale", {x=0,y=0})
412 player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
413 player:hud_change(hudtable.hudids[name].text, "text", "")
415 player:hud_change(hudtable.hudids[name].bar, "number", 0)
416 player:hud_change(hudtable.hudids[name].bar, "item", 0)
417 hudtable.hudstate[name].hidden = true
418 return true
421 function hb.unhide_hudbar(player, identifier)
422 if not player_exists(player) then return false end
423 local name = player:get_player_name()
424 local hudtable = hb.get_hudtable(identifier)
425 if hudtable == nil then return false end
426 if hudtable.hudstate[name].hidden == false then return true end
427 local value = hudtable.hudstate[name].value
428 local max = hudtable.hudstate[name].max
429 if hb.settings.bar_type == "progress_bar" then
430 if hudtable.hudids[name].icon ~= nil then
431 player:hud_change(hudtable.hudids[name].icon, "scale", {x=1,y=1})
433 if hudtable.hudstate[name].max ~= 0 then
434 player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
436 player:hud_change(hudtable.hudids[name].text, "text", make_label(hudtable.format_string, hudtable.format_string_config, hudtable.label, value, max))
437 elseif hb.settings.bar_type == "statbar_modern" then
438 player:hud_change(hudtable.hudids[name].bar, "scale", {x=1,y=1})
440 player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(value, max))
441 player:hud_change(hudtable.hudids[name].bar, "item", hb.value_to_barlength(max, max))
442 hudtable.hudstate[name].hidden = false
443 return true
446 function hb.get_hudbar_state(player, identifier)
447 if not player_exists(player) then return nil end
448 local ref = hb.get_hudtable(identifier).hudstate[player:get_player_name()]
449 -- Do not forget to update this chunk of code in case the state changes
450 local copy = {
451 hidden = ref.hidden,
452 value = ref.value,
453 max = ref.max,
454 text = ref.text,
455 barlength = ref.barlength,
457 return copy
460 function hb.get_hudbar_identifiers()
461 local ids = {}
462 for id, _ in pairs(hb.hudtables) do
463 table.insert(ids, id)
465 return ids
468 --register built-in HUD bars
469 if minetest.settings:get_bool("enable_damage") or hb.settings.forceload_default_hudbars then
470 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)
471 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)
474 local function hide_builtin(player)
475 local flags = player:hud_get_flags()
476 flags.healthbar = false
477 flags.breathbar = false
478 player:hud_set_flags(flags)
482 local function custom_hud(player)
483 if minetest.settings:get_bool("enable_damage") or hb.settings.forceload_default_hudbars then
484 local hide
485 if minetest.settings:get_bool("enable_damage") then
486 hide = false
487 else
488 hide = true
490 local hp = player:get_hp()
491 local hp_max = player:get_properties().hp_max
492 hb.init_hudbar(player, "health", math.min(hp, hp_max), hp_max, hide)
493 local breath = player:get_breath()
494 local breath_max = player:get_properties().breath_max
495 local hide_breath
496 if breath >= breath_max and hb.settings.autohide_breath == true then hide_breath = true else hide_breath = false end
497 hb.init_hudbar(player, "breath", math.min(breath, breath_max), breath_max, hide_breath or hide)
501 local function update_health(player)
502 local hp_max = player:get_properties().hp_max
503 hb.change_hudbar(player, "health", player:get_hp(), hp_max)
506 -- update built-in HUD bars
507 local function update_hud(player)
508 if not player_exists(player) then return end
509 if minetest.settings:get_bool("enable_damage") then
510 if hb.settings.forceload_default_hudbars then
511 hb.unhide_hudbar(player, "health")
513 --air
514 local breath_max = player:get_properties().breath_max
515 local breath = player:get_breath()
517 if breath >= breath_max and hb.settings.autohide_breath == true then
518 hb.hide_hudbar(player, "breath")
519 else
520 hb.unhide_hudbar(player, "breath")
521 hb.change_hudbar(player, "breath", math.min(breath, breath_max), breath_max)
523 --health
524 update_health(player)
525 elseif hb.settings.forceload_default_hudbars then
526 hb.hide_hudbar(player, "health")
527 hb.hide_hudbar(player, "breath")
531 minetest.register_on_player_hpchange(function(player)
532 if hb.players[player:get_player_name()] ~= nil then
533 update_health(player)
535 end)
537 minetest.register_on_respawnplayer(function(player)
538 update_health(player)
539 hb.hide_hudbar(player, "breath")
540 end)
542 minetest.register_on_joinplayer(function(player)
543 hide_builtin(player)
544 custom_hud(player)
545 hb.players[player:get_player_name()] = player
546 end)
548 minetest.register_on_leaveplayer(function(player)
549 hb.players[player:get_player_name()] = nil
550 end)
552 local main_timer = 0
553 local timer = 0
554 minetest.register_globalstep(function(dtime)
555 main_timer = main_timer + dtime
556 timer = timer + dtime
557 if main_timer > hb.settings.tick or timer > 4 then
558 if main_timer > hb.settings.tick then main_timer = 0 end
559 -- only proceed if damage is enabled
560 if minetest.settings:get_bool("enable_damage") or hb.settings.forceload_default_hudbars then
561 for _, player in pairs(hb.players) do
562 -- update all hud elements
563 update_hud(player)
567 if timer > 4 then timer = 0 end
568 end)