init.lua 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. local S = minetest.get_translator("hudbars")
  2. local N = function(s) return s end
  3. hb = {}
  4. hb.hudtables = {}
  5. -- number of registered HUD bars
  6. hb.hudbars_count = 0
  7. -- table which records which HUD bar slots have been “registered” so far; used for automatic positioning
  8. hb.registered_slots = {}
  9. hb.settings = {}
  10. function hb.load_setting(sname, stype, defaultval, valid_values)
  11. local sval
  12. if stype == "string" then
  13. sval = minetest.settings:get(sname)
  14. elseif stype == "bool" then
  15. sval = minetest.settings:get_bool(sname)
  16. elseif stype == "number" then
  17. sval = tonumber(minetest.settings:get(sname))
  18. end
  19. if sval ~= nil then
  20. if valid_values ~= nil then
  21. local valid = false
  22. for i=1,#valid_values do
  23. if sval == valid_values[i] then
  24. valid = true
  25. end
  26. end
  27. if not valid then
  28. minetest.log("error", "[hudbars] Invalid value for "..sname.."! Using default value ("..tostring(defaultval)..").")
  29. return defaultval
  30. else
  31. return sval
  32. end
  33. else
  34. return sval
  35. end
  36. else
  37. return defaultval
  38. end
  39. end
  40. -- Load default settings
  41. dofile(minetest.get_modpath("hudbars").."/default_settings.lua")
  42. local function player_exists(player)
  43. return player ~= nil and player:is_player()
  44. end
  45. local function make_label(format_string, format_string_config, label, start_value, max_value)
  46. local params = {}
  47. local order = format_string_config.order
  48. for o=1, #order do
  49. if order[o] == "label" then
  50. table.insert(params, label)
  51. elseif order[o] == "value" then
  52. if format_string_config.format_value then
  53. table.insert(params, string.format(format_string_config.format_value, start_value))
  54. else
  55. table.insert(params, start_value)
  56. end
  57. elseif order[o] == "max_value" then
  58. if format_string_config.format_max_value then
  59. table.insert(params, string.format(format_string_config.format_max_value, max_value))
  60. else
  61. table.insert(params, max_value)
  62. end
  63. end
  64. end
  65. local ret
  66. if format_string_config.textdomain then
  67. ret = minetest.translate(format_string_config.textdomain, format_string, unpack(params))
  68. else
  69. ret = S(format_string, unpack(params))
  70. end
  71. return ret
  72. end
  73. -- Table which contains all players with active default HUD bars (only for internal use)
  74. hb.players = {}
  75. function hb.value_to_barlength(value, max)
  76. if max == 0 then
  77. return 0
  78. else
  79. if hb.settings.bar_type == "progress_bar" then
  80. local x
  81. if value < 0 then x=-0.5 else x = 0.5 end
  82. local ret = math.modf((value/max) * hb.settings.max_bar_length + x)
  83. return ret
  84. else
  85. local x
  86. if value < 0 then x=-0.5 else x = 0.5 end
  87. local ret = math.modf((value/max) * hb.settings.statbar_length + x)
  88. return ret
  89. end
  90. end
  91. end
  92. function hb.get_hudtable(identifier)
  93. return hb.hudtables[identifier]
  94. end
  95. function hb.get_hudbar_position_index(identifier)
  96. if hb.settings.sorting[identifier] ~= nil then
  97. return hb.settings.sorting[identifier]
  98. else
  99. local i = 0
  100. while true do
  101. if hb.registered_slots[i] ~= true and hb.settings.sorting_reverse[i] == nil then
  102. return i
  103. end
  104. i = i + 1
  105. end
  106. end
  107. end
  108. function hb.register_hudbar(identifier, text_color, label, textures, default_start_value, default_start_max, default_start_hidden, format_string, format_string_config)
  109. minetest.log("action", "hb.register_hudbar: "..tostring(identifier))
  110. local hudtable = {}
  111. local pos, offset
  112. local index = math.floor(hb.get_hudbar_position_index(identifier))
  113. hb.registered_slots[index] = true
  114. if hb.settings.alignment_pattern == "stack_up" then
  115. pos = hb.settings.pos_left
  116. offset = {
  117. x = hb.settings.start_offset_left.x,
  118. y = hb.settings.start_offset_left.y - hb.settings.vmargin * index
  119. }
  120. elseif hb.settings.alignment_pattern == "stack_down" 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
  125. }
  126. else
  127. if index % 2 == 0 then
  128. pos = hb.settings.pos_left
  129. offset = {
  130. x = hb.settings.start_offset_left.x,
  131. y = hb.settings.start_offset_left.y - hb.settings.vmargin * (index/2)
  132. }
  133. else
  134. pos = hb.settings.pos_right
  135. offset = {
  136. x = hb.settings.start_offset_right.x,
  137. y = hb.settings.start_offset_right.y - hb.settings.vmargin * ((index-1)/2)
  138. }
  139. end
  140. end
  141. if format_string == nil then
  142. format_string = N("@1: @2/@3")
  143. end
  144. if format_string_config == nil then
  145. format_string_config = {}
  146. end
  147. if format_string_config.order == nil then
  148. format_string_config.order = { "label", "value", "max_value" }
  149. end
  150. if format_string_config.format_value == nil then
  151. format_string_config.format_value = "%d"
  152. end
  153. if format_string_config.format_max_value == nil then
  154. format_string_config.format_max_value = "%d"
  155. end
  156. hudtable.add_all = function(player, hudtable, start_value, start_max, start_hidden)
  157. if start_value == nil then start_value = hudtable.default_start_value end
  158. if start_max == nil then start_max = hudtable.default_start_max end
  159. if start_hidden == nil then start_hidden = hudtable.default_start_hidden end
  160. local ids = {}
  161. local state = {}
  162. local name = player:get_player_name()
  163. local bgscale, iconscale, text, barnumber, bgiconnumber
  164. if start_max == 0 or start_hidden then
  165. bgscale = { x=0, y=0 }
  166. else
  167. bgscale = { x=1, y=1 }
  168. end
  169. if start_hidden then
  170. iconscale = { x=0, y=0 }
  171. barnumber = 0
  172. bgiconnumber = 0
  173. text = ""
  174. else
  175. iconscale = { x=1, y=1 }
  176. barnumber = hb.value_to_barlength(start_value, start_max)
  177. bgiconnumber = hb.settings.statbar_length
  178. text = make_label(format_string, format_string_config, label, start_value, start_max)
  179. end
  180. if hb.settings.bar_type == "progress_bar" then
  181. ids.bg = player:hud_add({
  182. hud_elem_type = "image",
  183. position = pos,
  184. scale = bgscale,
  185. text = "hudbars_bar_background.png",
  186. alignment = {x=1,y=1},
  187. offset = { x = offset.x - 1, y = offset.y - 1 },
  188. z_index = 0,
  189. })
  190. if textures.icon ~= nil then
  191. ids.icon = player:hud_add({
  192. hud_elem_type = "image",
  193. position = pos,
  194. scale = iconscale,
  195. text = textures.icon,
  196. alignment = {x=-1,y=1},
  197. offset = { x = offset.x - 3, y = offset.y },
  198. z_index = 1,
  199. })
  200. end
  201. end
  202. local bar_image, bgicon, bar_size
  203. if hb.settings.bar_type == "progress_bar" then
  204. bar_image = textures.bar
  205. -- NOTE: Intentionally set to nil. For some reason, on some systems,
  206. -- the progress bar is displaced when the bar_size is set explicitly here.
  207. -- On the other hand, setting this to nil is deprecated in MT 5.0.0 due to
  208. -- a debug log warning, but nothing is explained in lua_api.txt.
  209. -- This section is a potential bug magnet, please watch with care!
  210. -- The size of the bar image is expected to be exactly 2×16 pixels.
  211. bar_size = nil
  212. elseif hb.settings.bar_type == "statbar_classic" or hb.settings.bar_type == "statbar_modern" then
  213. bar_image = textures.icon
  214. bgicon = textures.bgicon
  215. bar_size = {x=24, y=24}
  216. end
  217. ids.bar = player:hud_add({
  218. hud_elem_type = "statbar",
  219. position = pos,
  220. text = bar_image,
  221. text2 = bgicon,
  222. number = barnumber,
  223. item = bgiconnumber,
  224. alignment = {x=-1,y=-1},
  225. offset = offset,
  226. direction = 0,
  227. size = bar_size,
  228. z_index = 1,
  229. })
  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 - 1},
  239. z_index = 2,
  240. })
  241. end
  242. -- Do not forget to update hb.get_hudbar_state if you add new fields to the state table
  243. state.hidden = start_hidden
  244. state.value = start_value
  245. state.max = start_max
  246. state.text = text
  247. 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..". "
  250. if start_max < start_value then
  251. minetest.log("error", main_error_text.."start_max ("..start_max..") is smaller than start_value ("..start_value..")!")
  252. end
  253. if start_max < 0 then
  254. minetest.log("error", main_error_text.."start_max ("..start_max..") is smaller than 0!")
  255. end
  256. if start_value < 0 then
  257. minetest.log("error", main_error_text.."start_value ("..start_value..") is smaller than 0!")
  258. end
  259. hb.hudtables[identifier].hudids[name] = ids
  260. hb.hudtables[identifier].hudstate[name] = state
  261. end
  262. hudtable.identifier = identifier
  263. hudtable.format_string = format_string
  264. hudtable.format_string_config = format_string_config
  265. hudtable.label = label
  266. hudtable.hudids = {}
  267. hudtable.hudstate = {}
  268. hudtable.default_start_hidden = default_start_hidden
  269. hudtable.default_start_value = default_start_value
  270. hudtable.default_start_max = default_start_max
  271. hb.hudbars_count= hb.hudbars_count + 1
  272. hb.hudtables[identifier] = hudtable
  273. end
  274. function hb.init_hudbar(player, identifier, start_value, start_max, start_hidden)
  275. if not player_exists(player) then return false end
  276. local hudtable = hb.get_hudtable(identifier)
  277. hb.hudtables[identifier].add_all(player, hudtable, start_value, start_max, start_hidden)
  278. return true
  279. end
  280. function hb.change_hudbar(player, identifier, new_value, new_max_value, new_icon, new_bgicon, new_bar, new_label, new_text_color)
  281. 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
  282. return true
  283. end
  284. if not player_exists(player) then
  285. return false
  286. end
  287. local name = player:get_player_name()
  288. local hudtable = hb.get_hudtable(identifier)
  289. if not hudtable.hudstate[name] then
  290. return false
  291. end
  292. 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
  297. end
  298. else
  299. new_value = hudtable.hudstate[name].value
  300. end
  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
  305. end
  306. else
  307. new_max_value = hudtable.hudstate[name].max
  308. end
  309. if hb.settings.bar_type == "progress_bar" then
  310. if new_icon ~= nil and hudtable.hudids[name].icon ~= nil then
  311. player:hud_change(hudtable.hudids[name].icon, "text", new_icon)
  312. end
  313. if new_bgicon ~= nil and hudtable.hudids[name].bgicon ~= nil then
  314. player:hud_change(hudtable.hudids[name].bgicon, "text", new_bgicon)
  315. end
  316. if new_bar ~= nil then
  317. player:hud_change(hudtable.hudids[name].bar , "text", new_bar)
  318. end
  319. if new_label ~= nil then
  320. hudtable.label = new_label
  321. local new_text = make_label(hudtable.format_string, hudtable.format_string_config, new_label, hudtable.hudstate[name].value, hudtable.hudstate[name].max)
  322. player:hud_change(hudtable.hudids[name].text, "text", new_text)
  323. end
  324. if new_text_color ~= nil then
  325. player:hud_change(hudtable.hudids[name].text, "number", new_text_color)
  326. end
  327. else
  328. if new_icon ~= nil and hudtable.hudids[name].bar ~= nil then
  329. player:hud_change(hudtable.hudids[name].bar, "text", new_icon)
  330. end
  331. if new_bgicon ~= nil and hudtable.hudids[name].bg ~= nil then
  332. player:hud_change(hudtable.hudids[name].bg, "text", new_bgicon)
  333. end
  334. end
  335. local main_error_text =
  336. "[hudbars] Bad call to hb.change_hudbar, identifier: “"..tostring(identifier).."”, player name: “"..name.."”. "
  337. if new_max_value < new_value then
  338. minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than new_value ("..new_value..")!")
  339. end
  340. if new_max_value < 0 then
  341. minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than 0!")
  342. end
  343. if new_value < 0 then
  344. minetest.log("error", main_error_text.."new_value ("..new_value..") is smaller than 0!")
  345. end
  346. if hudtable.hudstate[name].hidden == false then
  347. if max_changed and hb.settings.bar_type == "progress_bar" then
  348. if hudtable.hudstate[name].max == 0 then
  349. player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
  350. else
  351. player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
  352. end
  353. end
  354. if value_changed or max_changed then
  355. local new_barlength = hb.value_to_barlength(new_value, new_max_value)
  356. if new_barlength ~= hudtable.hudstate[name].barlength then
  357. player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(new_value, new_max_value))
  358. hudtable.hudstate[name].barlength = new_barlength
  359. end
  360. if hb.settings.bar_type == "progress_bar" then
  361. local new_text = make_label(hudtable.format_string, hudtable.format_string_config, hudtable.label, new_value, new_max_value)
  362. if new_text ~= hudtable.hudstate[name].text then
  363. player:hud_change(hudtable.hudids[name].text, "text", new_text)
  364. hudtable.hudstate[name].text = new_text
  365. end
  366. end
  367. end
  368. end
  369. return true
  370. end
  371. function hb.hide_hudbar(player, identifier)
  372. if not player_exists(player) then return false end
  373. local name = player:get_player_name()
  374. local hudtable = hb.get_hudtable(identifier)
  375. if hudtable == nil then return false end
  376. if hudtable.hudstate[name].hidden == true then return true end
  377. if hb.settings.bar_type == "progress_bar" then
  378. if hudtable.hudids[name].icon ~= nil then
  379. player:hud_change(hudtable.hudids[name].icon, "scale", {x=0,y=0})
  380. end
  381. player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
  382. player:hud_change(hudtable.hudids[name].text, "text", "")
  383. end
  384. player:hud_change(hudtable.hudids[name].bar, "number", 0)
  385. player:hud_change(hudtable.hudids[name].bar, "item", 0)
  386. hudtable.hudstate[name].hidden = true
  387. return true
  388. end
  389. function hb.unhide_hudbar(player, identifier)
  390. if not player_exists(player) then return false end
  391. local name = player:get_player_name()
  392. local hudtable = hb.get_hudtable(identifier)
  393. if hudtable == nil then return false end
  394. if hudtable.hudstate[name].hidden == false then return true end
  395. local value = hudtable.hudstate[name].value
  396. local max = hudtable.hudstate[name].max
  397. if hb.settings.bar_type == "progress_bar" then
  398. if hudtable.hudids[name].icon ~= nil then
  399. player:hud_change(hudtable.hudids[name].icon, "scale", {x=1,y=1})
  400. end
  401. if hudtable.hudstate[name].max ~= 0 then
  402. player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
  403. end
  404. player:hud_change(hudtable.hudids[name].text, "text", make_label(hudtable.format_string, hudtable.format_string_config, hudtable.label, value, max))
  405. elseif hb.settings.bar_type == "statbar_modern" then
  406. player:hud_change(hudtable.hudids[name].bar, "scale", {x=1,y=1})
  407. end
  408. player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(value, max))
  409. player:hud_change(hudtable.hudids[name].bar, "item", hb.value_to_barlength(max, max))
  410. hudtable.hudstate[name].hidden = false
  411. return true
  412. end
  413. function hb.get_hudbar_state(player, identifier)
  414. if not player_exists(player) then return nil end
  415. local ref = hb.get_hudtable(identifier).hudstate[player:get_player_name()]
  416. -- Do not forget to update this chunk of code in case the state changes
  417. local copy = {
  418. hidden = ref.hidden,
  419. value = ref.value,
  420. max = ref.max,
  421. text = ref.text,
  422. barlength = ref.barlength,
  423. }
  424. return copy
  425. end
  426. function hb.get_hudbar_identifiers()
  427. local ids = {}
  428. for id, _ in pairs(hb.hudtables) do
  429. table.insert(ids, id)
  430. end
  431. return ids
  432. end
  433. --register built-in HUD bars
  434. if minetest.settings:get_bool("enable_damage") or hb.settings.forceload_default_hudbars then
  435. 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)
  436. 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)
  437. end
  438. local function hide_builtin(player)
  439. local flags = player:hud_get_flags()
  440. flags.healthbar = false
  441. flags.breathbar = false
  442. player:hud_set_flags(flags)
  443. end
  444. local function custom_hud(player)
  445. if minetest.settings:get_bool("enable_damage") or hb.settings.forceload_default_hudbars then
  446. local hide
  447. if minetest.settings:get_bool("enable_damage") then
  448. hide = false
  449. else
  450. hide = true
  451. end
  452. local hp = player:get_hp()
  453. local hp_max = player:get_properties().hp_max
  454. hb.init_hudbar(player, "health", math.min(hp, hp_max), hp_max, hide)
  455. local breath = player:get_breath()
  456. local breath_max = player:get_properties().breath_max
  457. local hide_breath
  458. if breath >= breath_max and hb.settings.autohide_breath == true then hide_breath = true else hide_breath = false end
  459. hb.init_hudbar(player, "breath", math.min(breath, breath_max), breath_max, hide_breath or hide)
  460. end
  461. end
  462. local function update_health(player)
  463. local hp_max = player:get_properties().hp_max
  464. local hp = math.min(player:get_hp(), hp_max)
  465. hb.change_hudbar(player, "health", hp, hp_max)
  466. end
  467. -- update built-in HUD bars
  468. local function update_hud(player)
  469. if not player_exists(player) then return end
  470. if minetest.settings:get_bool("enable_damage") then
  471. if hb.settings.forceload_default_hudbars then
  472. hb.unhide_hudbar(player, "health")
  473. end
  474. --air
  475. local breath_max = player:get_properties().breath_max
  476. local breath = player:get_breath()
  477. if breath >= breath_max and hb.settings.autohide_breath == true then
  478. hb.hide_hudbar(player, "breath")
  479. else
  480. hb.unhide_hudbar(player, "breath")
  481. hb.change_hudbar(player, "breath", math.min(breath, breath_max), breath_max)
  482. end
  483. --health
  484. update_health(player)
  485. elseif hb.settings.forceload_default_hudbars then
  486. hb.hide_hudbar(player, "health")
  487. hb.hide_hudbar(player, "breath")
  488. end
  489. end
  490. minetest.register_on_player_hpchange(function(player)
  491. if hb.players[player:get_player_name()] ~= nil then
  492. update_health(player)
  493. end
  494. end)
  495. minetest.register_on_respawnplayer(function(player)
  496. update_health(player)
  497. hb.hide_hudbar(player, "breath")
  498. end)
  499. minetest.register_on_joinplayer(function(player)
  500. hide_builtin(player)
  501. custom_hud(player)
  502. hb.players[player:get_player_name()] = player
  503. end)
  504. minetest.register_on_leaveplayer(function(player)
  505. hb.players[player:get_player_name()] = nil
  506. end)
  507. local main_timer = 0
  508. local timer = 0
  509. minetest.register_globalstep(function(dtime)
  510. main_timer = main_timer + dtime
  511. timer = timer + dtime
  512. if main_timer > hb.settings.tick or timer > 4 then
  513. if main_timer > hb.settings.tick then main_timer = 0 end
  514. -- only proceed if damage is enabled
  515. if minetest.settings:get_bool("enable_damage") or hb.settings.forceload_default_hudbars then
  516. for _, player in pairs(hb.players) do
  517. -- update all hud elements
  518. update_hud(player)
  519. end
  520. end
  521. end
  522. if timer > 4 then timer = 0 end
  523. end)