// 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; }