local vector_direction = vector.direction
--local minetest_dir_to_yaw = minetest.dir_to_yaw
local vector_distance = vector.distance
local vector_multiply = vector.multiply
local math_random  = math.random

--[[
 _   _                     _   _
| | | |                   | | | |
| | | |     __ _ _ __   __| | | |
| | | |    / _` | '_ \ / _` | | |
|_| | |___| (_| | | | | (_| | |_|
(_) \_____/\__,_|_| |_|\__,_| (_)
]]--



--[[
 _____           _           _
|  ___|         | |         | |
| |____  ___ __ | | ___   __| | ___
|  __\ \/ / '_ \| |/ _ \ / _` |/ _ \
| |___>  <| |_) | | (_) | (_| |  __/
\____/_/\_\ .__/|_|\___/ \__,_|\___|
          | |
          |_|
]]--

local minetest_line_of_sight = minetest.line_of_sight

mobs.explode_attack_walk = function(self,dtime)

    --this needs an exception
    if self.attacking == nil or not self.attacking:is_player() then
        self.attacking = nil
        return
    end

    mobs.set_yaw_while_attacking(self)

    local pos = self.object:get_pos()
    local attack_pos = self.attacking:get_pos()
    local distance_from_attacking = vector_distance(pos, attack_pos)

    --make mob walk up to player within 2 nodes distance then start exploding
    if (
        distance_from_attacking >= self.reach and

        --don't allow explosion to cancel unless out of the reach boundary
        not (self.explosion_animation and self.explosion_animation > 0 and distance_from_attacking <= self.defuse_reach) or
        --don't allow creeper to finish exploding animation if can't see target
        not minetest_line_of_sight(
            {x = pos.x, y = pos.y + self.eye_height, z = pos.z},
            {x = attack_pos.x, y = attack_pos.y + (self.attacking.eye_height or 0), z = attack_pos.z}
        )
    ) then
        mobs.set_velocity(self, self.run_velocity)
        mobs.set_mob_animation(self,"run")

        mobs.reverse_explosion_animation(self,dtime)

    else
        mobs.set_velocity(self,0)

        --this is the only way I can reference this without dumping extra data on all mobs
        if not self.explosion_animation then
            self.explosion_animation = 0
        end

        --play ignite sound
        if self.explosion_animation == 0 then
            mobs.play_sound(self,"attack")
        end

        mobs.set_mob_animation(self,"stand")

        mobs.handle_explosion_animation(self)

        self.explosion_animation = self.explosion_animation + (dtime/2.5)
    end

    --make explosive mobs jump
    --check for nodes to jump over
    --explosive mobs will just ride against walls for now
	local node_in_front_of = mobs.jump_check(self)
	if node_in_front_of == 1 then
		mobs.jump(self)
    end

    --do biggening explosion thing
    if self.explosion_animation and self.explosion_animation > self.explosion_timer then
        mcl_explosions.explode(self.object:get_pos(), self.explosion_strength,{ drop_chance = 1.0 })
        self.object:remove()
    end
end


--this is a small helper function to make working with explosion animations easier
mobs.reverse_explosion_animation = function(self,dtime)
    --if explosion animation was greater than 0 then reverse it
    if self.explosion_animation and self.explosion_animation > 0 then
        self.explosion_animation = self.explosion_animation - dtime
        if self.explosion_animation < 0 then
            self.explosion_animation = 0
        end
    end

    mobs.handle_explosion_animation(self)
end




--[[
______                 _
| ___ \               | |
| |_/ /   _ _ __   ___| |__
|  __/ | | | '_ \ / __| '_ \
| |  | |_| | | | | (__| | | |
\_|   \__,_|_| |_|\___|_| |_|
]]--



mobs.punch_attack_walk = function(self,dtime)
    --this needs an exception
    if self.attacking == nil or not self.attacking:is_player() then
        self.attacking = nil
        return
    end

    local distance_from_attacking = mobs.get_2d_distance(self.object:get_pos(), self.attacking:get_pos())

    if distance_from_attacking >= self.minimum_follow_distance then
        mobs.set_velocity(self, self.run_velocity)
        mobs.set_mob_animation(self, "run")
    else
        mobs.set_velocity(self, 0)
        mobs.set_mob_animation(self, "stand")
    end

    mobs.set_yaw_while_attacking(self)

    --make punchy mobs jump
    --check for nodes to jump over
    --explosive mobs will just ride against walls for now
	local node_in_front_of = mobs.jump_check(self)

	if node_in_front_of == 1 then
		mobs.jump(self)
    end

    --mobs that can climb over stuff
    if self.always_climb and node_in_front_of > 0 then
        mobs.climb(self)
    end


    --auto reset punch_timer
    if not self.punch_timer then
        self.punch_timer = 0
    end

    if self.punch_timer > 0 then
        self.punch_timer = self.punch_timer - dtime
    end
end

mobs.punch_attack = function(self)

    self.attacking:punch(self.object, 1.0, {
        full_punch_interval = 1.0,
        damage_groups = {fleshy = self.damage}
    }, nil)

    self.punch_timer = self.punch_timer_cooloff


    --knockback
    local pos1 = self.object:get_pos()
    pos1.y = 0
    local pos2 = self.attacking:get_pos()
    pos2.y = 0
    local dir = vector_direction(pos1,pos2)

    dir = vector_multiply(dir,3)

	local attacking = self.attacking
	if attacking then
		local attacking_velocity = attacking:get_velocity() or attacking:get_player_velocity()
		if attacking_velocity then
			if attacking_velocity.y <= 1 then
				dir.y = 5
			end
		end
	end

    self.attacking:add_velocity(dir)
end




--[[
______          _           _   _ _
| ___ \        (_)         | | (_) |
| |_/ / __ ___  _  ___  ___| |_ _| | ___
|  __/ '__/ _ \| |/ _ \/ __| __| | |/ _ \
| |  | | | (_) | |  __/ (__| |_| | |  __/
\_|  |_|  \___/| |\___|\___|\__|_|_|\___|
              _/ |
             |__/
]]--


mobs.projectile_attack_walk = function(self,dtime)

    --this needs an exception
    if self.attacking == nil or not self.attacking:is_player() then
        self.attacking = nil
        return
    end

    mobs.set_yaw_while_attacking(self)

    local distance_from_attacking = vector_distance(self.object:get_pos(), self.attacking:get_pos())


    if distance_from_attacking >= self.reach then
        mobs.set_velocity(self, self.run_velocity)
        mobs.set_mob_animation(self,"run")
    else
        mobs.set_velocity(self,0)
        mobs.set_mob_animation(self,"stand")
    end

    --do this to not load data into other mobs
    if not self.projectile_timer then
        self.projectile_timer = math_random(self.projectile_cooldown_min, self.projectile_cooldown_max)
    end

    --run projectile timer
    if self.projectile_timer > 0 then
        self.projectile_timer = self.projectile_timer - dtime

        --shoot
        if self.projectile_timer <= 0 then
            --reset timer
            self.projectile_timer = math_random(self.projectile_cooldown_min, self.projectile_cooldown_max)
            mobs.shoot_projectile(self)
        end
    end

    --make shooty mobs jump
    --check for nodes to jump over
    --explosive mobs will just ride against walls for now
	local node_in_front_of = mobs.jump_check(self)
	if node_in_front_of == 1 then
		mobs.jump(self)
    end

end









--[[
 _  ______ _         _
| | |  ___| |       | |
| | | |_  | |_   _  | |
| | |  _| | | | | | | |
|_| | |   | | |_| | |_|
(_) \_|   |_|\__, | (_)
              __/ |
             |___/
]]--




--[[
______          _           _   _ _
| ___ \        (_)         | | (_) |
| |_/ / __ ___  _  ___  ___| |_ _| | ___
|  __/ '__/ _ \| |/ _ \/ __| __| | |/ _ \
| |  | | | (_) | |  __/ (__| |_| | |  __/
\_|  |_|  \___/| |\___|\___|\__|_|_|\___|
              _/ |
             |__/
]]--

local random_pitch_multiplier = {-1,1}

mobs.projectile_attack_fly = function(self, dtime)
    --this needs an exception
    if self.attacking == nil or not self.attacking:is_player() then
        self.attacking = nil
        return
    end

    --this is specifically for random ghast movement
    if self.fly_random_while_attack then

        --enable rotation locking
		mobs.movement_rotation_lock(self)

        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

        mobs.set_fly_velocity(self, self.run_velocity)

    else

        mobs.set_yaw_while_attacking(self)

        local distance_from_attacking = vector_distance(self.object:get_pos(), self.attacking:get_pos())

        if distance_from_attacking >= self.reach then
            mobs.set_pitch_while_attacking(self)
            mobs.set_fly_velocity(self, self.run_velocity)
            mobs.set_mob_animation(self,"run")
        else
            mobs.set_pitch_while_attacking(self)
            mobs.set_fly_velocity(self, 0)
            mobs.set_mob_animation(self,"stand")
        end
    end


    --do this to not load data into other mobs
    if not self.projectile_timer then
        self.projectile_timer = math_random(self.projectile_cooldown_min, self.projectile_cooldown_max)
    end

    --run projectile timer
    if self.projectile_timer > 0 then
        self.projectile_timer = self.projectile_timer - dtime

        --shoot
        if self.projectile_timer <= 0 then

            if self.fly_random_while_attack then
                mobs.set_yaw_while_attacking(self)
                self.walk_timer = 0
            end
            --reset timer
            self.projectile_timer = math_random(self.projectile_cooldown_min, self.projectile_cooldown_max)
            mobs.shoot_projectile(self)
        end
    end
end
