functions.lua 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. -- read/write
  2. function hunger.read(player)
  3. local inv = player:get_inventory()
  4. if not inv then
  5. return nil
  6. end
  7. local hgp = inv:get_stack("hunger", 1):get_count()
  8. if hgp == 0 then
  9. hgp = 21
  10. inv:set_stack("hunger", 1, ItemStack({name = ":", count = hgp}))
  11. else
  12. hgp = hgp
  13. end
  14. if tonumber(hgp) > HUNGER_MAX + 1 then
  15. hgp = HUNGER_MAX + 1
  16. end
  17. return hgp - 1
  18. end
  19. function hunger.save(player)
  20. local inv = player:get_inventory()
  21. local name = player:get_player_name()
  22. local value = hunger[name].lvl
  23. if not inv or not value then
  24. return nil
  25. end
  26. if value > HUNGER_MAX then
  27. value = HUNGER_MAX
  28. end
  29. if value < 0 then
  30. value = 0
  31. end
  32. inv:set_stack("hunger", 1, ItemStack({name = ":", count = value + 1}))
  33. return true
  34. end
  35. function hunger.update_hunger(player, new_lvl)
  36. local name = player:get_player_name() or nil
  37. if not name then
  38. return false
  39. end
  40. if minetest.setting_getbool("enable_damage") == false then
  41. hunger[name] = 20
  42. return
  43. end
  44. local lvl = hunger[name].lvl
  45. if new_lvl then
  46. lvl = new_lvl
  47. end
  48. if lvl > HUNGER_MAX then
  49. lvl = HUNGER_MAX
  50. end
  51. hunger[name].lvl = lvl
  52. if lvl > 20 then
  53. lvl = 20
  54. end
  55. hud.change_item(player, "hunger", {number = lvl})
  56. hunger.save(player)
  57. end
  58. local update_hunger = hunger.update_hunger
  59. -- player-action based hunger changes
  60. function hunger.handle_node_actions(pos, oldnode, player, ext)
  61. if not player or not player:is_player() then
  62. return
  63. end
  64. local name = player:get_player_name()
  65. if not name or not hunger[name] then
  66. return
  67. end
  68. local exhaus = hunger[name].exhaus
  69. if not exhaus then
  70. hunger[name].exhaus = 0
  71. --return
  72. end
  73. local new = HUNGER_EXHAUST_PLACE
  74. -- placenode event
  75. if not ext then
  76. new = HUNGER_EXHAUST_DIG
  77. end
  78. -- assume its send by action_timer(globalstep)
  79. if not pos and not oldnode then
  80. new = HUNGER_EXHAUST_MOVE
  81. end
  82. exhaus = exhaus + new
  83. if exhaus > HUNGER_EXHAUST_LVL then
  84. exhaus = 0
  85. local h = tonumber(hunger[name].lvl)
  86. if h > 0 then
  87. update_hunger(player, h - 1)
  88. end
  89. end
  90. hunger[name].exhaus = exhaus
  91. end
  92. -- Time based hunger functions
  93. local hunger_timer = 0
  94. local health_timer = 0
  95. local action_timer = 0
  96. local function hunger_globaltimer(dtime)
  97. hunger_timer = hunger_timer + dtime
  98. health_timer = health_timer + dtime
  99. action_timer = action_timer + dtime
  100. if action_timer > HUNGER_MOVE_TICK then
  101. for _,player in ipairs(minetest.get_connected_players()) do
  102. local controls = player:get_player_control()
  103. -- Determine if the player is walking
  104. if controls.up or controls.down or controls.left or controls.right then
  105. hunger.handle_node_actions(nil, nil, player)
  106. end
  107. end
  108. action_timer = 0
  109. end
  110. -- lower saturation by 1 point after <HUNGER_TICK> second(s)
  111. if hunger_timer > HUNGER_TICK then
  112. for _,player in ipairs(minetest.get_connected_players()) do
  113. local name = player:get_player_name()
  114. local tab = hunger[name]
  115. if tab then
  116. local hunger = tab.lvl
  117. if hunger > 0 then
  118. update_hunger(player, hunger - 1)
  119. end
  120. end
  121. end
  122. hunger_timer = 0
  123. end
  124. -- heal or damage player, depending on saturation
  125. if health_timer > HUNGER_HEALTH_TICK then
  126. for _,player in ipairs(minetest.get_connected_players()) do
  127. local name = player:get_player_name()
  128. local tab = hunger[name]
  129. if tab then
  130. -- local air = player:get_breath() or 0
  131. local hp = player:get_hp()
  132. -- heal player by 1 hp if not dead and saturation is > 15 (of 30) player is not drowning
  133. -- if tonumber(tab.lvl) > HUNGER_HEAL_LVL and hp > 0 and air > 0 then
  134. -- player:set_hp(hp + HUNGER_HEAL)
  135. -- end
  136. -- or damage player by 1 hp if saturation is < 2 (of 30)
  137. if tonumber(tab.lvl) < HUNGER_STARVE_LVL then
  138. player:set_hp(hp - HUNGER_STARVE)
  139. end
  140. end
  141. end
  142. health_timer = 0
  143. end
  144. end
  145. if minetest.setting_getbool("enable_damage") then
  146. minetest.register_globalstep(hunger_globaltimer)
  147. end
  148. -- food functions
  149. local food = hunger.food
  150. function hunger.register_food(name, hunger_change, replace_with_item, poisen, heal, sound)
  151. food[name] = {}
  152. food[name].saturation = hunger_change -- hunger points added
  153. food[name].replace = replace_with_item -- what item is given back after eating
  154. food[name].poisen = poisen -- time its poisening
  155. food[name].healing = heal -- amount of HP
  156. food[name].sound = sound -- special sound that is played when eating
  157. end
  158. -- Poison player
  159. local function poisenp(tick, time, time_left, player)
  160. time_left = time_left + tick
  161. if time_left < time then
  162. minetest.after(tick, poisenp, tick, time, time_left, player)
  163. else
  164. hud.change_item(player, "hunger", {text = "hud_hunger_fg.png"})
  165. end
  166. local hp = player:get_hp() -1 or 0
  167. if hp > 0 then
  168. player:set_hp(hp)
  169. end
  170. end
  171. -- wrapper for minetest.item_eat (this way we make sure other mods can't break this one)
  172. local org_eat = core.do_item_eat
  173. core.do_item_eat = function(hp_change, replace_with_item, itemstack, user, pointed_thing)
  174. local old_itemstack = itemstack
  175. itemstack = hunger.eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
  176. for _, callback in pairs(core.registered_on_item_eats) do
  177. local result = callback(hp_change, replace_with_item, itemstack, user, pointed_thing, old_itemstack)
  178. if result then
  179. return result
  180. end
  181. end
  182. return itemstack
  183. end
  184. function hunger.eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
  185. local item = itemstack:get_name()
  186. local def = food[item]
  187. if not def then
  188. def = {}
  189. def.saturation = hp_change * 1.3
  190. def.replace = replace_with_item
  191. end
  192. local func = hunger.item_eat(def.saturation, def.replace, def.poisen, def.healing, def.sound)
  193. return func(itemstack, user, pointed_thing)
  194. end
  195. function hunger.item_eat(hunger_change, replace_with_item, poisen, heal, sound)
  196. return function(itemstack, user, pointed_thing)
  197. if itemstack:take_item() ~= nil and user ~= nil then
  198. local name = user:get_player_name()
  199. local sat = tonumber(hunger[name].lvl)
  200. local hp = user:get_hp()
  201. -- Saturation
  202. if sat < HUNGER_MAX and hunger_change then
  203. sat = sat + hunger_change
  204. hunger.update_hunger(user, sat)
  205. end
  206. -- Healing
  207. if hp < 20 and heal then
  208. hp = hp + heal
  209. if hp > 20 then
  210. hp = 20
  211. end
  212. user:set_hp(hp)
  213. end
  214. -- Poison
  215. if poisen then
  216. hud.change_item(user, "hunger", {text = "hunger_statbar_poisen.png"})
  217. poisenp(1.0, poisen, 0, user)
  218. end
  219. -- eating sound
  220. if not sound then
  221. sound = "hunger_eat"
  222. end
  223. minetest.sound_play(sound, {to_player = name, gain = 0.7})
  224. if replace_with_item then
  225. if itemstack:is_empty() then
  226. itemstack:add_item(replace_with_item)
  227. else
  228. local inv = user:get_inventory()
  229. if inv:room_for_item("main", {name=replace_with_item}) then
  230. inv:add_item("main", replace_with_item)
  231. else
  232. local pos = user:getpos()
  233. pos.y = math.floor(pos.y + 0.5)
  234. core.add_item(pos, replace_with_item)
  235. end
  236. end
  237. end
  238. end
  239. return itemstack
  240. end
  241. end