]> git.lizzy.rs Git - Crafter.git/blob - mods/player_mechanics/player_interaction.lua
Implement fall damage step delay calculation to accomodate for client->server lag
[Crafter.git] / mods / player_mechanics / player_interaction.lua
1 local minetest,armor_class,math,pairs,ipairs = minetest,armor_class,math,pairs,ipairs
2
3 local pos
4 local name
5 local damage_nodes
6 local real_nodes
7 local a_min
8 local a_max
9 local _
10 local cancel_fall_damage = function(player)
11         name = player:get_player_name()
12         if player:get_hp() <= 0 then
13                 return
14         end
15         -- used for finding a damage node from the center of the player
16         -- rudementary collision detection
17         pos = player:get_pos()
18         pos.y = pos.y
19         a_min = vector.new(
20                 pos.x-0.25,
21                 pos.y-0.85,
22                 pos.z-0.25
23         )
24         a_max = vector.new(
25                 pos.x+0.25,
26                 pos.y+0.85,
27                 pos.z+0.25
28         )
29         _,saving_nodes = minetest.find_nodes_in_area( a_min,  a_max, {"group:disable_fall_damage"})
30         real_nodes = {}
31         for node_data,_ in pairs(saving_nodes) do
32                 if saving_nodes[node_data] > 0 then
33                         table.insert(real_nodes,node_data)
34                 end
35         end
36         -- find the highest damage node
37         if table.getn(real_nodes) > 0 then
38                 return(true)
39         end
40         return(false)
41 end
42
43
44 local function calc_fall_damage(player,hp_change,velocity)
45         if cancel_fall_damage(player) then
46                 return
47         else
48                 --boots absorb fall damage
49                 local fall_damage = velocity+13
50                 --print("fall damage:",fall_damage)
51                 local inv = player:get_inventory()
52                 local stack = inv:get_stack("armor_feet", 1)
53                 local name = stack:get_name()
54                 if name ~= "" then
55                         local absorption = 0
56
57                         absorption = minetest.get_item_group(name,"armor_level")*2
58                         --print("absorbtion:",absorption)
59                         local wear_level = ((9-minetest.get_item_group(name,"armor_level"))*8)*(5-minetest.get_item_group(name,"armor_type"))*math.abs(fall_damage)
60                         
61                         stack:add_wear(wear_level)
62                         
63                         inv:set_stack("armor_feet", 1, stack)
64                         
65                         local new_stack = inv:get_stack("armor_feet",1):get_name()
66
67                         if new_stack == "" then                                 
68                                 minetest.sound_play("armor_break",{to_player=player:get_player_name(),gain=1,pitch=math.random(80,100)/100})
69                                 armor_class.recalculate_armor(player)
70                                 armor_class.set_armor_gui(player)
71                                 --do particles too
72                         elseif minetest.get_item_group(new_stack,"boots") > 0 then 
73                                 local pos = player:get_pos()
74                                 minetest.add_particlespawner({
75                                         amount = 30,
76                                         time = 0.00001,
77                                         minpos = {x=pos.x-0.5, y=pos.y+0.1, z=pos.z-0.5},
78                                         maxpos = {x=pos.x+0.5, y=pos.y+0.1, z=pos.z+0.5},
79                                         minvel = vector.new(-0.5,1,-0.5),
80                                         maxvel = vector.new(0.5 ,2 ,0.5),
81                                         minacc = {x=0, y=-9.81, z=1},
82                                         maxacc = {x=0, y=-9.81, z=1},
83                                         minexptime = 0.5,
84                                         maxexptime = 1.5,
85                                         minsize = 0,
86                                         maxsize = 0,
87                                         --attached = player,
88                                         collisiondetection = true,
89                                         collision_removal = true,
90                                         vertical = false,
91                                         node = {name= name.."particletexture"},
92                                         --texture = "eat_particles_1.png"
93                                 })
94                                 minetest.sound_play("armor_fall_damage", {object=player, gain = 1.0, max_hear_distance = 60,pitch = math.random(80,100)/100})   
95                         end
96
97                         fall_damage = fall_damage + absorption
98
99                         if fall_damage >= 0 then
100                                 fall_damage = 0
101                         else
102                                 player:set_hp(player:get_hp()+fall_damage,{reason="correction"})
103                                 minetest.sound_play("hurt", {object=player, gain = 1.0, max_hear_distance = 60,pitch = math.random(80,100)/100})
104                         end
105                 else
106                         player:set_hp(player:get_hp()-hp_change,{reason="correction"})
107                         minetest.sound_play("hurt", {object=player, gain = 1.0, max_hear_distance = 60,pitch = math.random(80,100)/100})
108                 end
109         end
110 end
111
112
113 --hurt sound and disable fall damage group handling
114 minetest.register_on_player_hpchange(function(player, hp_change, reason)
115         if reason.type == "fall" then
116                 local fall = math.floor(player:get_player_velocity().y+0.5)
117                 minetest.after(0.01,function()
118                         calc_fall_damage(player,hp_change,fall)
119                 end)
120                 return(0)
121         elseif hp_change < 0 and reason.reason ~= "correction" then
122                 print("playing 3")
123                 minetest.sound_play("hurt", {object=player, gain = 1.0, max_hear_distance = 60,pitch = math.random(80,100)/100})
124         end
125         return(hp_change)
126 end, true)
127
128 --throw all items on death
129 minetest.register_on_dieplayer(function(player, reason)
130         local pos = player:get_pos()
131         local inv = player:get_inventory()
132         
133         for i = 1,inv:get_size("main") do
134                 local stack = inv:get_stack("main", i)
135                 local name = stack:get_name()
136                 local count = stack:get_count()
137                 if name ~= "" then
138                         local obj = minetest.add_item(pos, stack)
139                         if obj then
140                                 obj:set_velocity(vector.new(math.random(-3,3),math.random(4,8),math.random(-3,3)))
141                         end
142                         inv:set_stack("main", i, ItemStack(""))
143                 else
144                         inv:set_stack("main", i, ItemStack(""))
145                 end
146         end
147
148         local stack = inv:get_stack("armor_head", 1)
149         local name = stack:get_name()
150         if name ~= "" then
151                 local obj = minetest.add_item(pos, stack)
152                 if obj then
153                         obj:set_velocity(vector.new(math.random(-3,3),math.random(4,8),math.random(-3,3)))
154                 end
155                 inv:set_stack("armor_head", 1, ItemStack(""))
156         end
157
158         stack = inv:get_stack("armor_torso", 1)
159         name = stack:get_name()
160         if name ~= "" then
161                 local obj = minetest.add_item(pos, stack)
162                 if obj then
163                         obj:set_velocity(vector.new(math.random(-3,3),math.random(4,8),math.random(-3,3)))
164                 end
165                 inv:set_stack("armor_torso", 1, ItemStack(""))
166         end
167
168         stack = inv:get_stack("armor_legs", 1)
169         name = stack:get_name()
170         if name ~= "" then
171                 local obj = minetest.add_item(pos, stack)
172                 if obj then
173                         obj:set_velocity(vector.new(math.random(-3,3),math.random(4,8),math.random(-3,3)))
174                 end
175                 inv:set_stack("armor_legs", 1, ItemStack(""))
176         end
177
178
179         stack = inv:get_stack("armor_feet", 1)
180         name = stack:get_name()
181         if name ~= "" then
182                 local obj = minetest.add_item(pos, stack)
183                 if obj then
184                         obj:set_velocity(vector.new(math.random(-3,3),math.random(4,8),math.random(-3,3)))
185                 end
186                 inv:set_stack("armor_feet", 1, ItemStack(""))
187         end
188
189
190         armor_class.recalculate_armor(player)
191 end)
192
193
194 --this dumps the players crafting table on closing the inventory
195 dump_craft = function(player)
196         local inv = player:get_inventory()
197         local pos = player:get_pos()
198         pos.y = pos.y + player:get_properties().eye_height
199         for i = 1,inv:get_size("craft") do
200                 local item = inv:get_stack("craft", i)
201                 local obj = minetest.add_item(pos, item)
202                 if obj then
203                         local x=math.random(-2,2)*math.random()
204                         local y=math.random(2,5)
205                         local z=math.random(-2,2)*math.random()
206                         obj:set_velocity({x=x, y=y, z=z})
207                 end
208                 inv:set_stack("craft", i, nil)
209         end
210 end
211
212
213 --play sound to keep up with player's placing vs inconsistent client placing sound 
214 minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing)
215         local node = minetest.registered_nodes[newnode.name]
216         local sound = node.sounds
217         local placing = ""
218         if sound then
219                 placing = sound.placing
220         end
221         --only play the sound when is defined
222         if type(placing) == "table" then
223                 minetest.sound_play(placing.name, {
224                           pos = pos,
225                           gain = placing.gain,
226                           --pitch = math.random(60,100)/100
227                 })
228         end
229 end)
230
231 --replace stack when empty (building)
232 minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing)
233         local old = itemstack:get_name()
234         --pass through to check
235         minetest.after(0,function(pos, newnode, placer, oldnode, itemstack, pointed_thing,old)
236                 if not placer then
237                         return
238                 end
239                 local new = placer:get_wielded_item():get_name()
240                 if old ~= new and new == "" then
241                         local inv = placer:get_inventory()
242                         --check if another stack
243                         if inv:contains_item("main", old) then
244                                 --print("moving stack")
245                                 --run through inventory
246                                 for i = 1,inv:get_size("main") do
247                                         --if found set wielded item and remove old stack
248                                         if inv:get_stack("main", i):get_name() == old then
249                                                 local count = inv:get_stack("main", i):get_count()
250                                                 placer:set_wielded_item(old.." "..count)
251                                                 inv:set_stack("main",i,ItemStack(""))   
252                                                 minetest.sound_play("pickup", {
253                                                           to_player = player,
254                                                           gain = 0.7,
255                                                           pitch = math.random(60,100)/100
256                                                 })
257                                                 return                          
258                                         end
259                                 end
260                         end
261                 end
262         end,pos, newnode, placer, oldnode, itemstack, pointed_thing,old)
263 end)
264
265 local do_critical_particles = function(pos)
266         minetest.add_particlespawner({
267                 amount = 40,
268                 time = 0.001,
269                 minpos = pos,
270                 maxpos = pos,
271                 minvel = vector.new(-2,-2,-2),
272                 maxvel = vector.new(2,8,2),
273                 minacc = {x=0, y=4, z=0},
274                 maxacc = {x=0, y=12, z=0},
275                 minexptime = 1.1,
276                 maxexptime = 1.5,
277                 minsize = 1,
278                 maxsize = 2,
279                 collisiondetection = false,
280                 vertical = false,
281                 texture = "critical.png",
282         })
283 end
284
285 --we need to do this to override the default damage mechanics
286 minetest.register_on_joinplayer(function(player)
287         local meta = player:get_meta()
288         meta:set_float("player_punch_timer",0)
289 end)
290
291 minetest.register_globalstep(function(dtime)
292         for _,player in ipairs(minetest.get_connected_players()) do
293                 local meta = player:get_meta()
294                 local punch_timer = meta:get_float("player_punch_timer")
295
296                 --limit this so the game engine isn't calculating huge floats
297                 if punch_timer > 0 then
298                         punch_timer = punch_timer - dtime
299                         if punch_timer < 0 then punch_timer = 0 end
300                         meta:set_float("player_punch_timer",punch_timer)
301                 end
302         end
303 end)
304
305 --this throws the player when they're punched and activates the custom damage mechanics
306 minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
307         local meta = player:get_meta()
308         local punch_timer = meta:get_float("player_punch_timer")
309         local hurt = tool_capabilities.damage_groups.damage
310         local hp = player:get_hp()
311         if punch_timer <= 0 and hp > 0 then
312                 meta:set_float("player_punch_timer",0.25)
313                 if hitter:is_player() and hitter ~= player then
314                         local puncher_vel = hitter:get_player_velocity().y
315                         if puncher_vel < 0 then
316                                 hurt = hurt * 1.5
317                                 critical = true
318                                 do_critical_particles(player:get_pos())
319                                 minetest.sound_play("critical", {pos=player:get_pos(), gain = 0.1, max_hear_distance = 16,pitch = math.random(80,100)/100})
320                         end
321                 end
322
323                 dir = vector.multiply(dir,10)
324                 local vel = player:get_player_velocity()
325                 dir.y = 0
326                 if vel.y <= 0 then
327                         dir.y = 7
328                 end
329
330                 local hp_modifier = math.ceil(armor_class.calculate_armor_absorbtion(player)/3)
331                 --print("hp_modifier:",hp_modifier)
332                 armor_class.damage_armor(player,math.abs(hurt))
333
334                 --print("hurt:",hurt,"|","hp_modifier:",hp_modifier)
335                 local modify_output = (hurt == 0)
336                 
337                 hurt = hurt - hp_modifier
338
339                 if modify_output == false and hurt <= 0 then
340                         hurt = 1
341                 elseif modify_output == true then
342                         hurt = 0
343                 end
344                 player:add_player_velocity(dir)
345                 player:set_hp(hp-hurt)
346         end
347 end)
348
349