2 if (minetest
.get_modpath("intllib")) then
5 S
= function ( s
) return s
end
11 -- This variable tells you if the hunger gameplay mechanic is active.
12 -- IMPORTANT: If damage is disabled on load time, most of the functions are NOT
13 -- available! Check if mcl_hunger is active before using the API.
14 mcl_hunger
.active
= false
17 mcl_hunger
.exhaust
= function() end
19 mcl_hunger
.HUD_TICK
= 0.1
21 -- Exhaustion increase
22 mcl_hunger
.EXHAUST_DIG
= 5 -- after digging node
23 mcl_hunger
.EXHAUST_JUMP
= 50 -- jump
24 mcl_hunger
.EXHAUST_SPRINT_JUMP
= 200 -- jump while sprinting
25 mcl_hunger
.EXHAUST_ATTACK
= 100 -- hit an enemy
26 mcl_hunger
.EXHAUST_SWIM
= 10 -- player movement in water
27 mcl_hunger
.EXHAUST_SPRINT
= 100 -- sprint (per node)
28 mcl_hunger
.EXHAUST_DAMAGE
= 100 -- TODO (mostly done): taking damage (protected by armor)
29 mcl_hunger
.EXHAUST_REGEN
= 6000 -- Regenerate 1 HP
30 mcl_hunger
.EXHAUST_LVL
= 4000 -- at what exhaustion player saturation gets lowered
32 mcl_hunger
.SATURATION_INIT
= 5 -- Initial saturation for new/respawning players
34 if minetest
.settings
:get_bool("enable_damage") then
35 mcl_hunger
.active
= true
37 -- Debug Mode. If enabled, saturation and exhaustion are shown as well.
38 -- NOTE: Read-only. The setting should only be read at the beginning, this mod is not
39 -- prepared to change this setting later.
40 mcl_hunger
.debug
= minetest
.settings
:get_bool("mcl_hunger_debug")
41 if mcl_hunger
.debug
== nil then
42 mcl_hunger
.debug
= false
45 --[[ Data value format notes:
46 Hunger values is identical to Minecraft's and ranges from 0 to 20.
47 Exhaustion and saturation values are stored as integers, unlike in Minecraft.
48 Exhaustion is Minecraft exhaustion times 1000 and ranges from 0 to 4000.
49 Saturation is Minecraft saturation and ranges from 0 to 20.
51 Food saturation is stored in the custom item definition field _mcl_saturation.
52 This field uses the original Minecraft value.
55 -- Count number of poisonings a player has at once
56 mcl_hunger
.poison_damage
= {} -- damaging poison
57 mcl_hunger
.poison_hunger
= {} -- food poisoning, increasing hunger
59 -- Cooldown timers for each player, to force a short delay between consuming 2 food items
60 mcl_hunger
.last_eat
= {}
65 local function init_hud(player
)
66 hb
.init_hudbar(player
, "hunger", mcl_hunger
.get_hunger(player
))
67 if mcl_hunger
.debug
then
68 hb
.init_hudbar(player
, "saturation", mcl_hunger
.get_saturation(player
), mcl_hunger
.get_hunger(player
))
69 hb
.init_hudbar(player
, "exhaustion", mcl_hunger
.get_exhaustion(player
))
73 -- HUD updating functions for Debug Mode. No-op if not in Debug Mode
74 function mcl_hunger
.update_saturation_hud(player
, saturation
, hunger
)
75 if mcl_hunger
.debug
then
76 hb
.change_hudbar(player
, "saturation", saturation
, hunger
)
79 function mcl_hunger
.update_exhaustion_hud(player
, exhaustion
)
80 if mcl_hunger
.debug
then
81 hb
.change_hudbar(player
, "exhaustion", exhaustion
)
85 dofile(minetest
.get_modpath("mcl_hunger").."/hunger.lua")
87 -- register saturation hudbar
88 hb
.register_hudbar("hunger", 0xFFFFFF, S("Food"), { icon
= "hbhunger_icon.png", bgicon
= "hbhunger_bgicon.png", bar
= "hbhunger_bar.png" }, 20, 20, false)
89 if mcl_hunger
.debug
then
90 hb
.register_hudbar("saturation", 0xFFFFFF, S("Saturation"), { icon
= "mcl_hunger_icon_saturation.png", bgicon
= "mcl_hunger_bgicon_saturation.png", bar
= "mcl_hunger_bar_saturation.png" }, mcl_hunger
.SATURATION_INIT
, 200, false, S("%s: %.1f/%d"))
91 hb
.register_hudbar("exhaustion", 0xFFFFFF, S("Exhaust."), { icon
= "mcl_hunger_icon_exhaustion.png", bgicon
= "mcl_hunger_bgicon_exhaustion.png", bar
= "mcl_hunger_bar_exhaustion.png" }, 0, mcl_hunger
.EXHAUST_LVL
, false, S("%s: %d/%d"))
95 mcl_hunger
.get_hunger
= function(player
)
96 local hunger
= player
:get_attribute("mcl_hunger:hunger") or 20
97 return tonumber(hunger
)
100 mcl_hunger
.get_saturation
= function(player
)
101 local saturation
= player
:get_attribute("mcl_hunger:saturation") or mcl_hunger
.SATURATION_INIT
102 return tonumber(saturation
)
105 mcl_hunger
.get_exhaustion
= function(player
)
106 local exhaustion
= player
:get_attribute("mcl_hunger:exhaustion") or 0
107 return tonumber(exhaustion
)
110 mcl_hunger
.set_hunger
= function(player
, hunger
, update_hudbars
)
111 hunger
= math
.min(20, math
.max(0, hunger
))
112 player
:set_attribute("mcl_hunger:hunger", tostring(hunger
))
113 if update_hudbars
~= false then
114 hb
.change_hudbar(player
, "hunger", hunger
)
115 mcl_hunger
.update_saturation_hud(player
, nil, hunger
)
120 mcl_hunger
.set_saturation
= function(player
, saturation
, update_hudbar
)
121 saturation
= math
.min(mcl_hunger
.get_hunger(player
), math
.max(0, saturation
))
122 player
:set_attribute("mcl_hunger:saturation", tostring(saturation
))
123 if update_hudbar
~= false then
124 mcl_hunger
.update_saturation_hud(player
, saturation
)
129 mcl_hunger
.set_exhaustion
= function(player
, exhaustion
, update_hudbar
)
130 exhaustion
= math
.min(mcl_hunger
.EXHAUST_LVL
, math
.max(0.0, exhaustion
))
131 player
:set_attribute("mcl_hunger:exhaustion", tostring(exhaustion
))
132 if update_hudbar
~= false then
133 mcl_hunger
.update_exhaustion_hud(player
, exhaustion
)
141 minetest
.register_on_newplayer(function(player
)
142 local name
= player
:get_player_name()
143 mcl_hunger
.set_hunger(player
, 20, false)
144 mcl_hunger
.set_saturation(player
, mcl_hunger
.SATURATION_INIT
, false)
145 mcl_hunger
.set_exhaustion(player
, 0, false)
148 minetest
.register_on_joinplayer(function(player
)
149 local name
= player
:get_player_name()
151 mcl_hunger
.poison_damage
[name
] = 0
152 mcl_hunger
.poison_hunger
[name
] = 0
153 mcl_hunger
.last_eat
[name
] = -1
156 minetest
.register_on_respawnplayer(function(player
)
157 -- reset hunger, related values and poison
158 local name
= player
:get_player_name()
160 mcl_hunger
.stop_poison(player
)
161 mcl_hunger
.last_eat
[name
] = -1
163 local h
, s
, e
= 20, mcl_hunger
.SATURATION_INIT
, 0
164 mcl_hunger
.set_hunger(player
, h
, false)
165 mcl_hunger
.set_saturation(player
, s
, false)
166 mcl_hunger
.set_exhaustion(player
, e
, false)
167 hb
.change_hudbar(player
, "hunger", h
)
168 mcl_hunger
.update_saturation_hud(player
, s
, h
)
169 mcl_hunger
.update_exhaustion_hud(player
, e
)
172 -- PvP combat exhaustion
173 minetest
.register_on_punchplayer(function(victim
, puncher
, time_from_last_punch
, tool_capabilities
, dir
, damage
)
174 if victim
:is_player() and puncher
:is_player() then
175 mcl_hunger
.exhaust(victim
:get_player_name(), mcl_hunger
.EXHAUST_DAMAGE
)
176 mcl_hunger
.exhaust(puncher
:get_player_name(), mcl_hunger
.EXHAUST_ATTACK
)
180 function mcl_hunger
.exhaust(playername
, increase
)
181 local player
= minetest
.get_player_by_name(playername
)
182 if not player
then return false end
183 mcl_hunger
.set_exhaustion(player
, mcl_hunger
.get_exhaustion(player
) + increase
)
184 if mcl_hunger
.get_exhaustion(player
) >= mcl_hunger
.EXHAUST_LVL
then
185 mcl_hunger
.set_exhaustion(player
, 0.0)
187 local satuchanged
= false
188 local s
= mcl_hunger
.get_saturation(player
)
190 mcl_hunger
.set_saturation(player
, math
.max(s
- 1.0, 0))
192 elseif s
<= 0.0001 then
193 h
= mcl_hunger
.get_hunger(player
)
195 mcl_hunger
.set_hunger(player
, h
)
199 if h
~= nil then h
= h
end
200 mcl_hunger
.update_saturation_hud(player
, mcl_hunger
.get_saturation(player
), h
)
203 mcl_hunger
.update_exhaustion_hud(player
, mcl_hunger
.get_exhaustion(player
))
207 function mcl_hunger
.saturate(playername
, increase
, update_hudbar
)
208 local player
= minetest
.get_player_by_name(playername
)
209 mcl_hunger
.set_saturation(player
, math
.min(mcl_hunger
.get_saturation(player
) + increase
, mcl_hunger
.get_hunger(player
)))
210 if update_hudbar
~= false then
211 mcl_hunger
.update_saturation_hud(player
, mcl_hunger
.get_saturation(player
), mcl_hunger
.get_hunger(player
))
216 local timer
= 0 -- Half second timer
217 local timerMult
= 1 -- Cycles from 0 to 7, each time when timer hits half a second
218 minetest
.register_globalstep(function(dtime
)
219 main_timer
= main_timer
+ dtime
220 timer
= timer
+ dtime
221 if main_timer
> mcl_hunger
.HUD_TICK
or timer
> 0.5 then
222 if main_timer
> mcl_hunger
.HUD_TICK
then main_timer
= 0 end
223 for _
,player
in ipairs(minetest
.get_connected_players()) do
224 local name
= player
:get_player_name()
226 local h
= tonumber(mcl_hunger
.get_hunger(player
))
227 local hp
= player
:get_hp()
229 -- Slow health regeneration, and hunger damage (every 4s).
230 -- Regeneration rate based on tutorial video <https://www.youtube.com/watch?v=zs2t-xCVHBo>.
231 -- Minecraft Wiki seems to be wrong in claiming that full hunger gives 0.5s regen rate.
232 if timerMult
== 0 then
233 if h
>= 18 and hp
> 0 and hp
< 20 then
234 -- +1 HP, +exhaustion
236 mcl_hunger
.exhaust(name
, mcl_hunger
.EXHAUST_REGEN
)
237 mcl_hunger
.update_exhaustion_hud(player
, mcl_hunger
.get_exhaustion(player
))
239 -- Damage hungry player down to 1 HP
251 timerMult
= timerMult
+ 1
252 if timerMult
> 7 then