// IW6 GSC SOURCE
// Generated by https://github.com/xensik/gsc-tool

load_spitter_fx()
{
    level._effect["spit_AOE"] = loadfx( "vfx/gameplay/alien/vfx_alien_spitter_gas_cloud" );
    level._effect["spit_AOE_small"] = loadfx( "vfx/gameplay/alien/vfx_alien_spitter_gas_cloud_64" );
}

spitter_init()
{
    self.gas_cloud_available = 1;
}

spitter_death()
{
    release_spit_node();
}

is_escape_sequence_active()
{
    return common_scripts\utility::flag_exist( "hives_cleared" ) && common_scripts\utility::flag( "hives_cleared" );
}

get_max_spit_distance_squared()
{
    if ( is_escape_sequence_active() )
        return 3240000;

    return 1440000;
}

get_lookahead_percentage()
{
    if ( is_escape_sequence_active() )
        return 1.0;

    return 0.5;
}

spit_projectile( var_0 )
{
    if ( self.spit_type == "gas_cloud" )
        level.spitter_last_cloud_time = gettime();

    self.melee_type = "spit";
    self.spit_target = var_0;
    maps\mp\agents\alien\_alien_think::alien_melee( var_0 );
}

spit_attack( var_0 )
{
    self endon( "melee_pain_interrupt" );
    var_1 = isdefined( var_0 ) && isdefined( var_0.code_classname ) && var_0.code_classname == "script_vehicle";

    if ( var_1 )
        var_2 = var_0;
    else
        var_2 = self.spit_target;

    var_2 endon( "death" );
    maps\mp\agents\alien\_alien_anim_utils::turntowardsentity( var_2 );

    if ( isalive( var_2 ) )
    {
        self.spit_target = var_2;

        if ( var_1 )
        {
            var_3 = 5;
            var_4 = vectornormalize( anglestoforward( var_2.angles ) );
            var_5 = length( var_2 vehicle_getvelocity() ) * var_3;
            var_6 = var_4 * var_5;
            self.spit_target_location = var_2.origin + var_6 + ( 0, 0, 32 );
        }
        else
            self.spit_target_location = var_2.origin;

        self.looktarget = var_2;
        maps\mp\alien\_utility::set_alien_emissive( 0.2, 1.0 );

        if ( isdefined( self.current_spit_node ) && !maps\mp\alien\_utility::is_normal_upright( anglestoup( self.current_spit_node.angles ) ) )
        {
            var_7 = anglestoup( self.current_spit_node.angles );
            var_8 = anglestoforward( self.angles );
            var_9 = vectorcross( var_7, var_8 );
            var_8 = vectorcross( var_9, var_7 );
            var_10 = ( 0, 0, 0 ) - var_9;
            var_11 = axistoangles( var_8, var_10, var_7 );
            self scragentsetorientmode( "face angle abs", var_11 );
        }
        else if ( isdefined( self.enemy ) && var_2 == self.enemy )
            self scragentsetorientmode( "face enemy" );
        else
        {
            var_8 = vectornormalize( var_2.origin - self.origin );

            if ( isdefined( self.current_spit_node ) )
                var_7 = anglestoup( self.current_spit_node.angles );
            else
                var_7 = anglestoup( self.angles );

            var_9 = vectorcross( var_7, var_8 );
            var_8 = vectorcross( var_9, var_7 );
            var_10 = ( 0, 0, 0 ) - var_9;
            var_11 = axistoangles( var_8, var_10, var_7 );
            self scragentsetorientmode( "face angle abs", var_11 );
        }

        if ( self.oriented )
            self scragentsetanimmode( "anim angle delta" );
        else
            self scragentsetanimmode( "anim deltas" );

        play_spit_anim();
    }

    maps\mp\alien\_utility::set_alien_emissive_default( 0.2 );
    self.looktarget = undefined;
    self.spit_target = undefined;
    self.spit_target_location = undefined;
    self.spit_type = undefined;
}

play_spit_anim()
{
    switch ( self.spit_type )
    {
        case "close_range":
            maps\mp\agents\_scriptedagents::playanimuntilnotetrack( "close_spit_attack", "spit_attack", "end", ::handleattacknotetracks );
            break;
        case "gas_cloud":
            maps\mp\agents\_scriptedagents::playanimuntilnotetrack( "gas_spit_attack", "spit_attack", "end", ::handleattacknotetracks );
            break;
        case "long_range":
            var_0 = randomintrange( 2, 3 );

            for ( var_1 = 0; var_1 < var_0; var_1++ )
                maps\mp\agents\_scriptedagents::playanimuntilnotetrack( "long_range_spit_attack", "spit_attack", "end", ::handleattacknotetracks );

            break;
        default:
            break;
    }
}

