]> git.lizzy.rs Git - Crafter.git/blob - interaction.lua
9ed1df0dd6cd4d900e06f91cfe23bec4dab6952d
[Crafter.git] / interaction.lua
1 local minetest,math,vector = minetest,math,vector
2 --
3 mobs.create_interaction_functions = function(def,mob_register)
4         --the sword wear mechanic
5         mob_register.add_sword_wear = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
6                 if puncher:is_player() then
7                         local itemstack = puncher:get_wielded_item()
8                         local wear = itemstack:get_definition().mob_hit_wear
9                         if wear then
10                                 itemstack:add_wear(wear)
11                                 if itemstack:get_name() == "" then
12                                         minetest.sound_play("tool_break",{to_player = puncher:get_player_name(),gain=0.4})
13                                 end
14                                 puncher:set_wielded_item(itemstack)
15                         end
16                 end
17         end
18
19         --critical effect particles
20         mob_register.do_critical_particles = function(pos)
21                 minetest.add_particlespawner({
22                         amount = 40,
23                         time = 0.001,
24                         minpos = pos,
25                         maxpos = pos,
26                         minvel = vector.new(-2,-2,-2),
27                         maxvel = vector.new(2,8,2),
28                         minacc = {x=0, y=4, z=0},
29                         maxacc = {x=0, y=12, z=0},
30                         minexptime = 1.1,
31                         maxexptime = 1.5,
32                         minsize = 1,
33                         maxsize = 2,
34                         collisiondetection = false,
35                         vertical = false,
36                         texture = "critical.png",
37                 })
38         end
39         
40         mob_register.collision_detection = function(self)
41                 local pos = self.object:get_pos()
42                 --do collision detection from the base of the mob
43                 
44                 local collisionbox = self.object:get_properties().collisionbox
45
46                 pos.y = pos.y + collisionbox[2]
47                 
48                 local collision_boundary = collisionbox[4]
49
50                 local radius = collision_boundary
51
52                 if collisionbox[5] > collision_boundary then
53                         radius = collisionbox[5]
54                 end
55
56                 for _,object in ipairs(minetest.get_objects_inside_radius(pos, radius*1.25)) do
57                         if object ~= self.object and (object:is_player() or object:get_luaentity().mob == true) and
58                         --don't collide with rider, rider don't collide with thing
59                         (not object:get_attach() or (object:get_attach() and object:get_attach() ~= self.object)) and 
60                         (not self.object:get_attach() or (self.object:get_attach() and self.object:get_attach() ~= object)) then
61
62                                 local pos2 = object:get_pos()
63                                 
64                                 local object_collisionbox = object:get_properties().collisionbox
65
66                                 pos2.y = pos2.y + object_collisionbox[2]
67
68                                 local object_collision_boundary = object_collisionbox[4]
69
70
71                                 --this is checking the difference of the object collided with's possision
72                                 --if positive top of other object is inside (y axis) of current object
73                                 local y_base_diff = (pos2.y + object_collisionbox[5]) - pos.y
74
75                                 local y_top_diff = (pos.y + collisionbox[5]) - pos2.y
76
77
78                                 local distance = vector.distance(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z))
79
80                                 if distance <= collision_boundary + object_collision_boundary and y_base_diff >= 0 and y_top_diff >= 0 then
81
82                                         local dir = vector.direction(pos,pos2)
83                                         dir.y = 0
84                                         
85                                         --eliminate mob being stuck in corners
86                                         if dir.x == 0 and dir.z == 0 then
87                                                 dir = vector.new(math.random(-1,1)*math.random(),0,math.random(-1,1)*math.random())
88                                         end
89                                         
90                                         local velocity = vector.multiply(dir,1.1)
91                                         
92                                         local vel1 = vector.multiply(velocity, -1)
93                                         local vel2 = velocity
94                                         self.object:add_velocity(vel1)
95                                         
96                                         if object:is_player() then
97                                                 object:add_player_velocity(vel2)
98                                                 if self.on_fire then
99                                                         start_fire(object)
100                                                 else
101                                                         local meta = object:get_meta()
102                                                         if meta:get_int("on_fire") > 0 then
103                                                                 start_fire(self.object)
104                                                         end
105                                                 end
106                                         else
107                                                 object:add_velocity(vel2)
108                                                 if self.on_fire and not object:get_luaentity().on_fire then
109                                                         start_fire(object)
110                                                 end
111                                         end
112                                 end
113                         end
114                 end
115         end
116         if def.takes_fall_damage == nil or def.takes_fall_damage == true then
117                 mob_register.fall_damage = function(self)
118                         local vel = self.object:get_velocity()
119                         if vel and self.oldvel then
120                                 if self.oldvel.y < -7 and vel.y == 0 then
121                                         local damage = math.abs(self.oldvel.y + 7)
122                                         damage = math.floor(damage/1.5)
123                                         self.object:punch(self.object, 2, 
124                                                 {
125                                                 full_punch_interval=1.5,
126                                                 damage_groups = {damage=damage},
127                                                 })
128                                 end
129                         end
130                         self.oldvel = vel
131                 end
132         end
133
134         --this controls what happens when the mob gets punched
135         mob_register.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
136                 local hp = self.hp
137                 local vel = self.object:get_velocity()
138                 local hurt = tool_capabilities.damage_groups.damage
139                 
140                 if not hurt then
141                         hurt = 1
142                 end
143                 
144                 local critical = false
145                 
146                 --criticals
147                 local pos = self.object:get_pos()
148                 if puncher:is_player() then
149                         local puncher_vel = puncher:get_player_velocity().y
150                         if puncher_vel < 0 then
151                                 hurt = hurt * 1.5
152                                 critical = true
153                         end
154                 end
155                 
156                 local hp = hp-hurt
157
158                 if (self.punched_timer <= 0 and hp > 1) and not self.dead then
159                         self.object:set_texture_mod("^[colorize:"..self.damage_color..":130")
160                         self.hurt_color_timer = 0.25
161                         if puncher ~= self.object then
162                                 self.punched_timer = 0.25
163                                 if self.attacked_hostile then
164                                         self.hostile = true
165                                         self.hostile_timer = 20
166                                         if self.group_attack == true then
167                                                 for _,object in ipairs(minetest.get_objects_inside_radius(pos, self.view_distance)) do
168
169                                                         if not object:is_player() and object:get_luaentity() and object:get_luaentity().mobname == self.mobname then
170                                                                 object:get_luaentity().hostile = true
171                                                                 object:get_luaentity().hostile_timer = 20
172                                                         end
173                                                 end
174                                         end
175                                 else
176                                         self.scared = true
177                                         self.scared_timer = 10
178                                 end
179                                 if self.custom_on_punch then
180                                         self.custom_on_punch(self)
181                                 end
182                         end
183                         
184                         --critical effect
185                         if critical == true then
186                                 self.do_critical_particles(pos)
187                                 minetest.sound_play("critical", {object=self.object, gain = 0.1, max_hear_distance = 10,pitch = math.random(80,100)/100})
188                         end
189                         minetest.sound_play(self.hurt_sound, {object=self.object, gain = 1.0, max_hear_distance = 10,pitch = math.random(100,140)/100})
190                         
191                         self.hp = hp
192                         
193                         dir = vector.multiply(dir,10)
194                         if vel.y <= 0 then
195                                 dir.y = 4
196                         else
197                                 dir.y = 0
198                         end
199                         
200                         
201                         self.object:add_velocity(dir)
202                         self.add_sword_wear(self, puncher, time_from_last_punch, tool_capabilities, dir)
203                 elseif (self.punched_timer <= 0 and self.death_animation_timer == 0) then
204                         self.object:set_texture_mod("^[colorize:"..self.damage_color..":130")
205                         self.hurt_color_timer = 0.25
206                         if puncher ~= self.object then
207                                 self.punched_timer = 0.25
208                                 if self.attacked_hostile then
209                                         self.hostile = true
210                                         self.hostile_timer = 20
211                                         if self.group_attack == true then
212                                                 for _,object in ipairs(minetest.get_objects_inside_radius(pos, self.view_distance)) do
213                                                         if not object:is_player() and object:get_luaentity() and object:get_luaentity().mobname == self.mobname then
214                                                                 object:get_luaentity().hostile = true
215                                                                 object:get_luaentity().hostile_timer = 20
216                                                         end
217                                                 end
218                                         end
219                                 end
220                                 if self.custom_on_punch then
221                                         self.custom_on_punch(self)
222                                 end
223                         end
224                         self.death_animation_timer = 1
225                         self.dead = true
226                         
227                         --critical effect
228                         if critical == true then
229                                 self.do_critical_particles(pos)
230                                 minetest.sound_play("critical", {object=self.object, gain = 0.1, max_hear_distance = 10,pitch = math.random(80,100)/100})
231                         end
232                         minetest.sound_play(self.die_sound, {object=self.object, gain = 1.0, max_hear_distance = 10,pitch = math.random(80,100)/100})
233                         self.add_sword_wear(self, puncher, time_from_last_punch, tool_capabilities, dir)
234                 end
235         end
236
237         --this is what happens when a mob dies
238         mob_register.on_death = function(self, killer)
239                 local pos = self.object:get_pos()
240                 if def.hp then
241                         minetest.throw_experience(pos,math.ceil(def.hp/5)+math.random(0,1))
242                 end
243                 --pos.y = pos.y + 0.4
244                 minetest.sound_play("mob_die", {pos = pos, gain = 1.0})
245                 minetest.add_particlespawner({
246                         amount = 40,
247                         time = 0.001,
248                         minpos = pos,
249                         maxpos = pos,
250                         minvel = vector.new(-5,-5,-5),
251                         maxvel = vector.new(5,5,5),
252                         minacc = {x=0, y=0, z=0},
253                         maxacc = {x=0, y=0, z=0},
254                         minexptime = 1.1,
255                         maxexptime = 1.5,
256                         minsize = 1,
257                         maxsize = 2,
258                         collisiondetection = false,
259                         vertical = false,
260                         texture = "smoke.png",
261                 })
262                 
263                 --only throw items if registered
264                 if self.item_drop then
265                         local item
266                         if type(self.item_drop) == "string" then
267                                 item = self.item_drop
268                         elseif type(self.item_drop) == "table" then
269                                 item = self.item_drop[math.random(1,table.getn(self.item_drop))]
270                         end
271                         --detect if multiple items are going to be added
272                         if self.item_max then
273                                 local data_item_amount = math.random(self.item_minimum, self.item_max)
274                                 for i = 1 ,data_item_amount do
275                                         minetest.throw_item(vector.new(pos.x,pos.y+0.1,pos.z),item)
276                                 end
277                         else
278                                 minetest.throw_item(vector.new(pos.x,pos.y+0.1,pos.z),item)
279                         end
280                 end
281                         
282                 global_mob_amount = global_mob_amount - 1
283                 --print("Mobs Died. Current Mobs: "..global_mob_amount)
284                 
285                 if self.custom_on_death then
286                         self.custom_on_death(self)
287                 end
288
289                 self.object:remove()
290         end
291         
292         --the pig will look for and at players
293         mob_register.look_around = function(self,dtime)
294                 local pos = self.object:get_pos()
295                 
296                 if self.die_in_light and self.die_in_light_level and self.die_in_light == true then
297                         local light_level = minetest.get_node_light(pos)
298                         if light_level then
299                                 if (self.die_in_light == true and light_level > self.die_in_light_level) then
300                                         self.object:punch(self.object, 2, 
301                                                 {
302                                                 full_punch_interval=1.5,
303                                                 damage_groups = {damage=2},
304                                         })
305                                 end
306                         end
307                 end
308                 
309                 --STARE O_O
310                 --and follow!
311                 self.following = false
312                 local player_found = false
313
314                 for _,object in ipairs(minetest.get_objects_inside_radius(pos, self.view_distance)) do
315                         if object:is_player() and player_found == false and object:get_hp() > 0 then
316                                 --look at player's camera
317                                 local pos2 = object:get_pos()
318                                 pos2.y = pos2.y + 1.625
319                                 
320                                 player_found = true
321                                 
322                                 if self.head_bone then
323                                         self.move_head(self,pos2,dtime)
324                                 end
325                                 
326                                 --print(self.hostile)
327                                 if self.hostile == true then
328                                         local distance = vector.distance(pos,pos2)
329                                         self.following_pos = vector.new(pos2.x,pos2.y-1.625,pos2.z)
330
331                                         --punch the player
332                                         if self.attack_type == "punch" then
333                                                 if distance < 2.5 and self.punch_timer <= 0 and object:get_hp() > 0 then
334                                                         local meta = object:get_meta()
335                                                         local player_punch_timer = meta:get_float("player_punch_timer")
336                                                         if player_punch_timer <= 0 then
337                                                                 local line_of_sight = minetest.line_of_sight(pos, pos2)
338                                                                 if line_of_sight == true then
339                                                                         self.punch_timer = 0.25
340                                                                         object:punch(self.object, 2, 
341                                                                                 {
342                                                                                 full_punch_interval=1.5,
343                                                                                 damage_groups = {damage=self.attack_damage},
344                                                                         },vector.direction(pos,pos2))
345                                                                         --light the player on fire
346                                                                         if self.on_fire then
347                                                                                 start_fire(object)
348                                                                         end
349                                                                 end
350                                                         end
351                                                 end
352                                         elseif self.attack_type == "explode" then
353                                                 --mob will not explode if it cannot see you
354                                                 if distance <  self.explosion_radius and minetest.line_of_sight(vector.new(pos.x,pos.y+self.object:get_properties().collisionbox[5],pos.z), pos2) then
355                                                         
356                                                         if not self.tnt_timer then
357                                                                 minetest.sound_play("tnt_ignite", {object = self.object, gain = 1.0,})
358                                                                 self.tnt_timer = self.explosion_time
359                                                                 self.tnt_tick_timer  = 0.2
360                                                                 self.tnt_mod_state = 1
361                                                                 self.object:set_texture_mod("^[colorize:white:130")
362                                                         end
363                                                 end
364                                         elseif self.attack_type == "projectile" then
365                                                 if not self.projectile_timer then
366                                                         self.projectile_timer = self.projectile_timer_cooldown
367                                                 end
368                                                 if self.projectile_timer <= 0 then
369                                                         self.projectile_timer = self.projectile_timer_cooldown
370                                                         
371                                                         local obj = minetest.add_entity(vector.new(pos.x,pos.y+self.object:get_properties().collisionbox[5],pos.z), self.projectile_type)
372                                                         if obj then
373                                                                 local dir = vector.multiply(vector.direction(pos,vector.new(pos2.x,pos2.y-3,pos2.z)), 50)
374                                                                 obj:set_velocity(dir)
375                                                                 obj:get_luaentity().timer = 2
376                                                                 obj:get_luaentity().owner = self.object
377                                                         end
378                                                 end
379                                         end
380                                         --smart
381                                         if self.path_data and table.getn(self.path_data) > 0 then
382                                                 self.direction = vector.direction(vector.new(pos.x,0,pos.z), vector.new(self.path_data[1].x,0,self.path_data[1].z))
383                                         --dumb
384                                         else
385                                                 self.direction = vector.direction(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z))
386                                         end
387                                         self.speed = self.max_speed
388                                         self.following = true
389                                 elseif self.scared == true then
390                                         self.speed = self.max_speed
391                                         self.direction = vector.direction(vector.new(pos2.x,0,pos2.z),vector.new(pos.x,0,pos.z))
392                                         self.scared_timer = 10
393                                 end
394                                 --only look at one player
395                                 break
396                         end
397                 end
398                 --stare straight if not found
399                 if player_found == false then
400                         if self.move_head then
401                                 self.move_head(self,nil,dtime)
402                         end
403                         if self.following_pos then
404                                 self.following_pos = nil
405                         end
406                         if self.manage_hostile_timer then
407                                 self.manage_hostile_timer(self,dtime)
408                         end
409                 end
410         end
411         
412         return(mob_register)
413 end