mcl_hunger: Remove non-food poison mechanic
[MineClone/MineClone2.git] / mods / PLAYER / mcl_hunger / hunger.lua
blob42fa219f8dc0a6193e3a29460c4de993ac915e1c
1 local S = minetest.get_translator("mcl_hunger")
2 local mod_death_messages = minetest.get_modpath("mcl_death_messages")
4 -- wrapper for minetest.item_eat (this way we make sure other mods can't break this one)
5 local org_eat = minetest.do_item_eat
6 minetest.do_item_eat = function(hp_change, replace_with_item, itemstack, user, pointed_thing)
8 if not user or user:is_player() == false then
9 return itemstack
10 end
12 -- Call on_rightclick if the pointed node defines it
13 if pointed_thing.type == "node" then
14 local node = minetest.get_node(pointed_thing.under)
15 if user and not user:get_player_control().sneak then
16 if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
17 return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, user, itemstack) or itemstack
18 end
19 end
20 end
22 local old_itemstack = itemstack
24 local name = user:get_player_name()
26 local creative = minetest.is_creative_enabled(name)
28 -- Special foodstuffs like the cake may disable the eating delay
29 local no_eat_delay = creative or (minetest.get_item_group(itemstack:get_name(), "no_eat_delay") == 1)
31 -- Allow eating only after a delay of 2 seconds. This prevents eating as an excessive speed.
32 -- FIXME: time() is not a precise timer, so the actual delay may be +- 1 second, depending on which fraction
33 -- of the second the player made the first eat.
34 -- FIXME: In singleplayer, there's a cheat to circumvent this, simply by pausing the game between eats.
35 -- This is because os.time() obviously does not care about the pause. A fix needs a different timer mechanism.
36 if no_eat_delay or (mcl_hunger.last_eat[name] < 0) or (os.difftime(os.time(), mcl_hunger.last_eat[name]) >= 2) then
37 local can_eat_when_full = creative or (mcl_hunger.active == false) or minetest.get_item_group(itemstack:get_name(), "can_eat_when_full") == 1
38 -- Don't allow eating when player has full hunger bar (some exceptional items apply)
39 if can_eat_when_full or (mcl_hunger.get_hunger(user) < 20) then
40 itemstack = mcl_hunger.eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
41 for _, callback in pairs(minetest.registered_on_item_eats) do
42 local result = callback(hp_change, replace_with_item, itemstack, user, pointed_thing, old_itemstack)
43 if result then
44 return result
45 end
46 end
47 mcl_hunger.last_eat[name] = os.time()
48 end
49 end
51 return itemstack
52 end
54 function mcl_hunger.eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
55 local item = itemstack:get_name()
56 local def = mcl_hunger.registered_foods[item]
57 if not def then
58 def = {}
59 if type(hp_change) ~= "number" then
60 hp_change = 1
61 minetest.log("error", "Wrong on_use() definition for item '" .. item .. "'")
62 end
63 def.saturation = hp_change
64 def.replace = replace_with_item
65 end
66 local func = mcl_hunger.item_eat(def.saturation, def.replace, def.poisontime, def.poison, def.exhaust, def.poisonchance, def.sound)
67 return func(itemstack, user, pointed_thing)
68 end
70 -- Reset HUD bars after food poisoning
72 function mcl_hunger.reset_bars_poison_hunger(player)
73 hb.change_hudbar(player, "hunger", nil, nil, "hbhunger_icon.png", nil, "hbhunger_bar.png")
74 if mcl_hunger.debug then
75 hb.change_hudbar(player, "exhaustion", nil, nil, nil, nil, "mcl_hunger_bar_exhaustion.png")
76 end
77 end
79 -- Poison player
80 local function poisonp(tick, time, time_left, damage, exhaustion, name)
81 if not mcl_hunger.active then
82 return
83 end
84 local player = minetest.get_player_by_name(name)
85 -- First check if player is still there
86 if not player then
87 return
88 end
89 local name = player:get_player_name()
90 -- Abort if food poisonings have been stopped
91 if mcl_hunger.poison_hunger[name] == 0 then
92 return
93 end
94 time_left = time_left + tick
95 if time_left < time then
96 minetest.after(tick, poisonp, tick, time, time_left, damage, exhaustion, name)
97 else
98 if exhaustion > 0 then
99 mcl_hunger.poison_hunger [name] = mcl_hunger.poison_hunger[name] - 1
101 if mcl_hunger.poison_hunger[name] <= 0 then
102 mcl_hunger.reset_bars_poison_hunger(player)
106 -- Deal damage and exhaust player
107 -- TODO: Introduce fatal poison at higher difficulties
108 if player:get_hp()-damage > 0 then
109 if mod_death_messages then
110 mcl_death_messages.player_damage(player, S("@1 succumbed to the poison.", name))
112 player:set_hp(player:get_hp()-damage)
115 mcl_hunger.exhaust(name, exhaustion)
119 local poisonrandomizer = PseudoRandom(os.time())
121 function mcl_hunger.item_eat(hunger_change, replace_with_item, poisontime, poison, exhaust, poisonchance, sound)
122 return function(itemstack, user, pointed_thing)
123 local itemname = itemstack:get_name()
124 local creative = minetest.is_creative_enabled(user:get_player_name())
125 if itemstack:peek_item() ~= nil and user ~= nil then
126 if not creative then
127 itemstack:take_item()
129 local name = user:get_player_name()
130 local hp = user:get_hp()
132 local pos = user:get_pos()
133 -- player height
134 pos.y = pos.y + 1.5
135 local foodtype = minetest.get_item_group(itemname, "food")
136 if foodtype == 3 then
137 -- Item is a drink, only play drinking sound (no particle)
138 minetest.sound_play("survival_thirst_drink", {
139 max_hear_distance = 12,
140 gain = 1.0,
141 pitch = 1 + math.random(-10, 10)*0.005,
142 object = user,
143 }, true)
144 else
145 -- Assume the item is a food
146 -- Add eat particle effect and sound
147 local def = minetest.registered_items[itemname]
148 local texture = def.inventory_image
149 if not texture or texture == "" then
150 texture = def.wield_image
152 -- Special item definition field: _food_particles
153 -- If false, force item to not spawn any food partiles when eaten
154 if def._food_particles ~= false and texture and texture ~= "" then
155 local v = user:get_player_velocity()
156 local minvel = vector.add(v, {x=-1, y=1, z=-1})
157 local maxvel = vector.add(v, {x=1, y=2, z=1})
159 minetest.add_particlespawner({
160 amount = math.min(math.max(8, hunger_change*2), 25),
161 time = 0.1,
162 minpos = {x=pos.x, y=pos.y, z=pos.z},
163 maxpos = {x=pos.x, y=pos.y, z=pos.z},
164 minvel = minvel,
165 maxvel = maxvel,
166 minacc = {x=0, y=-5, z=0},
167 maxacc = {x=0, y=-9, z=0},
168 minexptime = 1,
169 maxexptime = 1,
170 minsize = 1,
171 maxsize = 2,
172 collisiondetection = true,
173 vertical = false,
174 texture = texture,
177 minetest.sound_play("mcl_hunger_bite", {
178 max_hear_distance = 12,
179 gain = 1.0,
180 pitch = 1 + math.random(-10, 10)*0.005,
181 object = user,
182 }, true)
185 if mcl_hunger.active and hunger_change then
186 -- Add saturation (must be defined in item table)
187 local _mcl_saturation = minetest.registered_items[itemname]._mcl_saturation
188 local saturation
189 if not _mcl_saturation then
190 saturation = 0
191 else
192 saturation = minetest.registered_items[itemname]._mcl_saturation
194 mcl_hunger.saturate(name, saturation, false)
196 -- Add food points
197 local h = mcl_hunger.get_hunger(user)
198 if h < 20 and hunger_change then
199 h = h + hunger_change
200 if h > 20 then h = 20 end
201 mcl_hunger.set_hunger(user, h, false)
204 hb.change_hudbar(user, "hunger", h)
205 mcl_hunger.update_saturation_hud(user, mcl_hunger.get_saturation(user), h)
207 -- Poison
208 if mcl_hunger.active and poisontime then
209 local do_poison = false
210 if poisonchance then
211 if poisonrandomizer:next(0,100) < poisonchance then
212 do_poison = true
214 else
215 do_poison = true
217 if do_poison then
218 -- Set food poison bars
219 if exhaust and exhaust > 0 then
220 hb.change_hudbar(user, "hunger", nil, nil, "mcl_hunger_icon_foodpoison.png", nil, "mcl_hunger_bar_foodpoison.png")
221 if mcl_hunger.debug then
222 hb.change_hudbar(user, "exhaustion", nil, nil, nil, nil, "mcl_hunger_bar_foodpoison.png")
224 mcl_hunger.poison_hunger[name] = mcl_hunger.poison_hunger[name] + 1
226 poisonp(1, poisontime, 0, poison, exhaust, user:get_player_name())
230 if not creative then
231 itemstack:add_item(replace_with_item)
234 return itemstack
238 if mcl_hunger.active then
239 -- player-action based hunger changes
240 minetest.register_on_dignode(function(pos, oldnode, player)
241 -- is_fake_player comes from the pipeworks, we are not interested in those
242 if not player or not player:is_player() or player.is_fake_player == true then
243 return
245 local name = player:get_player_name()
246 -- dig event
247 mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_DIG)
248 end)