get_best_spit_target( var_0 )
{
    if ( common_scripts\utility::cointoss() && maps\mp\alien\_utility::get_alien_type() != "seeder" )
    {
        var_1 = get_grief_targets();

        foreach ( var_3 in var_1 )
        {
            if ( is_valid_spit_target( var_3, 0 ) )
                return var_3;
        }

        wait 0.05;
    }

    if ( isdefined( var_0 ) )
    {
        if ( isalive( var_0 ) && is_valid_spit_target( var_0, 0 ) )
            return var_0;
    }

    var_5 = get_current_possible_targets();
    var_6 = 4;
    var_7 = 0;

    foreach ( var_9 in var_5 )
    {
        if ( !isalive( var_9 ) )
            continue;

        if ( isdefined( var_0 ) && var_9 == var_0 )
            continue;

        if ( is_valid_spit_target( var_9, 1 ) )
            return var_9;

        var_7++;

        if ( var_7 >= var_6 )
        {
            common_scripts\utility::waitframe();
            var_7 = 0;
        }
    }

    return undefined;
}

get_grief_targets()
{
    var_0 = [];

    if ( !can_spit_gas_cloud() || is_pet() )
        return var_0;

    foreach ( var_2 in level.players )
    {
        if ( !isalive( var_2 ) )
            continue;

        if ( isdefined( var_2.inlaststand ) && var_2.inlaststand )
            var_0[var_0.size] = var_2;
    }

    if ( isdefined( level.drill ) && isdefined( level.drill.state ) && level.drill.state == "offline" )
        var_0[var_0.size] = level.drill;

    return common_scripts\utility::array_randomize( var_0 );
}

is_valid_spit_target( var_0, var_1 )
{
    if ( !isalive( var_0 ) )
        return 0;

    if ( var_1 && isplayer( var_0 ) && !has_attacker_space( var_0 ) )
        return 0;

    var_2 = get_max_spit_distance_squared();
    var_3 = distance2dsquared( self.origin, var_0.origin );

    if ( var_3 > var_2 )
        return 0;

    self.looktarget = var_0;

    if ( !isalive( var_0 ) )
        return 0;

    if ( ( isplayer( var_0 ) || issentient( var_0 ) ) && !isdefined( var_0.usingremote ) )
        var_4 = var_0 geteye();
    else
        var_4 = var_0.origin;

    var_5 = self gettagorigin( "TAG_BREATH" );
    return bullettracepassed( var_5, var_4, 0, self );
}

get_spit_fire_pos( var_0 )
{
    return self gettagorigin( "TAG_BREATH" );
}

has_attacker_space( var_0 )
{
    var_1 = level.maxalienattackerdifficultyvalue - level.alien_types[self.alien_type].attributes["attacker_difficulty"];
    var_2 = maps\mp\agents\alien\_alien_think::get_current_attacker_value( var_0 );
    return var_2 <= var_1;
}

handleattacknotetracks( var_0, var_1, var_2, var_3 )
{
    if ( isdefined( level.dlc_attacknotetrack_override_func ) )
    {
        self [[ level.dlc_attacknotetrack_override_func ]]( var_0, var_1, var_2, var_3 );
        return;
    }

    if ( var_0 == "spit" )
        return fire_spit_projectile();
}

fire_spit_projectile()
{
    if ( !isdefined( self.spit_target ) && !isdefined( self.spit_target_location ) )
        return;

    var_0 = isalive( self.spit_target );
    var_1 = isdefined( self.spit_target.code_classname ) && self.spit_target.code_classname == "script_vehicle";

    if ( var_0 && !var_1 )
        var_2 = self.spit_target.origin;
    else
        var_2 = self.spit_target_location;

    if ( self.spit_type == "gas_cloud" )
        spit_gas_cloud_projectile( var_2 );
    else if ( var_0 )
    {
        var_3 = 1400;
        var_2 = get_lookahead_target_location( var_3, self.spit_target, 0 );

        if ( !bullettracepassed( var_2, get_spit_fire_pos( var_2 ), 0, self ) )
            var_2 = get_lookahead_target_location( var_3, self.spit_target, 1 );

        spit_basic_projectile( var_2 );
    }
}

