]> git.lizzy.rs Git - Crafter.git/blobdiff - mods/mob/api/interaction.lua
Implement armor defense and make mobs more powerful
[Crafter.git] / mods / mob / api / interaction.lua
index dd567e6e410b5154b84d23729788eed96d64a8af..695c4760708b8b2772fbdb6af778acb03124f5c2 100644 (file)
 --
 mobs.create_interaction_functions = function(def,mob_register)
-    --the sword wear mechanic
-    mob_register.add_sword_wear = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
-        if puncher:is_player() then
-            local itemstack = puncher:get_wielded_item()
-            local wear = itemstack:get_definition().mob_hit_wear
-            if wear then
-                itemstack:add_wear(wear)
-                if itemstack:get_name() == "" then
-                    minetest.sound_play("tool_break",{to_player = puncher:get_player_name(),gain=0.4})
-                end
-                puncher:set_wielded_item(itemstack)
-            end
-        end
-    end
+       --the sword wear mechanic
+       mob_register.add_sword_wear = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
+               if puncher:is_player() then
+                       local itemstack = puncher:get_wielded_item()
+                       local wear = itemstack:get_definition().mob_hit_wear
+                       if wear then
+                               itemstack:add_wear(wear)
+                               if itemstack:get_name() == "" then
+                                       minetest.sound_play("tool_break",{to_player = puncher:get_player_name(),gain=0.4})
+                               end
+                               puncher:set_wielded_item(itemstack)
+                       end
+               end
+       end
 
-    --critical effect particles
-    mob_register.do_critical_particles = function(pos)
-        minetest.add_particlespawner({
-            amount = 40,
-            time = 0.001,
-            minpos = pos,
-            maxpos = pos,
-            minvel = vector.new(-2,-2,-2),
-            maxvel = vector.new(2,8,2),
-            minacc = {x=0, y=4, z=0},
-            maxacc = {x=0, y=12, z=0},
-            minexptime = 1.1,
-            maxexptime = 1.5,
-            minsize = 1,
-            maxsize = 2,
-            collisiondetection = false,
-            vertical = false,
-            texture = "critical.png",
-        })
-    end
-    
-    mob_register.collision_detection = function(self)
-        local pos = self.object:get_pos()
-        --do collision detection from the base of the mob
-        pos.y = pos.y - self.object:get_properties().collisionbox[2]
-        for _,object in ipairs(minetest.get_objects_inside_radius(pos, self.collision_boundary)) do
-            if object:is_player() or object:get_luaentity().mob == true then
-                local pos2 = object:get_pos()
-                
-                local dir = vector.direction(pos,pos2)
-                dir.y = 0
-                
-                --eliminate mob being stuck in corners
-                if dir.x == 0 and dir.z == 0 then
-                    dir = vector.new(math.random(-1,1)*math.random(),0,math.random(-1,1)*math.random())
-                end
-                
-                local velocity = vector.multiply(dir,1.1)
-                
-                vel1 = vector.multiply(velocity, -1)
-                vel2 = velocity
-                self.object:add_velocity(vel1)
-                
-                if object:is_player() then
-                    object:add_player_velocity(vel2)
-                else
-                    object:add_velocity(vel2)
-                end
-            end
-        end
-    end
-    
-    mob_register.fall_damage = function(self)
-        local vel = self.object:get_velocity()
-        if vel and self.oldvel then
-           if self.oldvel.y < -7 and vel.y == 0 then
-              local damage = math.abs(self.oldvel.y + 7)
-              damage = math.floor(damage/1.5)
-              self.object:punch(self.object, 2, 
-                 {
-                  full_punch_interval=1.5,
-                  damage_groups = {damage=damage},
-                 })
-           end
-        end
-        self.oldvel = vel
-    end
+       --critical effect particles
+       mob_register.do_critical_particles = function(pos)
+               minetest.add_particlespawner({
+                       amount = 40,
+                       time = 0.001,
+                       minpos = pos,
+                       maxpos = pos,
+                       minvel = vector.new(-2,-2,-2),
+                       maxvel = vector.new(2,8,2),
+                       minacc = {x=0, y=4, z=0},
+                       maxacc = {x=0, y=12, z=0},
+                       minexptime = 1.1,
+                       maxexptime = 1.5,
+                       minsize = 1,
+                       maxsize = 2,
+                       collisiondetection = false,
+                       vertical = false,
+                       texture = "critical.png",
+               })
+       end
+       
+       mob_register.collision_detection = function(self)
+               local pos = self.object:get_pos()
+               --do collision detection from the base of the mob
+               
+               local collisionbox = self.object:get_properties().collisionbox
 
