1 local minetest,math,vector = minetest,math,vector
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
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})
14 puncher:set_wielded_item(itemstack)
19 --critical effect particles
20 mob_register.do_critical_particles = function(pos)
21 minetest.add_particlespawner({
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},
34 collisiondetection = false,
36 texture = "critical.png",
40 mob_register.collision_detection = function(self)
41 local pos = self.object:get_pos()
42 --do collision detection from the base of the mob
44 local collisionbox = self.object:get_properties().collisionbox
46 pos.y = pos.y + collisionbox[2]
48 local collision_boundary = collisionbox[4]
50 local radius = collision_boundary
52 if collisionbox[5] > collision_boundary then
53 radius = collisionbox[5]
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
62 local pos2 = object:get_pos()
64 local object_collisionbox = object:get_properties().collisionbox
66 pos2.y = pos2.y + object_collisionbox[2]
68 local object_collision_boundary = object_collisionbox[4]
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
75 local y_top_diff = (pos.y + collisionbox[5]) - pos2.y
78 local distance = vector.distance(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z))
80 if distance <= collision_boundary + object_collision_boundary and y_base_diff >= 0 and y_top_diff >= 0 then
82 local dir = vector.direction(pos,pos2)
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())
90 local velocity = vector.multiply(dir,1.1)
92 local vel1 = vector.multiply(velocity, -1)
94 self.object:add_velocity(vel1)
96 if object:is_player() then
97 object:add_player_velocity(vel2)
101 local meta = object:get_meta()
102 if meta:get_int("on_fire") > 0 then
103 start_fire(self.object)
107 object:add_velocity(vel2)
108 if self.on_fire and not object:get_luaentity().on_fire then
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,
125 full_punch_interval=1.5,
126 damage_groups = {damage=damage},
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)
137 local vel = self.object:get_velocity()
138 local hurt = tool_capabilities.damage_groups.damage
144 local critical = false
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
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
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
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
177 self.scared_timer = 10
179 if self.custom_on_punch then
180 self.custom_on_punch(self)
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})
189 minetest.sound_play(self.hurt_sound, {object=self.object, gain = 1.0, max_hear_distance = 10,pitch = math.random(100,140)/100})
193 dir = vector.multiply(dir,10)
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
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
220 if self.custom_on_punch then
221 self.custom_on_punch(self)
224 self.death_animation_timer = 1
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})
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)
237 --this is what happens when a mob dies
238 mob_register.on_death = function(self, killer)
239 local pos = self.object:get_pos()
241 minetest.throw_experience(pos,math.ceil(def.hp/5)+math.random(0,1))
243 --pos.y = pos.y + 0.4
244 minetest.sound_play("mob_die", {pos = pos, gain = 1.0})
245 minetest.add_particlespawner({
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},
258 collisiondetection = false,
260 texture = "smoke.png",
263 --only throw items if registered
264 if self.item_drop then
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))]
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)
278 minetest.throw_item(vector.new(pos.x,pos.y+0.1,pos.z),item)
282 global_mob_amount = global_mob_amount - 1
283 --print("Mobs Died. Current Mobs: "..global_mob_amount)
285 if self.custom_on_death then
286 self.custom_on_death(self)
292 --the pig will look for and at players
293 mob_register.look_around = function(self,dtime)
294 local pos = self.object:get_pos()
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)
299 if (self.die_in_light == true and light_level > self.die_in_light_level) then
300 self.object:punch(self.object, 2,
302 full_punch_interval=1.5,
303 damage_groups = {damage=2},
311 self.following = false
312 local player_found = false
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
322 if self.head_bone then
323 self.move_head(self,pos2,dtime)
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)
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,
342 full_punch_interval=1.5,
343 damage_groups = {damage=self.attack_damage},
344 },vector.direction(pos,pos2))
345 --light the player on fire
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
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")
364 elseif self.attack_type == "projectile" then
365 if not self.projectile_timer then
366 self.projectile_timer = self.projectile_timer_cooldown
368 if self.projectile_timer <= 0 then
369 self.projectile_timer = self.projectile_timer_cooldown
371 local obj = minetest.add_entity(vector.new(pos.x,pos.y+self.object:get_properties().collisionbox[5],pos.z), self.projectile_type)
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
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))
385 self.direction = vector.direction(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z))
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
394 --only look at one player
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)
403 if self.following_pos then
404 self.following_pos = nil
406 if self.manage_hostile_timer then
407 self.manage_hostile_timer(self,dtime)