]> git.lizzy.rs Git - Crafter.git/blob - mods/hunger/init.lua
Overhaul eating
[Crafter.git] / mods / hunger / init.lua
1 local minetest,math,ItemStack = 
2       minetest,math,ItemStack
3 local mod_storage             = minetest.get_mod_storage()
4 local player_hunger_data      = {} -- array to hold hunger data
5 local hunger_class            = {}
6 hunger_pointer                = {} -- allow other mods to access local data
7 hunger_class.data             = nil
8 hunger_class.drowning         = nil
9 hunger_class.hp               = nil
10 hunger_class.name             = nil
11 hunger_class.i_data           = nil
12 hunger_class.count            = nil
13 hunger_class.food_data        = {}
14 hunger_class.pairs            = pairs
15 hunger_class.ipairs           = ipairs
16 hunger_class.get_connected    = minetest.get_connected_players
17 hunger_class.get_group        = minetest.get_item_group
18
19 --this is the max exhaustion a player will get before their
20 --satiation goes down and rolls over
21 hunger_class.exhaustion_peak  = 512
22 --when satiation runs out this is when the hunger peak variable
23 --is used, everytime the player rolls over this their hunger ticks down
24 --based on what they're doing
25 hunger_class.hunger_peak      = 128
26
27 -- creates volitile data for the game to use
28 hunger_class.set_data = function(player,data)
29         hunger_class.name = player:get_player_name()
30         if not player_hunger_data[hunger_class.name] then
31                 player_hunger_data[hunger_class.name] = {}
32         end
33
34         for index,i_data in hunger_class.pairs(data) do
35                 player_hunger_data[hunger_class.name][index] = i_data
36         end
37
38         if data.hunger then
39                 hud_manager.change_hud({
40                         player    =  player ,
41                         hud_name  = "hunger",
42                         element   = "number",
43                         data      =  data.hunger
44                 })
45         end
46 end
47
48 -- dynamic indexing
49 hunger_class.get_data = function(player,requested_data)
50         hunger_class.name = player:get_player_name()
51         if player_hunger_data[hunger_class.name] then
52                 hunger_class.i_data = {}
53                 hunger_class.count  = 0
54                 for _,i_data in hunger_class.pairs(requested_data) do
55                         if player_hunger_data[hunger_class.name][i_data] then
56                                 hunger_class.i_data[i_data] = player_hunger_data[hunger_class.name][i_data]
57                                 hunger_class.count = hunger_class.count + 1
58                         end
59                 end
60
61                 if hunger_class.count > 0 then
62                         return(hunger_class.i_data)
63                 else
64                         return(nil)
65                 end
66         end
67         return(nil)
68 end
69
70 -- removes hunger data
71 hunger_class.terminate = function(player)
72         hunger_class.name = player:get_player_name()
73         if player_hunger_data[hunger_class.name] then
74                 player_hunger_data[hunger_class.name] = nil
75         end
76 end
77
78 -- loads data from mod storage
79 hunger_class.load_data = function(player)
80         hunger_class.name = player:get_player_name()
81         if mod_storage:get_int(hunger_class.name.."h_save") > 0 then
82                 return({
83                                 hunger                = mod_storage:get_int(hunger_class.name.."hunger"               ),
84                                 satiation             = mod_storage:get_int(hunger_class.name.."satiation"            ),
85                                 exhaustion            = mod_storage:get_int(hunger_class.name.."exhaustion"           ),
86                                 regeneration_interval = mod_storage:get_int(hunger_class.name.."regeneration_interval")
87                           })
88         else
89                 return({
90                                 hunger                = 20,
91                                 satiation             = 20,
92                                 regeneration_interval = 0,
93                                 exhaustion            = 0
94                           })
95         end
96 end
97
98 -- saves data to be utilized on next login
99 hunger_class.save_data = function(player)
100         if type(player) ~= "string" and player:is_player() then
101                 hunger_class.name = player:get_player_name()
102         elseif type(player) == "string" then
103                 hunger_class.name = player
104         end
105         if player_hunger_data[hunger_class.name] then
106                 for index,integer in hunger_class.pairs(player_hunger_data[hunger_class.name]) do
107                         mod_storage:set_int(hunger_class.name..index,integer)
108                 end
109         end
110
111         mod_storage:set_int(hunger_class.name.."h_save", 1)
112
113         player_hunger_data[hunger_class.name] = nil
114 end
115
116 -- is used for shutdowns to save all data
117 hunger_class.save_all = function()
118         for name,data in hunger_class.pairs(player_hunger_data) do
119                 hunger_class.save_data(name)
120         end
121 end
122
123 -- an easy translation pool
124 hunger_class.satiation_pool = {
125         [0]   = 1,
126         [0.5] = 3,
127         [1]   = 6,
128         [2]   = 8,
129         [3]   = 1
130 }
131 -- ticks up the exhaustion when counting down satiation
132 hunger_class.tick_up_satiation = function(m_data,exhaustion)
133         return(exhaustion + hunger_class.satiation_pool[m_data])
134 end
135
136 -- an easy translation pool
137 hunger_class.hunger_pool = {
138         [0]   = 1,
139         [0.5] = 2,
140         [1]   = 3,
141         [2]   = 4,
142         [3]   = 1
143 }
144 -- ticks up the exhaustion when counting down hunger
145 hunger_class.tick_up_hunger = function(m_data,exhaustion)
146         return(exhaustion + hunger_class.hunger_pool[m_data])
147 end
148
149 -- allows other mods to set hunger data
150 hunger_pointer.set_data = function(player,data)
151         hunger_class.name = player:get_player_name()
152         if not player_hunger_data[hunger_class.name] then
153                 player_hunger_data[hunger_class.name] = {}
154         end
155
156         for index,i_data in hunger_class.pairs(data) do
157                 player_hunger_data[hunger_class.name][index] = i_data
158         end
159
160         if data.hunger then
161                 hud_manager.change_hud({
162                         player    =  player ,
163                         hud_name  = "hunger",
164                         element   = "number",
165                         data      =  data.hunger
166                 })
167         end
168 end
169
170 -- allows other mods to index hunger data
171 hunger_pointer.get_data = function(player,requested_data)
172         hunger_class.name = player:get_player_name()
173         if player_hunger_data[hunger_class.name] then
174                 hunger_class.i_data = {}
175                 hunger_class.count  = 0
176                 for _,i_data in hunger_class.pairs(requested_data) do
177                         if player_hunger_data[hunger_class.name][i_data] then
178                                 hunger_class.i_data[i_data] = player_hunger_data[hunger_class.name][i_data]
179                                 hunger_class.count = hunger_class.count + 1
180                         end
181                 end
182                 if hunger_class.count > 0 then
183                         return(hunger_class.i_data)
184                 else
185                         return(nil)
186                 end
187         end
188         return(nil)
189 end
190
191 -- saves specific users data for when they relog
192 minetest.register_on_leaveplayer(function(player)
193         hunger_class.save_data(player)
194         hunger_class.terminate(player)
195 end)
196
197 -- save all data to mod storage on shutdown
198 minetest.register_on_shutdown(function()
199         hunger_class.save_all()
200 end)
201
202 -- create new data for hunger per player
203 minetest.register_on_joinplayer(function(player)
204         hunger_class.name        = player:get_player_name()
205         hunger_class.data        = hunger_class.load_data(player)
206
207         hunger_class.set_data(player,hunger_class.data)
208
209         hud_manager.add_hud(player,"hunger_bg",{
210                 hud_elem_type = "statbar",
211                 position      = {x = 0.5, y = 1},
212                 text          = "hunger_icon_bg.png",
213                 number        = 20,
214                 direction     = 1,
215                 size          = {x = 24, y = 24},
216                 offset        = {x = 24*10, y= -(48 + 24 + 39)},
217         })
218         hud_manager.add_hud(player,"hunger",{
219                 hud_elem_type = "statbar",
220                 position      = {x = 0.5, y = 1},
221                 text          = "hunger_icon.png",
222                 number        = hunger_class.data.hunger,
223                 direction     = 1,
224                 size          = {x = 24, y = 24},
225                 offset        = {x = 24*10, y= -(48 + 24 + 39)},
226         })
227 end)
228
229 -- resets the players hunger settings to max
230 minetest.register_on_respawnplayer(function(player)
231         hunger_class.set_data(player,{
232                 hunger                = 20,
233                 satiation             = 20,
234                 regeneration_interval = 0,
235                 exhaustion            = 0,
236         })
237 end)
238
239
240 local function hunger_update()
241         for _,player in hunger_class.ipairs(hunger_class.get_connected()) do
242                 --do not regen player's health if dead - this will be reused for 1up apples
243                 if player:get_hp() > 0 then
244                 
245
246                         hunger_class.data = hunger_class.get_data(player,{
247                                 "hunger","satiation","exhaustion","regeneration_interval"
248                         })
249
250                         print(dump(hunger_class.data))
251
252                         --movement state
253                         local m_data = movement_pointer.get_data(player,{"state"})
254                         if m_data then
255                                 m_data = m_data.state
256                         end
257                         
258                         -- if player is moving in state 0 add 0.5
259                         if m_data == 0 then
260                                 local input = player:get_player_control()
261                                 if input.jump or input.right or input.left or input.down or input.up then
262                                         m_data = 0.5
263                                 end
264                         end
265                         
266                         
267
268                         -- count down invisible satiation bar
269                         if hunger_class.data.satiation > 0 and hunger_class.data.hunger >= 20 then
270                                 hunger_class.data.exhaustion = hunger_class.tick_up_satiation(m_data, hunger_class.data.exhaustion)
271                                 if hunger_class.data.exhaustion >= hunger_class.exhaustion_peak then
272
273                                         hunger_class.data.satiation = hunger_class.data.satiation - 1
274                                         hunger_class.data.exhaustion = hunger_class.data.exhaustion - hunger_class.exhaustion_peak
275                                         
276                                         --reset this to use for the hunger tick
277                                         if hunger_class.data.satiation == 0 then
278                                                 hunger_class.data.exhaustion = 0
279                                         end
280
281                                         hunger_class.set_data(player,{satiation=hunger_class.data.satiation})
282                                 end
283                                 hunger_class.set_data(player,{exhaustion=hunger_class.data.exhaustion})
284                         -- count down hunger bars
285                         elseif hunger_class.data.hunger > 0 then
286                                 hunger_class.data.exhaustion = hunger_class.tick_up_hunger(m_data,hunger_class.data.exhaustion)
287                                 
288                                 if hunger_class.data.exhaustion >= hunger_class.hunger_peak then
289                                         --don't allow hunger to go negative
290                                         if hunger_class.data.hunger > 0 then
291                                                 hunger_class.data.exhaustion = hunger_class.data.exhaustion - hunger_class.hunger_peak
292                                                 hunger_class.data.hunger = hunger_class.data.hunger - 1
293                                                 hunger_class.set_data(player,{hunger=hunger_class.data.hunger})
294                                         end
295                                 end
296                                 hunger_class.set_data(player,{exhaustion=hunger_class.data.exhaustion})
297                         -- hurt the player if hunger bar empty
298                         elseif hunger_class.data.hunger <= 0 then
299                                 hunger_class.data.exhaustion = hunger_class.data.exhaustion + 1
300                                 local hp = player:get_hp()
301                                 if hp > 0 and hunger_class.data.exhaustion >= 2 then
302                                         player:set_hp(hp-1)
303                                         hunger_class.data.exhaustion = 0
304                                 end
305                                 hunger_class.set_data(player,{exhaustion=hunger_class.data.exhaustion})
306                         end
307                         
308                         
309                         hunger_class.hp = player:get_hp()
310                         hunger_class.drowning = drowning_pointer.get_data(player,{"drowning"}).drowning
311                         --make regeneration happen every second
312                         if hunger_class.drowning == 0 and hunger_class.data.hunger >= 20 and hunger_class.hp < 20 then --  meta:get_int("on_fire") == 0 
313                                 --print(regeneration_interval,"--------------------------")
314                                 hunger_class.data.regeneration_interval = hunger_class.data.regeneration_interval + 1
315                                 if hunger_class.data.regeneration_interval >= 2 then
316                                         player:set_hp(hunger_class.hp+1)
317                                         hunger_class.data.exhaustion = hunger_class.data.exhaustion + 32
318                                         hunger_class.data.regeneration_interval = 0
319                                         
320                                         hunger_class.set_data(player,{
321                                                 regeneration_interval = hunger_class.data.regeneration_interval,
322                                                 exhaustion            = hunger_class.data.exhaustion           ,
323                                                 satiation             = hunger_class.data.satiation            ,
324                                         })
325                                 else
326                                         hunger_class.set_data(player,{regeneration_interval=hunger_class.data.regeneration_interval})
327                                 end
328                         --reset the regen interval
329                         else
330                                 hunger_class.set_data(player,{regeneration_interval=0})
331                         end
332                 end
333         end
334         
335         minetest.after(0.5, function()
336                 hunger_update()
337         end)
338 end
339
340 minetest.register_on_mods_loaded(function()
341         minetest.after(0,function()
342                 hunger_update()
343         end)
344 end)
345
346 --take away hunger and satiation randomly while mining
347 minetest.register_on_dignode(function(pos, oldnode, digger)
348         if digger and digger:is_player() then
349                 hunger_class.set_data(digger,{
350                         exhaustion = hunger_class.get_data(digger,{"exhaustion"}).exhaustion + math.random(0,2)
351                 })
352         end
353 end)
354
355 -- take the eaten food
356 hunger_class.take_food = function(player)
357         hunger_class.data = player:get_wielded_item()
358         hunger_class.data:take_item()
359         player:set_wielded_item(hunger_class.data)
360 end
361
362 -- players eat food
363 hunger_pointer.eat_food = function(player,item)
364         hunger_class.data = hunger_class.get_data(player,{
365                 "hunger"   ,
366                 "satiation",
367         })      
368         
369         if type(item) == "string" then
370                 item = ItemStack(item)
371         elseif type(item) == "table" then
372                 item = ItemStack(item.name)
373         end
374         item = item:get_name()
375         
376         hunger_class.food_data.satiation = hunger_class.get_group( item, "satiation" )
377         hunger_class.food_data.hunger    = hunger_class.get_group( item, "hunger"    )
378         
379         hunger_class.data.hunger = hunger_class.data.hunger + hunger_class.food_data.hunger
380
381         if hunger_class.data.hunger > 20 then
382                 hunger_class.data.hunger = 20
383         end
384         
385         -- unlimited
386         -- this makes the game easier
387         hunger_class.data.satiation = hunger_class.data.satiation + hunger_class.food_data.satiation
388         
389         hunger_class.set_data(player,{
390                 hunger    = hunger_class.data.hunger   ,
391                 satiation = hunger_class.data.satiation,
392         })
393
394         hunger_class.take_food(player)
395 end
396
397 -- easily allows mods to register food
398 minetest.register_food = function(name,def)
399         minetest.register_craftitem(":"..name, {
400                 description = def.description,
401                 inventory_image = def.texture,
402                 groups = {satiation=def.satiation,hunger=def.hunger},
403         })
404
405         minetest.register_node(":"..name.."node", {
406                 tiles = {def.texture},
407                 drawtype = "allfaces",
408         })
409 end
410
411
412 minetest.register_chatcommand("hungry", {
413         params = "<mob>",
414         description = "A debug command to test food",
415         privs = {server = true},
416         func = function(name)
417                 local player = minetest.get_player_by_name(name)
418                 hunger_class.set_data(player,{
419                         exhaustion = 0,
420                         hunger     = 1,
421                         satiation  = 0
422                 })
423         end
424 })