Tweak breath icons
[minetest_hudbars.git] / init.lua
blob031ccda3eaa0801fa9edbdefe458faf8d485badc
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 - 4, y = offset.y + 2 },
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 hb.settings.bar_type == "progress_bar" then
408 if hudtable.hudids[name].icon ~= nil then
409 player:hud_change(hudtable.hudids[name].icon, "scale", {x=0,y=0})
411 player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
412 player:hud_change(hudtable.hudids[name].text, "text", "")
414 player:hud_change(hudtable.hudids[name].bar, "number", 0)
415 player:hud_change(hudtable.hudids[name].bar, "item", 0)
416 hudtable.hudstate[name].hidden = true
417 return true
420 function hb.unhide_hudbar(player, identifier)
421 if not player_exists(player) then return false end
422 local name = player:get_player_name()
423 local hudtable = hb.get_hudtable(identifier)
424 if hudtable == nil then return false end
425 local value = hudtable.hudstate[name].value
426 local max = hudtable.hudstate[name].max
427 if hb.settings.bar_type == "progress_bar" then
428 if hudtable.hudids[name].icon ~= nil then
429 player:hud_change(hudtable.hudids[name].icon, "scale", {x=1,y=1})
431 if hudtable.hudstate[name].max ~= 0 then
432 player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
434 player:hud_change(hudtable.hudids[name].text, "text", make_label(hudtable.format_string, hudtable.format_string_config, hudtable.label, value, max))
435 elseif hb.settings.bar_type == "statbar_modern" then
436 player:hud_change(hudtable.hudids[name].bar, "scale", {x=1,y=1})
438 player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(value, max))
439 player:hud_change(hudtable.hudids[name].bar, "item", hb.value_to_barlength(max, max))
440 hudtable.hudstate[name].hidden = false
441 return true
444 function hb.get_hudbar_state(player, identifier)
445 if not player_exists(player) then return nil end
446 local ref = hb.get_hudtable(identifier).hudstate[player:get_player_name()]
447 -- Do not forget to update this chunk of code in case the state changes
448 local copy = {
449 hidden = ref.hidden,
450 value = ref.value,
451 max = ref.max,
452 text = ref.text,
453 barlength = ref.barlength,
455 return copy
458 function hb.get_hudbar_identifiers()
459 local ids = {}
460 for id, _ in pairs(hb.hudtables) do
461 table.insert(ids, id)
463 return ids
466 --register built-in HUD bars
467 if minetest.settings:get_bool("enable_damage") or hb.settings.forceload_default_hudbars then
468 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)
469 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)
472 local function hide_builtin(player)
473 local flags = player:hud_get_flags()
474 flags.healthbar = false
475 flags.breathbar = false
476 player:hud_set_flags(flags)
480 local function custom_hud(player)
481 if minetest.settings:get_bool("enable_damage") or hb.settings.forceload_default_hudbars then
482 local hide
483 if minetest.settings:get_bool("enable_damage") then
484 hide = false
485 else
486 hide = true
488 local hp = player:get_hp()
489 local hp_max = player:get_properties().hp_max
490 hb.init_hudbar(player, "health", math.min(hp, hp_max), hp_max, hide)
491 local breath = player:get_breath()
492 local breath_max = player:get_properties().breath_max
493 local hide_breath
494 if breath >= breath_max and hb.settings.autohide_breath == true then hide_breath = true else hide_breath = false end
495 hb.init_hudbar(player, "breath", math.min(breath, breath_max), breath_max, hide_breath or hide)
499 local function update_health(player)
500 local hp_max = player:get_properties().hp_max
501 hb.change_hudbar(player, "health", player:get_hp(), hp_max)
504 -- update built-in HUD bars
505 local function update_hud(player)
506 if not player_exists(player) then return end
507 if minetest.settings:get_bool("enable_damage") then
508 if hb.settings.forceload_default_hudbars then
509 hb.unhide_hudbar(player, "health")
511 --air
512 local breath_max = player:get_properties().breath_max
513 local breath = player:get_breath()
515 if breath >= breath_max and hb.settings.autohide_breath == true then
516 hb.hide_hudbar(player, "breath")
517 else
518 hb.unhide_hudbar(player, "breath")
519 hb.change_hudbar(player, "breath", math.min(breath, breath_max), breath_max)
521 --health
522 update_health(player)
523 elseif hb.settings.forceload_default_hudbars then
524 hb.hide_hudbar(player, "health")
525 hb.hide_hudbar(player, "breath")
529 minetest.register_on_player_hpchange(function(player)
530 if hb.players[player:get_player_name()] ~= nil then
531 update_health(player)
533 end)
535 minetest.register_on_respawnplayer(function(player)
536 update_health(player)
537 hb.hide_hudbar(player, "breath")
538 end)
540 minetest.register_on_joinplayer(function(player)
541 hide_builtin(player)
542 custom_hud(player)
543 hb.players[player:get_player_name()] = player
544 end)
546 minetest.register_on_leaveplayer(function(player)
547 hb.players[player:get_player_name()] = nil
548 end)
550 local main_timer = 0
551 local timer = 0
552 minetest.register_globalstep(function(dtime)
553 main_timer = main_timer + dtime
554 timer = timer + dtime
555 if main_timer > hb.settings.tick or timer > 4 then
556 if main_timer > hb.settings.tick then main_timer = 0 end
557 -- only proceed if damage is enabled
558 if minetest.settings:get_bool("enable_damage") or hb.settings.forceload_default_hudbars then
559 for _, player in pairs(hb.players) do
560 -- update all hud elements
561 update_hud(player)
565 if timer > 4 then timer = 0 end
566 end)