IW4-Dump-Files/maps/_stealth_threat_enemy.gsc

527 lines
14 KiB
Plaintext

#include common_scripts\utility;
#include maps\_utility;
#include maps\_anim;
#include maps\_stealth_utility;
#include maps\_stealth_shared_utilities;
#include maps\_stealth_animation_funcs;
stealth_threat_enemy_main()
{
self enemy_init();
self thread enemy_Threat_Loop();
}
/************************************************************************************************************/
/* LOGIC */
/************************************************************************************************************/
enemy_Threat_Loop()
{
self endon( "death" );
self endon( "pain_death" );
if ( self.type == "dog" )
self thread enemy_threat_logic_dog();
while ( 1 )
{
self waittill( "_stealth_enemy_alert_level_change", type );
if ( !self ent_flag( "_stealth_enabled" ) )
continue;
self enemy_alert_level_change_reponse( type );
}
}
enemy_alert_level_change_reponse( type )
{
self ent_flag_set( "_stealth_enemy_alert_level_action" );
_type = type;
if ( issubstr( type, "warning" ) )
_type = "warning";
switch( _type )
{
case "warning":
self thread enemy_alert_level_warning_wrapper( type );
break;
case "attack":
self thread enemy_alert_level_attack_wrapper();
break;
case "reset":
self thread enemy_alert_level_reset_wrapper();
break;
}
}
// we do this because we assume dogs are sleeping...if so, then they'll never get an enemy
// because to make that assumption we set their ignorall to true in their init...so we need
// to give them the ability to find an enemy once they take damage...we might extend this in
// the future to also do the same thing if an ai gets too close or makes too much noise
enemy_threat_logic_dog()
{
self endon( "death" );
self endon( "pain_death" );
if ( !self ent_flag( "_stealth_behavior_asleep" ) )
return;
self enemy_threat_logic_dog_wait();
wait .5;
self delaythread( .6, ::ent_flag_clear, "_stealth_behavior_asleep" );
self.ignoreall = false;
}
enemy_threat_logic_dog_wait()
{
self endon( "pain" );
self endon( "enemy" );
array_thread( level.players, ::enemy_threat_logic_dog_wakeup_dist, self, 128 );
while ( 1 )
{
self waittill( "event_awareness", type );
if ( !self ent_flag( "_stealth_enabled" ) )
continue;
if ( type == "heard_scream" || type == "bulletwhizby" || type == "projectile_impact" || type == "explode" )
return;
}
}
enemy_threat_logic_dog_wakeup_dist( dog, dist )
{
dog endon( "death" );
self endon( "death" );
if ( !dog ent_flag( "_stealth_behavior_asleep" ) )
return;
dog endon( "_stealth_behavior_asleep" );
distsqrd = dist * dist;
while ( distancesquared( self.origin, dog.origin ) > distsqrd && self ent_flag( "_stealth_enabled" ) )
wait .1;
dog.ignoreall = false;
dog.favoriteenemy = self;
wait .1;
dog.favoriteenemy = undefined;
}
/************************************************************************************************************/
/* THREAT LEVELS */
/************************************************************************************************************/
enemy_alert_level_reset_wrapper()
{
//this function is different than normal because if we want to change the behavior the enemies have
//after losing a known enemy - we can set it up here...also we don't care about any previous knowledge of corpses
//through this wrapper either and we make sure we wait for the spotted flag to clear before going back to normal.
//this why this function exists instead of just using the normal wrapper
self endon( "_stealth_enemy_alert_level_change" );
self endon( "enemy_awareness_reaction" );//we'll end on a new event, but events will also end on a new threat
self endon( "death" );
self endon( "pain_death" );
self stealth_group_spotted_flag_waitopen();//wait for everyone to lose their enemy too
self enemy_stop_current_behavior();
self ent_flag_clear( "_stealth_enemy_alert_level_action" );
if( isdefined( self._stealth.plugins.corpse ) )
{
self ent_flag_clear( "_stealth_saw_corpse" );
self ent_flag_clear( "_stealth_found_corpse" );
}
self ent_flag_clear( "_stealth_attack" );
self ent_flag_set( "_stealth_normal" );
function = ai_get_behavior_function( "threat", "reset" ); // default ::enemy_alert_level_normal
self thread [[ function ]]();
}
enemy_alert_level_warning_wrapper( type )
{
spotted_flag = self group_get_flagname( "_stealth_spotted" );
self endon( "_stealth_enemy_alert_level_change" );
level endon( spotted_flag );
self endon( "death" );
self endon( "pain_death" );
self enemy_find_original_goal();
self enemy_stop_current_behavior();
function = ai_get_behavior_function( "threat", type ); // default ::enemy_alert_level_warning1, 2
self [[ function ]]();
// done with warning response behavior and still in warning state, return to normal
self enemy_alert_level_normal_wrapper();
}
enemy_lookaround_for_time( time )
{
oldfov = self.fovcosine;
self.fovcosine = 0.1;
self set_generic_idle_anim( "_stealth_look_around" );
wait time;
self clear_generic_idle_anim();
self.fovcosine = oldfov;
}
enemy_announce_alert()
{
self endon( "death" );
wait 0.25;
if ( isdefined( self.enemy ) && self cansee( self.enemy ) )
{
self enemy_announce_snd( "huh" );
self thread enemy_announce_attack();
}
else
{
self thread enemy_announce_huh();
}
}
enemy_alert_level_warning1()
{
if ( !isdefined( self.enemy ) )
return;
self thread enemy_announce_alert();
if ( isdefined( self.script_patroller ) )
{
if ( self.type != "dog" )
{
type = "a";
if ( cointoss() )
type = "b";
self set_generic_run_anim( "_stealth_patrol_search_" + type, true );
}
else
{
self set_dog_walk_anim();
self.script_growl = 1;
}
self.disablearrivals = true;
self.disableexits = true;
}
else if ( self.type == "dog" )
{
self set_dog_walk_anim();
self.script_growl = 1;
self.disablearrivals = true;
self.disableexits = true;
}
vec = vectornormalize( self.enemy.origin - self.origin );
dist = distance( self.enemy.origin, self.origin );
dist *= .25;
dist = clamp( dist, 64, 128 );
vec = vector_multiply( vec, dist );
spot = self.origin + vec + ( 0, 0, 16 );
end = spot + ( ( 0, 0, -96 ) );
spot = physicstrace( spot, end );
if ( spot == end )
return;
self ent_flag_set( "_stealth_override_goalpos" );
self setgoalpos( spot );
self.goalradius = 64;
// we do the timeout - because sometimes this puts his goal into a postion that
// is invalid and he'll never actually hit his goal...so we time out after 2 seconds
self waittill_notify_or_timeout( "goal", 2 );
// if AI can't reach it's goal, at least face it.
if ( !self isInGoal( self.origin ) )
self.shootPosOverride = spot + ( 0, 0, 64 );
enemy_lookaround_for_time( 10 );
self.shootPosOverride = undefined;
}
enemy_alert_level_warning2()
{
if ( !isdefined( self.enemy ) )
return;
self thread enemy_announce_alert();
if ( self.type != "dog" )
self set_generic_run_anim( "_stealth_patrol_cqb" );
else
{
self clear_run_anim();
self.script_nobark = 1;
self.script_growl = 1;
}
self.disablearrivals = false;
self.disableexits = false;
// self.oldfixednode = self.fixednode;
// self.fixednode = true;
lastknownspot = self.enemy.origin;
dist = distance( lastknownspot, self.origin );
self ent_flag_set( "_stealth_override_goalpos" );
self setgoalpos( lastknownspot );
self.goalradius = dist * .5;
self waittill( "goal" );
if ( self.type != "dog" )
{
type = "_stealth_patrol_search_a";
if ( cointoss() )
type = "_stealth_patrol_search_b";
self set_generic_run_anim( type, true );
}
else
{
self anim_generic_custom_animmode( self, "gravity", "_stealth_dog_stop" );
self set_dog_walk_anim();
}
self setgoalpos( lastknownspot );
self.goalradius = 64;
self.disablearrivals = true;
self.disableexits = true;
self waittill( "goal" );
enemy_lookaround_for_time( 15 );
// self.fixednode = self.oldfixednode;
if ( self.type != "dog" )
{
type = "a";
if ( randomint( 100 ) > 50 )
type = "b";
self set_generic_run_anim( "_stealth_patrol_search_" + type, true );
}
else
{
self set_dog_walk_anim();
self.script_growl = undefined;
}
}
enemy_alert_level_attack_wrapper()
{
self endon( "death" );
self endon( "pain_death" );
self endon( "_stealth_enemy_alert_level_change" );// - > this might not be a good idea - just added it recently - haven't tested
self notify( "endNewEnemyReactionAnim" );
self notify( "movemode" );
self.disablearrivals = false;
self.disableexits = false;
self enemy_find_original_goal(); //should ALWAYS know what the original goal was just in case...if we dont have any warnings, we catch it here
self ent_flag_set( "_stealth_attack" );
function = ai_get_behavior_function( "threat", "attack" ); // default ::enemy_alert_level_attack
self [[ function ]]();
}
enemy_alert_level_attack()
{
self thread enemy_announce_spotted( self.origin );
if ( isdefined( self.script_goalvolume ) )
self thread maps\_spawner::set_goal_volume();
else
self enemy_close_in_on_target();
}
enemy_close_in_on_target()
{
radius = 2048;
self.goalradius = radius;
if ( isdefined( self.script_stealth_dontseek ) && self.script_stealth_dontseek == true )
return;
self endon( "death" );
self ent_flag_set( "_stealth_override_goalpos" );
while ( isdefined( self.enemy ) && self ent_flag( "_stealth_enabled" ) )
{
self setgoalpos( self.enemy.origin );
self.goalradius = radius;
if ( radius > 600 )
radius *= .75;
else
return;
wait 15;
}
}
enemy_alert_level_normal_wrapper()
{
enemy_set_alert_level( "reset" );
self ent_flag_clear( "_stealth_enemy_alert_level_action" );
if ( self ent_flag_exist( "_stealth_saw_corpse" ) )
self ent_flag_waitopen( "_stealth_saw_corpse" );
//to make sure found corpse is set
wait .05;
if ( self ent_flag_exist( "_stealth_found_corpse" ) )
self ent_flag_waitopen( "_stealth_found_corpse" );
self ent_flag_set( "_stealth_normal" );
function = ai_get_behavior_function( "threat", "normal" ); // default ::enemy_alert_level_normal
self [[ function ]]();
}
enemy_alert_level_normal()
{
self thread enemy_announce_hmph();
self enemy_go_back();
}
/************************************************************************************************************/
/* SETUP */
/************************************************************************************************************/
enemy_init()
{
self enemy_default_threat_behavior();
self enemy_default_threat_anim_behavior();
self._stealth.plugins.threat = true;
self.script_stealth_dontseek = true;
self.alertLevel = "noncombat";
self.newEnemyReactionDistSq = squared( level._stealth.logic.ai_event[ "ai_eventDistFootstepSprint" ][ "hidden" ] );
}
enemy_default_threat_behavior()
{
array = [];
array[ "reset" ] = ::enemy_alert_level_normal;
array[ "warning1" ] = ::enemy_alert_level_warning1;
array[ "warning2" ] = ::enemy_alert_level_warning2;
array[ "attack" ] = ::enemy_alert_level_attack;
array[ "normal" ] = ::enemy_alert_level_normal;
if ( !isdefined( level._stealth.logic.alert_level_table ) )
{
level._stealth.logic.alert_level_table = [];
level._stealth.logic.alert_level_table[ "reset" ] = "noncombat";
level._stealth.logic.alert_level_table[ "warning" ] = "alert";
level._stealth.logic.alert_level_table[ "attack" ] = "combat";
}
self enemy_set_threat_behavior( array );
}
// set the code alert level
enemy_set_alert_level( type )
{
assertEx( isdefined( level._stealth.logic.alert_level_table[ type ] ), "unsupported alert_level" ); // may need a way to custom alert_level_table
self.alertLevel = level._stealth.logic.alert_level_table[ type ];
}
enemy_set_threat_behavior( array )
{
//clear the array
self._stealth.behavior.ai_functions[ "threat" ] = [];
if ( !isdefined( array[ "reset" ] ) )
array[ "reset" ] = ::enemy_alert_level_normal;
if ( !isdefined( array[ "attack" ] ) )
array[ "attack" ] = ::enemy_alert_level_attack;
if ( !isdefined( array[ "normal" ] ) )
array[ "normal" ] = ::enemy_alert_level_normal;
foreach ( key, function in array )
self ai_create_behavior_function( "threat", key, function );
self._stealth.logic.alert_level.max_warnings = array.size - 3;// sub 2 for reset, normal, and attack
}
enemy_alert_level_change( type )
{
self notify( "_stealth_enemy_alert_level_change", type ); // calls ::enemy_alert_level_change_reponse but one frame later. Must be after enemy_Animation_Loop thread process... messy
if ( !isdefined( self._stealth.plugins.threat ) )
{
// from _stealth_visibility_enemy::enemy_alert_level_nothing()
self.goalradius = level.default_goalradius;
return;
}
if ( issubstr( type, "warning" ) )
type = "warning";
enemy_set_alert_level( type );
self notify( "awareness_alert_level", type );
}
enemy_threat_anim_defaults()
{
array = [];
array[ "reset" ] = ::enemy_animation_nothing;
array[ "warning" ] = ::enemy_animation_nothing;
if ( self.type == "dog" )
array[ "attack" ] = ::dog_animation_generic;
else
array[ "attack" ] = ::enemy_animation_attack;
return array;
}
enemy_set_threat_anim_behavior( array )
{
def = enemy_threat_anim_defaults();
//set defautls for reset and attack if they're not there
if ( !isdefined( array[ "reset" ] ) )
array[ "reset" ] = def[ "reset" ];
if ( !isdefined( array[ "warning" ] ) )
array[ "warning" ] = def[ "warning" ];
if ( !isdefined( array[ "attack" ] ) )
array[ "attack" ] = def[ "attack" ];
foreach ( key, func in array )
self ai_create_behavior_function( "animation", key, func );
}
enemy_default_threat_anim_behavior()
{
array = enemy_threat_anim_defaults();
self enemy_set_threat_anim_behavior( array );
}