-    --this controls what happens when the mob gets punched
-    mob_register.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
-        local hp = self.hp
-        local vel = self.object:get_velocity()
-        local hurt = tool_capabilities.damage_groups.damage
-        
-        if not hurt then
-            hurt = 1
-        end
-        
-        local critical = false
-        
-        --criticals
-        local pos = self.object:get_pos()
-        if puncher:is_player() then
-            local puncher_vel = puncher:get_player_velocity().y
-            if puncher_vel < 0 then
-                hurt = hurt * 1.5
-                critical = true
-            end
-        end
-        
-        local hp = hp-hurt
-        
-        if (self.punched_timer <= 0 and hp > 1) then
-            if puncher ~= self.object then
-                self.hostile = true
-            end
-            self.hostile_timer = 20
-            self.punched_timer = 0.8
-            
-            --critical effect
-            if critical == true then
-                self.do_critical_particles(pos)
-                minetest.sound_play("critical", {object=self.object, gain = 0.1, max_hear_distance = 10,pitch = math.random(80,100)/100})
-            end
-            minetest.sound_play(self.hurt_sound, {object=self.object, gain = 1.0, max_hear_distance = 10,pitch = math.random(100,140)/100})
-            
-            self.hp = hp
-            
-            self.direction = vector.multiply(dir,-1)
-            dir = vector.multiply(dir,10)
-            if vel.y <= 0 then
-                dir.y = 4
-            else
-                dir.y = 0
-            end
-            
-            
-            self.object:add_velocity(dir)
-            self.add_sword_wear(self, puncher, time_from_last_punch, tool_capabilities, dir)
-        elseif (self.punched_timer <= 0 and self.death_animation_timer == 0) then
-            self.death_animation_timer = 1
-            self.dead = true
-            
-            --critical effect
-            if critical == true then
-                self.do_critical_particles(pos)
-                minetest.sound_play("critical", {object=self.object, gain = 0.1, max_hear_distance = 10,pitch = math.random(80,100)/100})
-            end
-            minetest.sound_play(self.die_sound, {object=self.object, gain = 1.0, max_hear_distance = 10,pitch = math.random(80,100)/100})
-            
-            self.object:set_texture_mod("^[colorize:red:130")
-            if self.child then
-            self.child:set_texture_mod("^[colorize:red:130") 
-            end
-            self.add_sword_wear(self, puncher, time_from_last_punch, tool_capabilities, dir)
-        end
-    end
+               pos.y = pos.y + collisionbox[2]
+               
+               local collision_boundary = collisionbox[4]
 