get_lookahead_target_location( var_0, var_1, var_2 )
{
    if ( !isplayer( var_1 ) )
        return var_1.origin;

    var_3 = get_lookahead_percentage();

    if ( var_2 && !isdefined( var_1.usingremote ) )
        var_4 = var_1 geteye();
    else
        var_4 = var_1.origin;

    var_5 = distance( self.origin, var_4 );
    var_6 = var_5 / var_0;
    var_7 = var_1 getvelocity();
    return var_4 + var_7 * var_3 * var_6;
}

can_spit_gas_cloud()
{
    if ( !self.gas_cloud_available )
        return 0;

    if ( isdefined( self.enemy ) && isdefined( self.enemy.no_gas_cloud_attack ) && self.enemy.no_gas_cloud_attack )
        return 0;

    var_0 = ( gettime() - level.spitter_last_cloud_time ) * 0.001;
    return level.spitter_gas_cloud_count < 3 && var_0 > 3.33;
}

spit_basic_projectile( var_0 )
{
    var_1 = get_spit_fire_pos( var_0 );
    var_2 = magicbullet( "alienspit_mp", var_1, var_0, self );
    var_2.owner = self;

    if ( isdefined( var_2 ) )
        var_2 thread spit_basic_projectile_impact_monitor( self );
}

spit_basic_projectile_impact_monitor( var_0 )
{
    self waittill( "explode", var_1 );

    if ( !isdefined( var_1 ) )
        return;

    playfx( level._effect["spit_AOE_small"], var_1 + ( 0, 0, 8 ), ( 0, 0, 1 ), ( 1, 0, 0 ) );
}

spit_gas_cloud_projectile( var_0 )
{
    var_1 = get_spit_fire_pos( var_0 );
    var_2 = magicbullet( "alienspit_gas_mp", var_1, var_0, self );
    var_2.owner = self;

    if ( isdefined( var_2 ) )
        var_2 thread spit_gas_cloud_projectile_impact_monitor( self );

    thread gas_cloud_available_timer();
}

gas_cloud_available_timer()
{
    self endon( "death" );
    self.gas_cloud_available = 0;
    var_0 = randomfloatrange( 10.0, 15.0 );
    wait( var_0 );
    self.gas_cloud_available = 1;
}

spit_gas_cloud_projectile_impact_monitor( var_0 )
{
    self waittill( "explode", var_1 );

    if ( !isdefined( var_1 ) )
        return;

    var_2 = spawn( "trigger_radius", var_1, 0, 150, 128 );

    if ( !isdefined( var_2 ) )
        return;

    level.spitter_gas_cloud_count++;
    var_2.onplayer = 1;
    playfx( level._effect["spit_AOE"], var_1 + ( 0, 0, 8 ), ( 0, 0, 1 ), ( 1, 0, 0 ) );
    thread spit_aoe_cloud_damage( var_1, var_2 );
    level notify( "spitter_spit", var_1 );
    wait 10.0;
    var_2 delete();
    level.spitter_gas_cloud_count--;
}

spit_aoe_cloud_damage( var_0, var_1 )
{
    var_1 endon( "death" );
    wait 2.0;

    for (;;)
    {
        var_1 waittill( "trigger", var_2 );

        if ( !isplayer( var_2 ) )
            continue;

        if ( !isalive( var_2 ) )
            continue;

        disorient_player( var_2 );
        damage_player( var_2, var_1 );
    }
}

damage_player( var_0, var_1 )
{
    var_2 = 0.5;
    var_3 = gettime();

    if ( !isdefined( var_0.last_spitter_gas_damage_time ) )
        var_4 = var_2;
    else if ( var_0.last_spitter_gas_damage_time + var_2 * 1000.0 > var_3 )
        return;
    else
        var_4 = min( var_2, ( var_3 - var_0.last_spitter_gas_damage_time ) * 0.001 );

    var_5 = var_0 maps\mp\alien\_perk_utility::perk_getgasdamagescalar();
    var_6 = int( 12.0 * var_4 * var_5 );

    if ( var_6 > 0 )
        var_0 thread [[ level.callbackplayerdamage ]]( var_1, var_1, var_6, 0, "MOD_SUICIDE", "alienspit_gas_mp", var_1.origin, ( 0, 0, 0 ), "none", 0 );

    var_0.last_spitter_gas_damage_time = var_3;
}

disorient_player( var_0 )
{
    if ( maps\mp\alien\_utility::is_chaos_mode() && var_0 maps\mp\alien\_perk_utility::perk_getgasdamagescalar() == 0 )
        return;
    else if ( !var_0 maps\mp\alien\_perk_utility::has_perk( "perk_medic", [ 1, 2, 3, 4 ] ) )
    {
        if ( isdefined( level.shell_shock_override ) )
            var_0 [[ level.shell_shock_override ]]( 0.5 );
        else
            var_0 shellshock( "alien_spitter_gas_cloud", 0.5 );
    }
}

