1 -- wrapper for minetest.item_eat (this way we make sure other mods can't break this one)
2 local org_eat
= core
.do_item_eat
3 core
.do_item_eat
= function(hp_change
, replace_with_item
, itemstack
, user
, pointed_thing
)
4 -- Call on_rightclick if the pointed node defines it
5 if pointed_thing
.type == "node" then
6 local node
= minetest
.get_node(pointed_thing
.under
)
7 if user
and not user
:get_player_control().sneak
then
8 if minetest
.registered_nodes
[node
.name
] and minetest
.registered_nodes
[node
.name
].on_rightclick
then
9 return minetest
.registered_nodes
[node
.name
].on_rightclick(pointed_thing
.under
, node
, user
, itemstack
) or itemstack
14 local old_itemstack
= itemstack
16 local name
= user
:get_player_name()
18 -- Special foodstuffs like the cake may disable the eating delay
19 local no_eat_delay
= minetest
.get_item_group(itemstack
:get_name(), "no_eat_delay") == 1
21 -- Allow eating only after a delay of 2 seconds. This prevents eating as an excessive speed.
22 -- FIXME: time() is not a precise timer, so the actual delay may be +- 1 second, depending on which fraction
23 -- of the second the player made the first eat.
24 -- FIXME: In singleplayer, there's a cheat to circumvent this, simply by pausing the game between eats.
25 -- This is because os.time() obviously does not care about the pause. A fix needs a different timer mechanism.
26 if no_eat_delay
or (mcl_hunger
.last_eat
[name
] < 0) or (os
.difftime(os
.time(), mcl_hunger
.last_eat
[name
]) >= 2) then
27 local can_eat_when_full
= minetest
.get_item_group(itemstack
:get_name(), "can_eat_when_full") == 1
28 -- Don't allow eating when player has full hunger bar (some exceptional items apply)
29 if can_eat_when_full
or (mcl_hunger
.get_hunger(user
) < 20) then
30 itemstack
= mcl_hunger
.eat(hp_change
, replace_with_item
, itemstack
, user
, pointed_thing
)
31 for _
, callback
in pairs(core
.registered_on_item_eats
) do
32 local result
= callback(hp_change
, replace_with_item
, itemstack
, user
, pointed_thing
, old_itemstack
)
37 mcl_hunger
.last_eat
[name
] = os
.time()
47 function mcl_hunger
.register_food(name
, hunger_change
, replace_with_item
, poisontime
, poison
, exhaust
, poisonchance
, sound
)
49 food
[name
].saturation
= hunger_change
-- hunger points added
50 food
[name
].replace
= replace_with_item
-- what item is given back after eating
51 food
[name
].poisontime
= poisontime
-- time it is poisoning. If this is set, this item is considered poisonous,
52 -- otherwise the following poison/exhaust fields are ignored
53 food
[name
].poison
= poison
-- poison damage per tick for poisonous food
54 food
[name
].exhaust
= exhaust
-- exhaustion per tick for poisonous food
55 food
[name
].poisonchance
= poisonchance
-- chance percentage that this item poisons the player (default: 100% if poisoning is enabled)
56 food
[name
].sound
= sound
-- special sound that is played when eating
59 function mcl_hunger
.eat(hp_change
, replace_with_item
, itemstack
, user
, pointed_thing
)
60 local item
= itemstack
:get_name()
61 local def
= food
[item
]
64 if type(hp_change
) ~= "number" then
66 core
.log("error", "Wrong on_use() definition for item '" .. item
.. "'")
68 def
.saturation
= hp_change
69 def
.replace
= replace_with_item
71 local func
= mcl_hunger
.item_eat(def
.saturation
, def
.replace
, def
.poisontime
, def
.poison
, def
.exhaust
, def
.poisonchance
, def
.sound
)
72 return func(itemstack
, user
, pointed_thing
)
75 -- Reset HUD bars after poisoning
76 local function reset_bars_poison_damage(player
)
77 hb
.change_hudbar(player
, "health", nil, nil, "hudbars_icon_health.png", nil, "hudbars_bar_health.png")
80 local function reset_bars_poison_hunger(player
)
81 hb
.change_hudbar(player
, "hunger", nil, nil, "hbhunger_icon.png", nil, "hbhunger_bar.png")
82 if mcl_hunger
.debug
then
83 hb
.change_hudbar(player
, "exhaustion", nil, nil, nil, nil, "mcl_hunger_bar_exhaustion.png")
88 local function poisonp(tick
, time
, time_left
, damage
, exhaustion
, name
)
89 local player
= minetest
.get_player_by_name(name
)
90 -- First check if player is still there
94 local name
= player
:get_player_name()
95 -- Abort if poisonings have been stopped
96 if mcl_hunger
.poison_damage
[name
] == 0 and mcl_hunger
.poison_hunger
[name
] == 0 then
99 time_left
= time_left
+ tick
100 if time_left
< time
then
101 minetest
.after(tick
, poisonp
, tick
, time
, time_left
, damage
, exhaustion
, name
)
104 mcl_hunger
.poison_damage
[name
] = mcl_hunger
.poison_damage
[name
] - 1
106 if exhaustion
> 0 then
107 mcl_hunger
.poison_hunger
[name
] = mcl_hunger
.poison_hunger
[name
] - 1
109 if mcl_hunger
.poison_damage
[name
] <= 0 then
110 reset_bars_poison_damage(player
)
112 if mcl_hunger
.poison_hunger
[name
] <= 0 then
113 reset_bars_poison_hunger(player
)
117 -- Deal damage and exhaust player
118 if player
:get_hp()-damage
> 0 then
119 player
:set_hp(player
:get_hp()-damage
)
122 mcl_hunger
.exhaust(name
, exhaustion
)
126 -- Immediately stop all poisonings for this player
127 function mcl_hunger
.stop_poison(player
)
128 mcl_hunger
.poison_damage
[player
:get_player_name()] = 0
129 mcl_hunger
.poison_hunger
[player
:get_player_name()] = 0
130 reset_bars_poison_damage(player
)
131 reset_bars_poison_hunger(player
)
134 local poisonrandomizer
= PseudoRandom(os
.time())
136 function mcl_hunger
.item_eat(hunger_change
, replace_with_item
, poisontime
, poison
, exhaust
, poisonchance
, sound
)
137 return function(itemstack
, user
, pointed_thing
)
138 local itemname
= itemstack
:get_name()
140 if itemstack
:take_item() ~= nil and user
~= nil then
141 local name
= user
:get_player_name()
142 local hp
= user
:get_hp()
144 local pos
= user
:get_pos()
147 local foodtype
= minetest
.get_item_group(itemname
, "food")
148 if foodtype
== 3 then
149 -- Item is a drink, only play drinking sound (no particle)
150 minetest
.sound_play("survival_thirst_drink", {
152 max_hear_distance
= 12,
156 -- Assume the item is a food
157 -- Add eat particle effect and sound
158 local def
= minetest
.registered_items
[itemname
]
159 local texture
= def
.inventory_image
160 if not texture
or texture
== "" then
161 texture
= def
.wield_image
163 -- Special item definition field: _food_particles
164 -- If false, force item to not spawn any food partiles when eaten
165 if def
._food_particles
~= false and texture
and texture
~= "" then
166 local v
= user
:get_player_velocity()
167 local minvel
= vector
.add(v
, {x
=-1, y
=1, z
=-1})
168 local maxvel
= vector
.add(v
, {x
=1, y
=2, z
=1})
170 minetest
.add_particlespawner({
171 amount
= math
.min(math
.max(8, hunger_change
*2), 25),
173 minpos
= {x
=pos
.x
, y
=pos
.y
, z
=pos
.z
},
174 maxpos
= {x
=pos
.x
, y
=pos
.y
, z
=pos
.z
},
177 minacc
= {x
=0, y
=-5, z
=0},
178 maxacc
= {x
=0, y
=-9, z
=0},
183 collisiondetection
= true,
188 minetest
.sound_play("mcl_hunger_bite", {
190 max_hear_distance
= 12,
195 if hunger_change
then
196 -- Add saturation (must be defined in item table)
197 local _mcl_saturation
= minetest
.registered_items
[itemname
]._mcl_saturation
199 if not _mcl_saturation
then
202 saturation
= minetest
.registered_items
[itemname
]._mcl_saturation
204 mcl_hunger
.saturate(name
, saturation
, false)
207 local h
= mcl_hunger
.get_hunger(user
)
208 if h
< 20 and hunger_change
then
209 h
= h
+ hunger_change
210 if h
> 20 then h
= 20 end
211 mcl_hunger
.set_hunger(user
, h
, false)
214 hb
.change_hudbar(user
, "hunger", h
)
215 mcl_hunger
.update_saturation_hud(user
, mcl_hunger
.get_saturation(user
), h
)
219 local do_poison
= false
221 if poisonrandomizer
:next(0,100) < poisonchance
then
229 if poison
and poison
> 0 then
230 hb
.change_hudbar(user
, "health", nil, nil, "hbhunger_icon_health_poison.png", nil, "hbhunger_bar_health_poison.png")
231 mcl_hunger
.poison_damage
[name
] = mcl_hunger
.poison_damage
[name
] + 1
233 if exhaust
and exhaust
> 0 then
234 hb
.change_hudbar(user
, "hunger", nil, nil, "mcl_hunger_icon_foodpoison.png", nil, "mcl_hunger_bar_foodpoison.png")
235 if mcl_hunger
.debug
then
236 hb
.change_hudbar(user
, "exhaustion", nil, nil, nil, nil, "mcl_hunger_bar_foodpoison.png")
238 mcl_hunger
.poison_hunger
[name
] = mcl_hunger
.poison_hunger
[name
] + 1
240 poisonp(1, poisontime
, 0, poison
, exhaust
, user
:get_player_name())
245 itemstack
:add_item(replace_with_item
)
251 -- player-action based hunger changes
252 minetest
.register_on_dignode(function(pos
, oldnode
, player
)
253 -- is_fake_player comes from the pipeworks, we are not interested in those
254 if not player
or not player
:is_player() or player
.is_fake_player
== true then
257 local name
= player
:get_player_name()
259 mcl_hunger
.exhaust(name
, mcl_hunger
.EXHAUST_DIG
)
262 -- Apply simple poison effect as long there are no real status effect
263 -- TODO: Remove this when status effects are in place
265 mcl_hunger
.register_food("mcl_farming:potato_item_poison", 2, "", 4, 1, 0, 60)
267 mcl_hunger
.register_food("mcl_mobitems:rotten_flesh", 4, "", 30, 0, 100, 80)
268 mcl_hunger
.register_food("mcl_mobitems:chicken", 2, "", 30, 0, 100, 30)
269 mcl_hunger
.register_food("mcl_mobitems:spider_eye", 2, "", 4, 1, 0)
271 mcl_hunger
.register_food("mcl_fishing:pufferfish_raw", 1, "", 60, 1, 300)