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