get_rl_toward( var_0 )
{
    var_1 = vectortoangles( var_0.origin - self.origin );
    var_2 = anglestoright( var_1 );

    if ( common_scripts\utility::cointoss() )
        var_2 = var_2 * -1;

    return var_2;
}

spitter_combat( var_0 )
{
    self endon( "bad_path" );
    self endon( "death" );
    self endon( "alien_main_loop_restart" );

    for (;;)
    {
        var_1 = find_spitter_attack_node( self.enemy );

        if ( isdefined( var_1 ) )
        {
            move_to_spitter_attack_node( var_1 );
            spitter_attack( self.enemy );
            continue;
        }

        wait 0.05;
    }
}

release_spit_node()
{
    if ( isdefined( self.current_spit_node ) )
    {
        self scragentrelinquishclaimednode( self.current_spit_node );
        self.current_spit_node.claimed = 0;
        self.current_spit_node = undefined;
    }
}

claim_spit_node( var_0 )
{
    self.current_spit_node = var_0;
    var_0.claimed = 1;
    self scragentclaimnode( var_0 );
}

move_to_spitter_attack_node( var_0 )
{
    self endon( "player_proximity_during_move" );
    release_spit_node();
    claim_spit_node( var_0 );
    self scragentsetgoalnode( var_0 );
    self scragentsetgoalradius( 64 );
    thread enemy_proximity_during_move_monitor();
    self waittill( "goal_reached" );
}

enemy_proximity_during_move_monitor()
{
    self endon( "death" );
    self endon( "goal_reached" );
    self endon( "alien_main_loop_restart" );

    for (;;)
    {
        wait 0.05;

        if ( !maps\mp\alien\_utility::is_normal_upright( anglestoup( self.angles ) ) )
            continue;

        if ( !maps\mp\agents\alien\_alien_think::melee_okay() )
            continue;

        if ( isdefined( self.valid_moving_spit_attack_time ) && gettime() < self.valid_moving_spit_attack_time )
            continue;

        var_0 = find_player_within_distance( 40000.0 );

        if ( isdefined( var_0 ) )
            break;
    }

    release_spit_node();
    self notify( "player_proximity_during_move" );
    self scragentsetgoalentity( var_0 );
    self scragentsetgoalradius( 2048.0 );
    self waittill( "goal_reached" );
}

get_possible_spitter_attack_nodes( var_0 )
{
    if ( maps\mp\alien\_utility::get_alien_type() == "seeder" )
        var_1 = getnodesinradius( var_0.origin, 768, 128, 512, "jump attack" );
    else
        var_1 = getnodesinradius( var_0.origin, 1000, 300, 512, "jump attack" );

    var_2 = [];

    foreach ( var_4 in var_1 )
    {
        if ( isdefined( var_4.claimed ) && var_4.claimed )
            continue;

        var_2[var_2.size] = var_4;
    }

    return var_2;
}

is_pet()
{
    return isdefined( self.pet ) && self.pet;
}

get_current_possible_targets()
{
    if ( is_pet() )
        return level.agentarray;
    else
        return level.players;
}

