VoxeLibre/mods/ENTITIES/mcl_mobs/api/mob_functions/ai.lua

1116 lines
29 KiB
Lua

local math = math
local vector = vector
local string = string
local tonumber = tonumber
local minetest_yaw_to_dir = minetest.yaw_to_dir
local minetest_get_item_group = minetest.get_item_group
local minetest_get_node = minetest.get_node
local minetest_line_of_sight = minetest.line_of_sight
local minetest_get_node_light = minetest.get_node_light
local DOUBLE_PI = math.pi * 2
local THIRTY_SECONDTH_PI = DOUBLE_PI * 0.03125
--a simple helper function which is too small to move into movement.lua
local quick_rotate = function(self,dtime)
self.yaw = self.yaw + THIRTY_SECONDTH_PI
if self.yaw > DOUBLE_PI then
self.yaw = self.yaw - DOUBLE_PI
end
end
--a simple helper function for rounding
--http://lua-users.org/wiki/SimpleRound
local function round2(num, numDecimalPlaces)
return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num))
end
--[[
_ _
| | | |
| | __ _ _ __ __| |
| | / _` | '_ \ / _` |
| |___| (_| | | | | (_| |
\_____/\__,_|_| |_|\__,_|
]]--
--this is basically reverse jump_check
local cliff_check = function(self,dtime)
--mobs will flip out if they are falling without this
if self.object:get_velocity().y ~= 0 then
return false
end
local pos = self.object:get_pos()
local dir = minetest_yaw_to_dir(self.yaw)
local collisionbox = self.object:get_properties().collisionbox
local radius = collisionbox[4] + 0.5
dir = vector.multiply(dir,radius)
local free_fall = minetest_line_of_sight(
{x = pos.x + dir.x, y = pos.y, z = pos.z + dir.z},
{x = pos.x + dir.x, y = pos.y - self.fear_height, z = pos.z + dir.z})
return free_fall
end
-- state switching logic (stand, walk, run, attacks)
local land_state_list_wandering = {"stand", "walk"}
local function land_state_switch(self, dtime)
--do math before sure not attacking, following, or running away so continue
--doing random walking for mobs if all states are not met
self.state_timer = self.state_timer - dtime
--only run away
if self.skittish and self.state == "run" then
self.run_timer = self.run_timer - dtime
if self.run_timer > 0 then
return
end
--continue
end
--ignore everything else if breeding
if self.breed_lookout_timer and self.breed_lookout_timer > 0 then
self.state = "breed"
return
--reset the state timer to get the mob out of
--the breed state
elseif self.state == "breed" then
self.state_timer = 0
end
--ignore everything else if following
if mobs.check_following(self) and
(not self.breed_lookout_timer or (self.breed_lookout_timer and self.breed_lookout_timer == 0)) and
(not self.breed_timer or (self.breed_timer and self.breed_timer == 0)) then
self.state = "follow"
return
--reset the state timer to get the mob out of
--the follow state - not the cleanest option
--but the easiest
elseif self.state == "follow" then
self.state_timer = 0
end
--only attack
if self.hostile and self.attacking then
self.state = "attack"
return
end
--if finally reached here then do random wander
if self.state_timer <= 0 then
self.state_timer = math.random(4,10) + math.random()
self.state = land_state_list_wandering[math.random(1,#land_state_list_wandering)]
end
end
-- states are executed here
local land_state_execution = function(self,dtime)
--[[ -- this is a debug which shows the timer and makes mobs breed 100 times faster
print(self.breed_timer)
if self.breed_timer > 0 then
self.breed_timer = self.breed_timer - (dtime * 100)
if self.breed_timer <= 0 then
self.breed_timer = 0
end
end
]]--
--no collisionbox exception
if not self.object:get_properties() then
return
end
--timer to time out looking for mate
if self.breed_lookout_timer and self.breed_lookout_timer > 0 then
self.breed_lookout_timer = self.breed_lookout_timer - dtime
--looking for mate failed
if self.breed_lookout_timer <= 0 then
self.breed_lookout_timer = 0
end
end
--cool off after breeding
if self.breed_timer and self.breed_timer > 0 then
self.breed_timer = self.breed_timer - dtime
--do this to skip the first check, using as switch
if self.breed_timer <= 0 then
self.breed_timer = 0
end
end
local pos = self.object:get_pos()
local collisionbox = self.object:get_properties().collisionbox
--get the center of the mob
pos.y = pos.y + (collisionbox[2] + collisionbox[5] / 2)
local current_node = minetest_get_node(pos).name
local float_now = false
--recheck if in water or lava
if minetest_get_item_group(current_node, "water") ~= 0 or minetest_get_item_group(current_node, "lava") ~= 0 then
float_now = true
end
--make slow falling mobs fall slow
if self.fall_slow then
local velocity = self.object:get_velocity()
if velocity then
if velocity.y < 0 then
--lua is acting really weird so we have to help it
if round2(self.object:get_acceleration().y, 1) == -self.gravity then
self.object:set_acceleration(vector.new(0,0,0))
mobs.mob_fall_slow(self)
end
else
if round2(self.object:get_acceleration().y, 1) == 0 then
self.object:set_acceleration(vector.new(0,-self.gravity,0))
end
end
end
end
--calculate fall damage
if self.fall_damage then
mobs.calculate_fall_damage(self)
end
if self.state == "stand" then
--do animation
mobs.set_mob_animation(self, "stand")
--set the velocity of the mob
mobs.set_velocity(self,0)
--animation fixes for explosive mobs
if self.attack_type == "explode" then
mobs.reverse_explosion_animation(self,dtime)
end
mobs.lock_yaw(self)
elseif self.state == "follow" then
--always look at players
mobs.set_yaw_while_following(self)
--check distance
local distance_from_follow_person = vector.distance(self.object:get_pos(), self.following_person:get_pos())
local distance_2d = mobs.get_2d_distance(self.object:get_pos(), self.following_person:get_pos())
--don't push the player if too close
--don't spin around randomly
if self.follow_distance < distance_from_follow_person and self.minimum_follow_distance < distance_2d then
mobs.set_mob_animation(self, "run")
mobs.set_velocity(self,self.run_velocity)
if mobs.jump_check(self) == 1 then
mobs.jump(self)
end
else
mobs.set_mob_animation(self, "stand")
mobs.set_velocity(self,0)
end
elseif self.state == "walk" then
self.walk_timer = self.walk_timer - dtime
--reset the walk timer
if self.walk_timer <= 0 then
--re-randomize the walk timer
self.walk_timer = math.random(1,6) + math.random()
--set the mob into a random direction
self.yaw = (math.random() * (math.pi * 2))
end
--do animation
mobs.set_mob_animation(self, "walk")
--enable rotation locking
mobs.movement_rotation_lock(self)
--check for nodes to jump over
local node_in_front_of = mobs.jump_check(self)
if node_in_front_of == 1 then
mobs.jump(self)
--turn if on the edge of cliff
--(this is written like this because unlike
--jump_check which simply tells the mob to jump
--this requires a mob to turn, removing the
--ease of a full implementation for it in a single
--function)
elseif node_in_front_of == 2 or (self.fear_height ~= 0 and cliff_check(self,dtime)) then
--turn 45 degrees if so
quick_rotate(self,dtime)
--stop the mob so it doesn't fall off
mobs.set_velocity(self,0)
end
--only move forward if path is clear
if node_in_front_of == 0 or node_in_front_of == 1 then
--set the velocity of the mob
mobs.set_velocity(self,self.walk_velocity)
end
--animation fixes for explosive mobs
if self.attack_type == "explode" then
mobs.reverse_explosion_animation(self,dtime)
end
elseif self.state == "run" then
--do animation
mobs.set_mob_animation(self, "run")
--enable rotation locking
mobs.movement_rotation_lock(self)
--check for nodes to jump over
local node_in_front_of = mobs.jump_check(self)
if node_in_front_of == 1 then
mobs.jump(self)
--turn if on the edge of cliff
--(this is written like this because unlike
--jump_check which simply tells the mob to jump
--this requires a mob to turn, removing the
--ease of a full implementation for it in a single
--function)
elseif node_in_front_of == 2 or (self.fear_height ~= 0 and cliff_check(self,dtime)) then
--turn 45 degrees if so
quick_rotate(self,dtime)
--stop the mob so it doesn't fall off
mobs.set_velocity(self,0)
end
--only move forward if path is clear
if node_in_front_of == 0 or node_in_front_of == 1 then
--set the velocity of the mob
mobs.set_velocity(self,self.run_velocity)
end
elseif self.state == "attack" then
--execute mob attack type
if self.attack_type == "explode" then
mobs.explode_attack_walk(self, dtime)
elseif self.attack_type == "punch" then
mobs.punch_attack_walk(self,dtime)
elseif self.attack_type == "projectile" then
mobs.projectile_attack_walk(self,dtime)
end
elseif self.state == "breed" then
mobs.breeding_effect(self)
local mate = mobs.look_for_mate(self)
--found a mate
if mate then
mobs.set_yaw_while_breeding(self,mate)
mobs.set_velocity(self, self.walk_velocity)
--smoosh together basically
if vector.distance(self.object:get_pos(), mate:get_pos()) <= self.breed_distance then
mobs.set_mob_animation(self, "stand")
if self.special_breed_timer == 0 then
self.special_breed_timer = 2 --breeding takes 2 seconds
end
self.special_breed_timer = self.special_breed_timer - dtime
if self.special_breed_timer <= 0 then
--pop a baby out, it's a miracle!
local baby_pos = vector.divide(vector.add(self.object:get_pos(), mate:get_pos()), 2)
minetest.add_entity(baby_pos, self.name, minetest.serialize({baby = true, grow_up_timer = self.grow_up_goal, bred = true}))
mobs.play_sound_specific(self,"item_drop_pickup")
self.special_breed_timer = 0
self.breed_lookout_timer = 0
self.breed_timer = self.breed_timer_cooloff
mate:get_luaentity().special_breed_timer = 0
mate:get_luaentity().breed_lookout_timer = 0
mate:get_luaentity().breed_timer = self.breed_timer_cooloff -- can reuse because it's the same mob
end
else
mobs.set_mob_animation(self, "walk")
end
--couldn't find a mate, just stand there until the player pushes it towards one
--or the timer runs out
else
mobs.set_mob_animation(self, "stand")
mobs.set_velocity(self,0)
end
end
if float_now then
mobs.float(self)
else
local acceleration = self.object:get_acceleration()
if acceleration and acceleration.y == 0 then
self.object:set_acceleration(vector.new(0,-self.gravity,0))
end
end
end
--[[
_____ _
/ ___| (_)
\ `--.__ ___ _ __ ___
`--. \ \ /\ / / | '_ ` _ \
/\__/ /\ V V /| | | | | | |
\____/ \_/\_/ |_|_| |_| |_|
]]--
-- state switching logic (stand, walk, run, attacks)
local swim_state_list_wandering = {"stand", "swim"}
local swim_state_switch = function(self, dtime)
self.state_timer = self.state_timer - dtime
if self.state_timer <= 0 then
self.state_timer = math.random(4,10) + math.random()
self.state = swim_state_list_wandering[math.random(1,#swim_state_list_wandering)]
end
end
--check if a mob needs to turn while swimming
local swim_turn_check = function(self,dtime)
local pos = self.object:get_pos()
pos.y = pos.y + 0.1
local dir = minetest_yaw_to_dir(self.yaw)
local collisionbox = self.object:get_properties().collisionbox
local radius = collisionbox[4] + 0.5
vector.multiply(dir, radius)
local test_dir = vector.add(pos,dir)
local green_flag_1 = minetest_get_item_group(minetest_get_node(test_dir).name, "solid") ~= 0
return green_flag_1
end
--this is to swap the built in engine acceleration modifier
local swim_physics_swapper = function(self,inside_swim_node)
--should be swimming, gravity is applied, switch to floating
if inside_swim_node and self.object:get_acceleration().y ~= 0 then
self.object:set_acceleration(vector.new(0,0,0))
--not be swim, gravity isn't applied, switch to falling
elseif not inside_swim_node and self.object:get_acceleration().y == 0 then
self.pitch = 0
self.object:set_acceleration(vector.new(0,-self.gravity,0))
end
end
local random_pitch_multiplier = {-1,1}
-- states are executed here
local swim_state_execution = function(self,dtime)
local pos = self.object:get_pos()
pos.y = pos.y + self.object:get_properties().collisionbox[5]
local current_node = minetest_get_node(pos).name
local inside_swim_node = false
--quick scan everything to see if inside swim node
for _,id in pairs(self.swim_in) do
if id == current_node then
inside_swim_node = true
break
end
end
--turn gravity on or off
swim_physics_swapper(self,inside_swim_node)
--swim properly if inside swim node
if inside_swim_node then
if self.state == "stand" then
--do animation
mobs.set_mob_animation(self, "stand")
mobs.set_swim_velocity(self,0)
if self.tilt_swim then
mobs.set_static_pitch(self)
end
mobs.lock_yaw(self)
elseif self.state == "swim" then
self.walk_timer = self.walk_timer - dtime
--reset the walk timer
if self.walk_timer <= 0 then
--re-randomize the walk timer
self.walk_timer = math.random(1,6) + math.random()
--set the mob into a random direction
self.yaw = (math.random() * (math.pi * 2))
--create a truly random pitch, since there is no easy access to pitch math that I can find
self.pitch = math.random() * math.random(1,3) * random_pitch_multiplier[math.random(1,2)]
end
--do animation
mobs.set_mob_animation(self, "walk")
--do a quick turn to make mob continuously move
--if in a fish tank or something
if swim_turn_check(self,dtime) then
quick_rotate(self,dtime)
end
mobs.set_swim_velocity(self,self.walk_velocity)
--only enable tilt swimming if enabled
if self.tilt_swim then
mobs.set_dynamic_pitch(self)
end
--enable rotation locking
mobs.movement_rotation_lock(self)
end
--flop around if not inside swim node
else
--do animation
mobs.set_mob_animation(self, "stand")
mobs.flop(self)
if self.tilt_swim then
mobs.set_static_pitch(self)
end
end
end
--[[
______ _
| ___| |
| |_ | |_ _
| _| | | | | |
| | | | |_| |
\_| |_|\__, |
__/ |
|___/
]]--
-- state switching logic (stand, walk, run, attacks)
local fly_state_list_wandering = {"stand", "fly"}
local fly_state_switch = function(self, dtime)
if self.hostile and self.attacking then
self.state = "attack"
return
end
self.state_timer = self.state_timer - dtime
if self.state_timer <= 0 then
self.state_timer = math.random(4,10) + math.random()
self.state = fly_state_list_wandering[math.random(1,#fly_state_list_wandering)]
end
end
--check if a mob needs to turn while flying
local fly_turn_check = function(self,dtime)
local pos = self.object:get_pos()
pos.y = pos.y + 0.1
local dir = minetest_yaw_to_dir(self.yaw)
local collisionbox = self.object:get_properties().collisionbox
local radius = collisionbox[4] + 0.5
vector.multiply(dir, radius)
local test_dir = vector.add(pos,dir)
local green_flag_1 = minetest_get_item_group(minetest_get_node(test_dir).name, "solid") ~= 0
return green_flag_1
end
--this is to swap the built in engine acceleration modifier
local fly_physics_swapper = function(self,inside_fly_node)
--should be flyming, gravity is applied, switch to floating
if inside_fly_node and self.object:get_acceleration().y ~= 0 then
self.object:set_acceleration(vector.new(0,0,0))
--not be fly, gravity isn't applied, switch to falling
elseif not inside_fly_node and self.object:get_acceleration().y == 0 then
self.pitch = 0
self.object:set_acceleration(vector.new(0,-self.gravity,0))
end
end
local random_pitch_multiplier = {-1,1}
-- states are executed here
local fly_state_execution = function(self,dtime)
local pos = self.object:get_pos()
pos.y = pos.y + 0.1
local current_node = minetest_get_node(pos).name
local inside_fly_node = minetest_get_item_group(current_node, "solid") == 0
local float_now = false
--recheck if in water or lava
if minetest_get_item_group(current_node, "water") ~= 0 or minetest_get_item_group(current_node, "lava") ~= 0 then
inside_fly_node = false
float_now = true
end
--turn gravity on or off
fly_physics_swapper(self,inside_fly_node)
--fly properly if inside fly node
if inside_fly_node then
if self.state == "stand" then
--do animation
mobs.set_mob_animation(self, "stand")
mobs.set_fly_velocity(self,0)
if self.tilt_fly then
mobs.set_static_pitch(self)
end
mobs.lock_yaw(self)
elseif self.state == "fly" then
self.walk_timer = self.walk_timer - dtime
--reset the walk timer
if self.walk_timer <= 0 then
--re-randomize the walk timer
self.walk_timer = math.random(1,6) + math.random()
--set the mob into a random direction
self.yaw = (math.random() * (math.pi * 2))
--create a truly random pitch, since there is no easy access to pitch math that I can find
self.pitch = math.random() * math.random(1,3) * random_pitch_multiplier[math.random(1,2)]
end
--do animation
mobs.set_mob_animation(self, "walk")
--do a quick turn to make mob continuously move
--if in a bird cage or something
if fly_turn_check(self,dtime) then
quick_rotate(self,dtime)
end
if self.tilt_fly then
mobs.set_dynamic_pitch(self)
end
mobs.set_fly_velocity(self,self.walk_velocity)
--enable rotation locking
mobs.movement_rotation_lock(self)
elseif self.state == "attack" then
--execute mob attack type
--if self.attack_type == "explode" then
--mobs.explode_attack_fly(self, dtime)
--elseif self.attack_type == "punch" then
--mobs.punch_attack_fly(self,dtime)
if self.attack_type == "projectile" then
mobs.projectile_attack_fly(self,dtime)
end
end
else
--make the mob float
if self.floats and float_now then
mobs.set_velocity(self, 0)
mobs.float(self)
if self.tilt_fly then
mobs.set_static_pitch(self)
end
end
end
end
--[[
___
|_ |
| |_ _ _ __ ___ _ __
| | | | | '_ ` _ \| '_ \
/\__/ / |_| | | | | | | |_) |
\____/ \__,_|_| |_| |_| .__/
| |
|_|
]]--
--check if a mob needs to turn while jumping
--[[local function jump_turn_check(self, dtime)
local pos = self.object:get_pos()
pos.y = pos.y + 0.1
local dir = minetest_yaw_to_dir(self.yaw)
local collisionbox = self.object:get_properties().collisionbox
local radius = collisionbox[4] + 0.5
vector.multiply(dir, radius)
local test_dir = vector.add(pos,dir)
local green_flag_1 = minetest_get_item_group(minetest_get_node(test_dir).name, "solid") ~= 0
return green_flag_1
end]]
-- state switching logic (stand, jump, run, attacks)
local jump_state_list_wandering = {"stand", "jump"}
local function jump_state_switch(self, dtime)
self.state_timer = self.state_timer - dtime
if self.state_timer <= 0 then
self.state_timer = math.random(4,10) + math.random()
self.state = jump_state_list_wandering[math.random(1,#jump_state_list_wandering)]
end
end
-- states are executed here
local function jump_state_execution(self, dtime)
local node_in_front_of = mobs.jump_check(self)
local pos = self.object:get_pos()
local collisionbox = self.object:get_properties().collisionbox
--get the center of the mob
pos.y = pos.y + (collisionbox[2] + collisionbox[5] / 2)
local current_node = minetest_get_node(pos).name
local float_now = false
--recheck if in water or lava
if minetest_get_item_group(current_node, "water") ~= 0 or minetest_get_item_group(current_node, "lava") ~= 0 then
float_now = true
end
if self.state == "stand" then
--do animation
mobs.set_mob_animation(self, "stand")
--set the velocity of the mob
mobs.set_velocity(self,0)
mobs.lock_yaw(self)
elseif self.state == "jump" then
self.walk_timer = self.walk_timer - dtime
--reset the jump timer
if self.walk_timer <= 0 then
--re-randomize the jump timer
self.walk_timer = math.random(1,6) + math.random()
--set the mob into a random direction
self.yaw = (math.random() * (math.pi * 2))
end
--do animation
mobs.set_mob_animation(self, "walk")
--enable rotation locking
mobs.movement_rotation_lock(self)
--jumping mobs are more loosey goosey
if node_in_front_of == 1 then
quick_rotate(self,dtime)
end
--only move forward if path is clear
mobs.jump_move(self,self.walk_velocity)
elseif self.state == "run" then
print("run")
elseif self.state == "attack" then
print("attack")
end
if float_now then
mobs.float(self)
end
end
--[[
___ ___ _ _ _
| \/ | (_) | | (_)
| . . | __ _ _ _ __ | | ___ __ _ _ ___
| |\/| |/ _` | | '_ \ | | / _ \ / _` | |/ __|
| | | | (_| | | | | | | |___| (_) | (_| | | (__
\_| |_/\__,_|_|_| |_| \_____/\___/ \__, |_|\___|
__/ |
|___/
]]--
--the main loop
mobs.mob_step = function(self, dtime)
--do not continue if non-existent
if not self or not self.object or not self.object:get_luaentity() then
self.object:remove()
return false
end
--DEBUG TIME!
--REMEMBER TO MOVE THIS AFTER DEATH CHECK
--if self.has_head then
-- mobs.do_head_logic(self,dtime)
--end
--if true then--DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
-- return
--end
--despawn mechanism
--don't despawned tamed or bred mobs
if not self.tamed and not self.bred then
self.lifetimer = self.lifetimer - dtime
if self.lifetimer <= 0 then
self.lifetimer = self.lifetimer_reset
if not mobs.check_for_player_within_area(self, 64) then
--print("removing in MAIN LOGIC!")
self.object:remove()
return
end
end
end
--color modifier which coincides with the pause_timer
if self.old_health and self.health < self.old_health then
self.object:set_texture_mod("^[colorize:red:120")
--fix double death sound
if self.health > 0 then
mobs.play_sound(self,"damage")
end
end
self.old_health = self.health
--do death logic (animation, poof, explosion, etc)
if self.health <= 0 or self.dead then
--play death sound once
if not self.played_death_sound then
self.dead = true
mobs.play_sound(self,"death")
self.played_death_sound = true
end
mobs.death_logic(self, dtime)
--this is here because the mob must continue to move
--while stunned before coming to a complete halt even during
--the death tilt
if self.pause_timer > 0 then
self.pause_timer = self.pause_timer - dtime
--perfectly reset pause_timer
if self.pause_timer < 0 then
self.pause_timer = 0
end
end
return
end
mobs.random_sound_handling(self,dtime)
--mobs drowning mechanic
if not self.breathes_in_water then
local pos = self.object:get_pos()
pos.y = pos.y + self.eye_height
local node = minetest_get_node(pos).name
if minetest_get_item_group(node, "water") ~= 0 then
self.breath = self.breath - dtime
--reset breath when drowning
if self.breath <= 0 then
self.health = self.health - 4
self.breath = 1
self.pause_timer = 0.5
end
elseif self.breath < self.breath_max then
self.breath = self.breath + dtime
--clean timer reset
if self.breath > self.breath_max then
self.breath = self.breath_max
end
end
end
--set mobs on fire when burned by sunlight
if self.ignited_by_sunlight then
local pos = self.object:get_pos()
pos.y = pos.y + 0.1
if self.burn_timer > 0 then
self.burn_timer = self.burn_timer - dtime
if self.burn_timer <= 0 then
self.health = self.health - 4
self.burn_timer = 0
end
end
if self.burn_timer == 0 then
local light_current, light_day = minetest_get_node_light(pos), minetest_get_node_light(pos, 0.5)
if light_current and light_day and light_current > 12 and light_day == 15 then
mcl_burning.set_on_fire(self.object, 1)
self.burn_timer = 1 --1.7 seconds
self.pause_timer = 0.4
end
end
end
--baby grows up
if self.baby then
--print(self.grow_up_timer)
--catch missing timer
if not self.grow_up_timer then
self.grow_up_timer = self.grow_up_goal
end
self.grow_up_timer = self.grow_up_timer - dtime
--baby grows up!
if self.grow_up_timer <= 0 then
self.grow_up_timer = 0
mobs.baby_grow_up(self)
end
end
--do custom mob instructions
if self.do_custom then
-- when false skip going any further
if self.do_custom(self, dtime) == false then
--this needs to be here or the mob becomes immortal
if self.pause_timer > 0 then
self.pause_timer = self.pause_timer - dtime
--perfectly reset pause_timer
if self.pause_timer <= 0 then
self.pause_timer = 0
self.object:set_texture_mod("")
end
end
--this overrides internal lua collision detection
return
end
end
local attacking = nil
--scan for players within eyesight
if self.hostile then
--true for line_of_sight is debug
attacking = mobs.detect_closest_player_within_radius(self,true,self.view_range,self.eye_height)
--go get the closest player
if attacking then
self.memory = 6 --6 seconds of memory
--set initial punch timer
if self.attacking == nil then
if self.attack_type == "punch" then
self.punch_timer = -1
end
end
self.attacking = attacking
--no player in area
elseif self.memory > 0 then
--try to remember
self.memory = self.memory - dtime
--get if memory player is within viewing range
if self.attacking and self.attacking:is_player() then
local distance = vector.distance(self.object:get_pos(), self.attacking:get_pos())
if distance > self.view_range then
self.memory = 0
end
--out of viewing range, forget em
else
self.memory = 0
end
if self.memory <= 0 then
--reset states when coming out of hostile state
if self.attacking ~= nil then
self.state_timer = -1
end
self.attacking = nil
self.memory = 0
end
end
end
--count down hostile cooldown timer when no players in range
if self.neutral and self.hostile and not attacking and self.hostile_cooldown_timer then
self.hostile_cooldown_timer = self.hostile_cooldown_timer - dtime
if self.hostile_cooldown_timer <= 0 then
self.hostile = false
self.hostile_cooldown_timer = 0
end
end
--mob is stunned after being hit
if self.pause_timer > 0 then
self.pause_timer = self.pause_timer - dtime
--don't break eye contact
if self.hostile and self.attacking then
mobs.set_yaw_while_attacking(self)
end
--perfectly reset pause_timer
if self.pause_timer <= 0 then
self.pause_timer = 0
self.object:set_texture_mod("")
end
--stop walking mobs from falling through the water
if not self.jump_only and not self.swim and not self.fly then
local pos = self.object:get_pos()
local collisionbox = self.object:get_properties().collisionbox
--get the center of the mob
pos.y = pos.y + (collisionbox[2] + collisionbox[5] / 2)
local current_node = minetest_get_node(pos).name
--recheck if in water or lava
if minetest_get_item_group(current_node, "water") ~= 0 or minetest_get_item_group(current_node, "lava") ~= 0 then
mobs.float(self)
end
end
--stop projectile mobs from being completely disabled while stunned
if self.projectile_timer and self.projectile_timer > 0.01 then
self.projectile_timer = self.projectile_timer - dtime
if self.projectile_timer < 0.01 then
self.projectile_timer = 0.01
end
end
return -- don't allow collision detection
--do normal ai
else
--jump only (like slimes)
if self.jump_only then
jump_state_switch(self, dtime)
jump_state_execution(self, dtime)
--swimming
elseif self.swim then
swim_state_switch(self, dtime)
swim_state_execution(self, dtime)
--flying
elseif self.fly then
fly_state_switch(self, dtime)
fly_state_execution(self,dtime)
--regular mobs that walk around
else
land_state_switch(self, dtime)
land_state_execution(self,dtime)
end
end
--do not continue if non-existent
if not self or not self.object or not self.object:get_luaentity() then
self.object:remove()
return false
end
--make it so mobs do not glitch out when walking around/jumping
mobs.swap_auto_step_height_adjust(self)
-- can mob be pushed, if so calculate direction -- do this last (overrides everything)
if self.pushable then
mobs.collision(self)
end
--overrides absolutely everything
--mobs get stuck in cobwebs like players
if not self.ignores_cobwebs then
local pos = self.object:get_pos()
local node = pos and minetest_get_node(pos).name
if node == "mcl_core:cobweb" then
--fight the rest of the api
if self.object:get_acceleration().y ~= 0 then
self.object:set_acceleration(vector.new(0,0,0))
end
mobs.stick_in_cobweb(self)
self.was_stuck_in_cobweb = true
else
--do not override other functions
if self.was_stuck_in_cobweb == true then
--return the mob back to normal
self.was_stuck_in_cobweb = nil
if self.object:get_acceleration().y == 0 and not self.swim and not self.fly then
self.object:set_acceleration(vector.new(0,-self.gravity,0))
end
end
end
end
self.old_velocity = self.object:get_velocity()
self.old_pos = self.object:get_pos()
end