init.lua 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. if not minetest.global_exists("xp") then xp = {} end
  2. xp.modpath = minetest.get_modpath("xp")
  3. xp.digxp_max = 1000000
  4. xp.digxp_hp_max = 50000
  5. xp.data = xp.data or {} -- Data is stored in string form.
  6. xp.dirty = true
  7. xp.dirty_players = xp.dirty_players or {}
  8. -- Localize for performance.
  9. local math_floor = math.floor
  10. local math_min = math.min
  11. local math_max = math.max
  12. -- This code supports multiple types of XP.
  13. -- Different types of XP are stored seperately.
  14. function xp.set_xp(pname, kind, amount)
  15. local key = pname .. ":" .. kind
  16. xp.data[key] = tostring(amount)
  17. xp.dirty = true
  18. xp.dirty_players[pname] = true
  19. end
  20. function xp.get_xp(pname, kind)
  21. local key = pname .. ":" .. kind
  22. if not xp.data[key] then
  23. return 0
  24. end
  25. return tonumber(xp.data[key])
  26. end
  27. -- 500 internal hp = 1 player-visible HP.
  28. --
  29. -- Note: max hp gain from XP must be no greater than 40000.
  30. -- Add 10000 which is the base hp, and we get 50000 hp, which is 100 HP.
  31. -- Adding 15000 to that brings us to 65000 hp, which is near the maximum the
  32. -- engine allows us; a total of 130 HP, obtainable with the health boost item.
  33. -- Unfortunately, this means that a bunch of people who used to have 120 HP
  34. -- just from the XP are now suddenly down 20 HP ... and they're probably not
  35. -- going to be happy.
  36. --
  37. -- The upside is that the health boost drink gives a fixed amount of HP, and is
  38. -- available much sooner than high XP normally is. Hope it's worth it.
  39. --
  40. -- Note: this function must return HP from XP ONLY, without taking any other
  41. -- factors into account, esp. NOT health boosts.
  42. function xp.get_hp_max(pname)
  43. local amount = math_max(math_min(xp.get_xp(pname, "digxp"), xp.digxp_hp_max), 0)
  44. local hpinc = math_floor(amount / 620)
  45. local scale = 500
  46. local max_hp = math_floor((minetest.PLAYER_MAX_HP_DEFAULT + hpinc) * scale)
  47. return max_hp
  48. end
  49. function xp.update_players_max_hp(pname, login)
  50. local pref = minetest.get_player_by_name(pname)
  51. if not pref then
  52. return
  53. end
  54. local pmeta = pref:get_meta()
  55. local max_hp = pova.get_active_modifier(pref, "properties").hp_max
  56. local cur_hp = pref:get_hp()
  57. if login then
  58. -- Get stored values.
  59. max_hp = pmeta:get_int("hp_max")
  60. cur_hp = pmeta:get_int("hp_cur")
  61. -- Should only happen for new players (and existing that don't have 'hp_max'
  62. -- in their meta info yet).
  63. if max_hp == 0 or cur_hp == 0 then
  64. max_hp = minetest.PLAYER_MAX_HP_DEFAULT
  65. cur_hp = max_hp
  66. end
  67. end
  68. local percent = (cur_hp / max_hp)
  69. if percent > 1 then percent = 1 end
  70. local new_max_hp = xp.get_hp_max(pname)
  71. local new_hp = math_min((percent * new_max_hp), new_max_hp)
  72. -- Note: 'hp_max' must be manually stored in player meta, because Minetest
  73. -- does not store this itself, and reverts to HP_MAX=20 on every login. The
  74. -- same logic applies to 'hp_cur', which we must keep track of ourselves.
  75. --
  76. -- Note: HP max must be set *before* HP update!
  77. -- Otherwise set_hp() will be ignored if hp is higher than existing max!
  78. --
  79. -- Note: must manually notify the HP change reason, here.
  80. pova.set_modifier(pref, "properties", {hp_max = new_max_hp}, "xphp")
  81. armor.notify_set_hp_reason({reason="xp_update"})
  82. pref:set_hp(new_hp)
  83. pmeta:set_int("hp_max", new_max_hp)
  84. -- Data 'hp_cur' will be updated when player leaves.
  85. -- Manually update HUD.
  86. hud.player_event(pref, "health_changed")
  87. end
  88. function xp.on_joinplayer(player)
  89. minetest.after(0, xp.update_players_max_hp, player:get_player_name(), true)
  90. end
  91. function xp.on_leaveplayer(player)
  92. local meta = player:get_meta()
  93. meta:set_int("hp_cur", player:get_hp())
  94. end
  95. function xp.write_xp()
  96. if xp.dirty then
  97. local temp = {}
  98. for k, v in pairs(xp.data) do
  99. -- Only save XP if >= min XP.
  100. local n = tonumber(v)
  101. if n >= 5.0 then
  102. temp[k] = v
  103. end
  104. end
  105. xp.storage:from_table({fields=temp})
  106. end
  107. xp.dirty = false
  108. end
  109. local timer = 0
  110. local delay = 60*5
  111. function xp.globalstep(dtime)
  112. timer = timer + dtime
  113. if timer >= delay then
  114. timer = 0
  115. xp.write_xp()
  116. local done_players = {}
  117. for k, v in pairs(xp.dirty_players) do
  118. xp.update_players_max_hp(k)
  119. done_players[k] = true
  120. end
  121. for k, v in pairs(done_players) do
  122. xp.dirty_players[k] = nil
  123. end
  124. end
  125. end
  126. if not xp.run_once then
  127. xp.storage = minetest.get_mod_storage()
  128. -- Load data.
  129. local data = xp.storage:to_table() or {}
  130. xp.data = data.fields or {}
  131. -- Save data.
  132. minetest.register_on_shutdown(function() xp.write_xp() end)
  133. minetest.register_globalstep(function(...) xp.globalstep(...) end)
  134. minetest.register_on_joinplayer(function(...) xp.on_joinplayer(...) end)
  135. minetest.register_on_leaveplayer(function(...) xp.on_leaveplayer(...) end)
  136. local c = "xp:core"
  137. local f = xp.modpath .. "/init.lua"
  138. reload.register_file(c, f, false)
  139. xp.run_once = true
  140. end