-    --this is what happens when a mob dies
-    mob_register.on_death = function(self, killer)
-        local pos = self.object:getpos()
-        --pos.y = pos.y + 0.4
-        minetest.sound_play("mob_die", {pos = pos, gain = 1.0})
-        minetest.add_particlespawner({
-            amount = 40,
-            time = 0.001,
-            minpos = pos,
-            maxpos = pos,
-            minvel = vector.new(-5,-5,-5),
-            maxvel = vector.new(5,5,5),
-            minacc = {x=0, y=0, z=0},
-            maxacc = {x=0, y=0, z=0},
-            minexptime = 1.1,
-            maxexptime = 1.5,
-            minsize = 1,
-            maxsize = 2,
-            collisiondetection = false,
-            vertical = false,
-            texture = "smoke.png",
-        })
-        
-        --only throw items if registered
-        if self.item_drop then
-            --detect if multiple items are going to be added
-            if self.item_amount  and self.item_minumum then
-                local data_item_amount = math.random(self.item_minimum, self.item_amount)
-                for i = self.item_minimum,data_item_amount do
-                    minetest.throw_item(pos,self.item_drop)
-                end
-            else
-                minetest.throw_item(pos,self.item_drop)
-            end
-        end
-            
-        
-        self.object:remove()
-        
-        if self.child then
-            self.child:get_luaentity().parent = nil
-        end
-    end
-    
-    --the pig will look for and at players
-    mob_register.look_around = function(self,dtime)
-        local pos = self.object:get_pos()
-        
-        if self.die_in_light and self.die_in_light_level and self.die_in_light == true then
-            local light_level = minetest.get_node_light(pos)
-            if light_level then
-                if (self.die_in_light == true and light_level > self.die_in_light_level) then
-                    local damage = self.hp
-                    self.object:punch(self.object, 2, 
-                        {
-                        full_punch_interval=1.5,
-                        damage_groups = {damage=damage},
-                    })
-                end
-            end
-        end
-        
-        --STARE O_O
-        --and follow!
-        self.following = false
-        local player_found = false
-        for _,object in ipairs(minetest.get_objects_inside_radius(pos, self.view_distance)) do
-            if object:is_player() and player_found == false and object:get_hp() > 0 then
-                --look at player's camera
-                local pos2 = object:get_pos()
-                pos2.y = pos2.y + 1.625
-                
-                player_found = true
-                
-                if self.child then
-                    self.move_head(self,pos2,dtime)
-                end
-                
-                if self.hostile == true then
-                    
-                    self.direction = vector.direction(pos,pos2)
-                    local distance = vector.distance(pos,pos2)-2
-                    if distance < 0 then
-                        distance = 0
-                    end
-                    
-                    --punch the player
-                    if self.attack_type == "punch" then
-                        if distance < 1 and self.punch_timer <= 0 and object:get_hp() > 0 then
-                            local line_of_sight = minetest.line_of_sight(pos, pos2)
-                            if line_of_sight == true then
-                                self.punch_timer = 1
-                                object:punch(self.object, 2, 
-                                    {
-                                    full_punch_interval=1.5,
-                                    damage_groups = {fleshy=2},
-                                },vector.direction(pos,pos2))
-                            end
-                        end
-                    elseif self.attack_type == "explode" then
-                        if distance <  self.explosion_radius then
-                            
-                            if not self.tnt_timer then
-                                minetest.sound_play("tnt_ignite", {object = self.object, gain = 1.0,})
-                                self.tnt_timer = self.explosion_time
-                                self.object:set_texture_mod("^[colorize:white:130")
-                                if self.child then
-                                    self.child:set_texture_mod("^[colorize:white:130") 
-                                end
-                            end
-                        end
-                    elseif self.attack_type == "projectile" then
-                        if not self.projectile_timer then
-                            self.projectile_timer = self.projectile_timer_cooldown
-                        end
-                        if self.projectile_timer <= 0 then
-                            self.projectile_timer = self.projectile_timer_cooldown
-                            
-                            local obj = minetest.add_entity(pos, self.projectile_type)
-                            if obj then
-                                local dir = vector.multiply(vector.direction(pos,pos2), 50)
-                                obj:set_velocity(dir)
-                                obj:get_luaentity().timer = 2
-                            end
-                        end
-                    end
-                    self.speed = distance * 4
-                    if self.speed > self.max_speed then
-                        self.speed = self.max_speed
-                    end
-                    self.following = true
-                end
-                --only look at one player
-                break
-            end
-        end
-        --stare straight if not found
-        if player_found == false then
-            if self.child then
-                self.move_head(self,nil,dtime)
-            end
-            if self.manage_hostile_timer then
-                self.manage_hostile_timer(self,dtime)
-            end
-        end
-    end
-    
-    return(mob_register)
-end
+               local radius = collision_boundary
+
+               if collisionbox[5] > collision_boundary then
+                       radius = collisionbox[5]
+               end
+
+               for _,object in ipairs(minetest.get_objects_inside_radius(pos, radius*1.25)) do
+                       if object ~= self.object and (object:is_player() or object:get_luaentity().mob == true) and
+                       --don't collide with rider, rider don't collide with thing
+                       (not object:get_attach() or (object:get_attach() and object:get_attach() ~= self.object)) and 
+                       (not self.object:get_attach() or (self.object:get_attach() and self.object:get_attach() ~= object)) then
+
+                               local pos2 = object:get_pos()
+                               
+                               local object_collisionbox = object:get_properties().collisionbox
+
+                               pos2.y = pos2.y + object_collisionbox[2]
+
+                               local object_collision_boundary = object_collisionbox[4]
+
+
+                               --this is checking the difference of the object collided with's possision
+                               --if positive top of other object is inside (y axis) of current object
+                               local y_base_diff = (pos2.y + object_collisionbox[5]) - pos.y
+
+                               local y_top_diff = (pos.y + collisionbox[5]) - pos2.y
+
+
+                               local distance = vector.distance(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z))
+
+                               if distance <= collision_boundary + object_collision_boundary and y_base_diff >= 0 and y_top_diff >= 0 then
+
+                                       local dir = vector.direction(pos,pos2)
+                                       dir.y = 0
+                                       
+                                       --eliminate mob being stuck in corners
+                                       if dir.x == 0 and dir.z == 0 then
+                                               dir = vector.new(math.random(-1,1)*math.random(),0,math.random(-1,1)*math.random())
+                                       end
+                                       
+                                       local velocity = vector.multiply(dir,1.1)
+                                       
+                                       local vel1 = vector.multiply(velocity, -1)
+                                       local vel2 = velocity
+                                       self.object:add_velocity(vel1)
+                                       
+                                       if object:is_player() then
+                                               object:add_player_velocity(vel2)
+                                       else
+                                               object:add_velocity(vel2)
+                                       end
+                               end
+                       end
+               end
+       end
+       if def.takes_fall_damage == nil or def.takes_fall_damage == true then
+               mob_register.fall_damage = function(self)
+                       local vel = self.object:get_velocity()
+                       if vel and self.oldvel then
+                               if self.oldvel.y < -7 and vel.y == 0 then
+                                       local damage = math.abs(self.oldvel.y + 7)
+                                       damage = math.floor(damage/1.5)
+                                       self.object:punch(self.object, 2, 
+                                               {
+                                               full_punch_interval=1.5,
+                                               damage_groups = {damage=damage},
+                                               })
+                               end
+                       end
+                       self.oldvel = vel
+               end
+       end
+
+       --this controls what happens when the mob gets punched
+       mob_register.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
+               local hp = self.hp
+               local vel = self.object:get_velocity()
+               local hurt = tool_capabilities.damage_groups.damage
+               
+               if not hurt then
+                       hurt = 1
+               end
+               
+               local critical = false
+               
+               --criticals
+               local pos = self.object:get_pos()
+               if puncher:is_player() then
+                       local puncher_vel = puncher:get_player_velocity().y
+                       if puncher_vel < 0 then
+                               hurt = hurt * 1.5
+                               critical = true
+                       end
+               end
+               
+               local hp = hp-hurt
+
+               if (self.punched_timer <= 0 and hp > 1) and not self.dead then
+                       self.object:set_texture_mod("^[colorize:"..self.damage_color..":130")
+                       self.hurt_color_timer = 0.25
+                       if puncher ~= self.object then
+                               self.punched_timer = 0.8
+                               if self.attacked_hostile then
+                                       self.hostile = true
+                                       self.hostile_timer = 20
+                                       if self.group_attack == true then
+                                               for _,object in ipairs(minetest.get_objects_inside_radius(pos, self.view_distance)) do
+
+                                                       if not object:is_player() and object:get_luaentity() and object:get_luaentity().mobname == self.mobname then
+                                                               object:get_luaentity().hostile = true
+                                                               object:get_luaentity().hostile_timer = 20
+                                                       end
+                                               end
+                                       end
+                               end
+                       end
+                       
+                       --critical effect
+                       if critical == true then
+                               self.do_critical_particles(pos)
+                               minetest.sound_play("critical", {object=self.object, gain = 0.1, max_hear_distance = 10,pitch = math.random(80,100)/100})
+                       end
+                       minetest.sound_play(self.hurt_sound, {object=self.object, gain = 1.0, max_hear_distance = 10,pitch = math.random(100,140)/100})
+                       
+                       self.hp = hp
+                       
+                       self.direction = vector.multiply(dir,-1)
+                       dir = vector.multiply(dir,10)
+                       if vel.y <= 0 then
+                               dir.y = 4
+                       else
+                               dir.y = 0
+                       end
+                       
+                       
+                       self.object:add_velocity(dir)
+                       self.add_sword_wear(self, puncher, time_from_last_punch, tool_capabilities, dir)
+               elseif (self.punched_timer <= 0 and self.death_animation_timer == 0) then
+                       self.object:set_texture_mod("^[colorize:"..self.damage_color..":130")
+                       self.hurt_color_timer = 0.25
+                       if puncher ~= self.object then
+                               self.punched_timer = 0.8
+                               if self.attacked_hostile then
+                                       self.hostile = true
+                                       self.hostile_timer = 20
+                                       if self.group_attack == true then
+                                               for _,object in ipairs(minetest.get_objects_inside_radius(pos, self.view_distance)) do
+                                                       if not object:is_player() and object:get_luaentity() and object:get_luaentity().mobname == self.mobname then
+                                                               object:get_luaentity().hostile = true
+                                                               object:get_luaentity().hostile_timer = 20
+                                                       end
+                                               end
+                                       end
+                               end
+                       end
+                       self.death_animation_timer = 1
+                       self.dead = true
+                       
+                       --critical effect
+                       if critical == true then
+                               self.do_critical_particles(pos)
+                               minetest.sound_play("critical", {object=self.object, gain = 0.1, max_hear_distance = 10,pitch = math.random(80,100)/100})
+                       end
+                       minetest.sound_play(self.die_sound, {object=self.object, gain = 1.0, max_hear_distance = 10,pitch = math.random(80,100)/100})
+                       self.add_sword_wear(self, puncher, time_from_last_punch, tool_capabilities, dir)
+               end
+       end
+
+       --this is what happens when a mob dies
+       mob_register.on_death = function(self, killer)
+               local pos = self.object:get_pos()
+               if def.hp then
+                       minetest.throw_experience(pos,math.ceil(def.hp/5)+math.random(0,1))
+               end
+               --pos.y = pos.y + 0.4
+               minetest.sound_play("mob_die", {pos = pos, gain = 1.0})
+               minetest.add_particlespawner({
+                       amount = 40,
+                       time = 0.001,
+                       minpos = pos,
+                       maxpos = pos,
+                       minvel = vector.new(-5,-5,-5),
+                       maxvel = vector.new(5,5,5),
+                       minacc = {x=0, y=0, z=0},
+                       maxacc = {x=0, y=0, z=0},
+                       minexptime = 1.1,
+                       maxexptime = 1.5,
+                       minsize = 1,
+                       maxsize = 2,
+                       collisiondetection = false,
+                       vertical = false,
+                       texture = "smoke.png",
+               })
+               
+               --only throw items if registered
+               if self.item_drop then
+                       local item
+                       if type(self.item_drop) == "string" then
+                               item = self.item_drop
+                       elseif type(self.item_drop) == "table" then
+                               item = self.item_drop[math.random(1,table.getn(self.item_drop))]
+                       end
+                       --detect if multiple items are going to be added
+                       if self.item_max then
+                               local data_item_amount = math.random(self.item_minimum, self.item_max)
+                               for i = 1 ,data_item_amount do
+                                       minetest.throw_item(vector.new(pos.x,pos.y+0.1,pos.z),item)
+                               end
+                       else
+                               minetest.throw_item(vector.new(pos.x,pos.y+0.1,pos.z),item)
+                       end
+               end
+                       
+               global_mob_amount = global_mob_amount - 1
+               --print("Mobs Died. Current Mobs: "..global_mob_amount)
+               
+               if self.custom_on_death then
+                       self.custom_on_death(self)
+               end
+
+               self.object:remove()
+       end
+       
+       --the pig will look for and at players
+       mob_register.look_around = function(self,dtime)
+               local pos = self.object:get_pos()
+               
+               if self.die_in_light and self.die_in_light_level and self.die_in_light == true then
+                       local light_level = minetest.get_node_light(pos)
+                       if light_level then
+                               if (self.die_in_light == true and light_level > self.die_in_light_level) then
+                                       self.object:punch(self.object, 2, 
+                                               {
+                                               full_punch_interval=1.5,
+                                               damage_groups = {damage=2},
+                                       })
+                               end
+                       end
+               end
+               
+               --STARE O_O
+               --and follow!
+               self.following = false
+               local player_found = false
+
+               for _,object in ipairs(minetest.get_objects_inside_radius(pos, self.view_distance)) do
+                       if object:is_player() and player_found == false and object:get_hp() > 0 then
+                               --look at player's camera
+                               local pos2 = object:get_pos()
+                               pos2.y = pos2.y + 1.625
+                               
+                               player_found = true
+                               
+                               if self.head_bone then
+                                       self.move_head(self,pos2,dtime)
+                               end
+                               
+                               --print(self.hostile)
+                               if self.hostile == true then
+                                       local distance = vector.distance(pos,pos2)
+                                       self.following_pos = vector.new(pos2.x,pos2.y-1.625,pos2.z)
+
+                                       --punch the player
+                                       if self.attack_type == "punch" then
+                                               if distance < 2.5 and self.punch_timer <= 0 and object:get_hp() > 0 then
+                                                       local line_of_sight = minetest.line_of_sight(pos, pos2)
+                                                       if line_of_sight == true then
+                                                               self.punch_timer = 1
+                                                               object:punch(self.object, 2, 
+                                                                       {
+                                                                       full_punch_interval=1.5,
+                                                                       damage_groups = {damage=self.attack_damage},
+                                                               },vector.direction(pos,pos2))
+                                                       end
+                                               end
+                                       elseif self.attack_type == "explode" then
+                                               --mob will not explode if it cannot see you
+                                               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
+                                                       
+                                                       if not self.tnt_timer then
+                                                               minetest.sound_play("tnt_ignite", {object = self.object, gain = 1.0,})
+                                                               self.tnt_timer = self.explosion_time
+                                                               self.tnt_tick_timer  = 0.2
+                                                               self.tnt_mod_state = 1
+                                                               self.object:set_texture_mod("^[colorize:white:130")
+                                                       end
+                                               end
+                                       elseif self.attack_type == "projectile" then
+                                               if not self.projectile_timer then
+                                                       self.projectile_timer = self.projectile_timer_cooldown
+                                               end
+                                               if self.projectile_timer <= 0 then
+                                                       self.projectile_timer = self.projectile_timer_cooldown
+                                                       
+                                                       local obj = minetest.add_entity(vector.new(pos.x,pos.y+self.object:get_properties().collisionbox[5],pos.z), self.projectile_type)
+                                                       if obj then
+                                                               local dir = vector.multiply(vector.direction(pos,vector.new(pos2.x,pos2.y-3,pos2.z)), 50)
+                                                               obj:set_velocity(dir)
+                                                               obj:get_luaentity().timer = 2
+                                                               obj:get_luaentity().owner = self.object
+                                                       end
+                                               end
+                                       end
+                                       --smart
+                                       if self.path_data and table.getn(self.path_data) > 0 then
+                                               self.direction = vector.direction(vector.new(pos.x,0,pos.z), vector.new(self.path_data[1].x,0,self.path_data[1].z))
+                                       --dumb
+                                       else
+                                               self.direction = vector.direction(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z))
+                                       end
+                                       self.speed = self.max_speed
+                                       self.following = true
+                               end
+                               --only look at one player
+                               break
+                       end
+               end
+               --stare straight if not found
+               if player_found == false then
+                       if self.move_head then
+                               self.move_head(self,nil,dtime)
+                       end
+                       if self.following_pos then
+                               self.following_pos = nil
+                       end
+                       if self.manage_hostile_timer then
+                               self.manage_hostile_timer(self,dtime)
+                       end
+               end
+       end
+       
+       return(mob_register)
+end
\ No newline at end of file