find_spitter_attack_node( var_0 )
{
    var_1 = [];

    if ( is_escape_sequence_active() && isdefined( level.escape_spitter_target_node ) )
    {
        var_1 = get_possible_spitter_attack_nodes( level.escape_spitter_target_node );

        if ( var_1.size > 0 )
            var_0 = level.escape_spitter_target_node;
    }

    if ( var_1.size == 0 && isdefined( var_0 ) )
        var_1 = get_possible_spitter_attack_nodes( var_0 );

    if ( var_1.size == 0 )
    {
        var_2 = get_current_possible_targets();

        foreach ( var_4 in var_2 )
        {
            wait 0.05;

            if ( !isalive( var_4 ) )
                continue;

            if ( isdefined( var_0 ) && var_4 == var_0 )
                continue;

            var_1 = get_possible_spitter_attack_nodes( var_4 );

            if ( var_1.size > 0 )
            {
                var_0 = var_4;
                break;
            }
        }
    }

    if ( var_1.size == 0 )
        var_1 = get_possible_spitter_attack_nodes( self );

    if ( var_1.size == 0 )
        return undefined;

    var_6 = [];

    if ( isdefined( var_0 ) )
    {
        var_6["dist_from_enemy_weight"] = 8.0;
        var_6["enemy_los_weight"] = 6.0;
        var_6["height_weight"] = 4.0;
        var_7 = get_rl_toward( var_0 );
        var_0 endon( "death" );
    }
    else
    {
        var_6["dist_from_enemy_weight"] = 0.0;
        var_6["enemy_los_weight"] = 0.0;
        var_6["height_weight"] = 0.0;
        var_7 = get_central_enemies_direction();
    }

    var_6["direction"] = "override";
    var_6["direction_override"] = var_7;
    var_6["direction_weight"] = 1.0;
    var_6["min_height"] = 64.0;
    var_6["max_height"] = 400.0;
    var_6["enemy_los"] = 1;
    var_6["min_dist_from_enemy"] = 300.0;
    var_6["max_dist_from_enemy"] = 800.0;
    var_6["desired_dist_from_enemy"] = 600.0;
    var_6["min_dist_from_all_enemies"] = 300.0;
    var_6["min_dist_from_all_enemies_weight"] = 5.0;
    var_6["not_recently_used_weight"] = 10.0;
    var_6["recently_used_time_limit"] = 30.0;
    var_6["random_weight"] = 1.0;
    var_8 = maps\mp\agents\alien\_alien_think::get_retreat_node_rated( var_0, var_6, var_1 );
    return var_8;
}

get_central_enemies_direction()
{
    var_0 = get_current_possible_targets();

    if ( var_0.size == 0 )
        return self.origin + anglestoforward( self.angles ) * 100;

    var_1 = ( 0, 0, 0 );

    foreach ( var_3 in var_0 )
        var_1 = var_1 + var_3.origin;

    var_1 = var_1 / var_0.size;
    return var_1 - self.origin;
}

spitter_attack( var_0 )
{
    self endon( "spitter_node_move_requested" );

    if ( !isdefined( self.current_spit_node ) )
    {
        choose_spit_type( "close_range" );
        spit_projectile( var_0 );
        self.valid_moving_spit_attack_time = gettime() + randomfloatrange( 1.5, 3.0 ) * 1000.0;
        return;
    }

    set_up_attack_node_watchers();

    if ( !is_escape_sequence_active() )
        wait( randomfloatrange( 1.5, 3.0 ) * 0.5 );

    for (;;)
    {
        var_1 = undefined;
        var_2 = 0.0;

        while ( !isdefined( var_1 ) )
        {
            var_2 = var_2 + 0.2;

            if ( var_2 >= 1.0 )
                return;

            wait 0.2;

            if ( isdefined( var_0 ) && isdefined( var_0.code_classname ) && var_0.code_classname == "script_vehicle" )
            {
                var_1 = var_0;
                continue;
            }

            var_1 = get_best_spit_target( var_0 );
        }

        choose_spit_type( "long_range" );
        spit_projectile( var_1 );
        wait( randomfloatrange( 1.5, 3.0 ) );
    }
}

choose_spit_type( var_0 )
{
    if ( !is_pet() && can_spit_gas_cloud() )
        self.spit_type = "gas_cloud";
    else
        self.spit_type = var_0;
}

set_up_attack_node_watchers()
{
    thread spitter_node_duration_monitor( 10 );
    thread spitter_node_attacked_monitor( 0.1 );

    if ( !is_pet() )
        thread spitter_node_player_proximity( 90000.0 );
}

spitter_node_duration_monitor( var_0 )
{
    self endon( "spitter_node_move_requested" );
    self endon( "death" );
    self endon( "alien_main_loop_restart" );
    wait( var_0 );
    self notify( "spitter_node_move_requested" );
}

spitter_node_attacked_monitor( var_0 )
{
    self endon( "spitter_node_move_requested" );
    self endon( "death" );
    self endon( "alien_main_loop_restart" );
    self waittill( "damage" );
    wait( var_0 );
    self notify( "spitter_node_move_requested" );
}

spitter_node_player_proximity( var_0 )
{
    self endon( "spitter_node_move_requested" );
    self endon( "death" );
    self endon( "alien_main_loop_restart" );

    for (;;)
    {
        var_1 = find_player_within_distance( var_0 );

        if ( isdefined( var_1 ) )
            break;

        wait 0.2;
    }

    self notify( "spitter_node_move_requested" );
}

find_player_within_distance( var_0 )
{
    foreach ( var_2 in level.players )
    {
        if ( !isalive( var_2 ) )
            continue;

        var_3 = distance2dsquared( self.origin, var_2.origin );

        if ( var_3 < var_0 )
            return var_2;
    }

    return undefined;
}
