514 lines
14 KiB
Plaintext
514 lines
14 KiB
Plaintext
#include common_scripts\utility;
|
|
#include maps\_utility;
|
|
#include maps\_anim;
|
|
#include maps\_stealth_utility;
|
|
#include maps\_stealth_shared_utilities;
|
|
|
|
stealth_visibility_enemy_main()
|
|
{
|
|
self enemy_init();
|
|
|
|
self thread enemy_threat_logic();
|
|
|
|
}
|
|
|
|
/************************************************************************************************************/
|
|
/* ENEMY LOGIC */
|
|
/************************************************************************************************************/
|
|
MIN_TIME_TO_LOSE_ENEMY = 20 * 1000;
|
|
|
|
enemy_threat_logic()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "pain_death" );
|
|
|
|
while ( 1 )
|
|
{
|
|
self ent_flag_wait( "_stealth_enabled" );
|
|
|
|
self waittill( "enemy" );
|
|
|
|
if ( !self ent_flag( "_stealth_enabled" ) )
|
|
continue;
|
|
|
|
if ( !isalive( self.enemy ) )
|
|
continue;
|
|
|
|
if ( !self stealth_group_spotted_flag() )
|
|
{
|
|
if ( !self enemy_alert_level_logic( self.enemy ) )
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// if we hit this line it means we're not the first ones to find the enemy
|
|
self maps\_stealth_threat_enemy::enemy_alert_level_change( "attack" );
|
|
}
|
|
|
|
self thread enemy_threat_set_spotted();
|
|
|
|
//wait a minimum of 10 seconds before trying to lose your enemy
|
|
wait 10;
|
|
|
|
// must not have gotten any event from enemy for MIN_TIME_TO_LOSE_ENEMY and must be out of maxVisibleDist
|
|
while ( isdefined( self.enemy ) && self ent_flag( "_stealth_enabled" ) )
|
|
{
|
|
time_past_last_event = gettime() - self lastKnownTime( self.enemy );
|
|
|
|
if ( MIN_TIME_TO_LOSE_ENEMY > time_past_last_event )
|
|
{
|
|
wait ( ( MIN_TIME_TO_LOSE_ENEMY - time_past_last_event ) * 0.001 );
|
|
continue;
|
|
}
|
|
|
|
if ( distance( self.origin, self.enemy.origin ) > self.enemy.maxVisibleDist )
|
|
break;
|
|
|
|
wait .5;
|
|
}
|
|
|
|
if ( !self ent_flag( "_stealth_enabled" ) )
|
|
continue;
|
|
|
|
//if we ever break out - if means everyone actually managed to hide...unbelievable
|
|
if ( isdefined( self.enemy ) )
|
|
enemy_alert_level_forget( self.enemy, 0 );
|
|
|
|
self clearenemy();
|
|
self maps\_stealth_threat_enemy::enemy_alert_level_change( "reset" );
|
|
}
|
|
}
|
|
|
|
enemy_alert_level_logic_start_attacking( enemy )
|
|
{
|
|
//the first check means that a gun shot or something equally bad happened
|
|
//the second check is to see if you've been spotted already twice before
|
|
if ( self ent_flag( "_stealth_bad_event_listener" ) || enemy._stealth.logic.spotted_list[ self.unique_id ] > self._stealth.logic.alert_level.max_warnings )
|
|
{
|
|
/#
|
|
if ( self ent_flag( "_stealth_bad_event_listener" ) )
|
|
self stealth_debug_print( "BROKEN STEALTH. Received ent '" + enemy.unique_id + "' as an enemy from code. Attacked because the reason was a bad_event_listener...ie a gunshot or something equally bad" );
|
|
else
|
|
self stealth_debug_print( "BROKEN STEALTH. Received ent '" + enemy.unique_id + "' as an enemy from code. Attacked because " + enemy.unique_id + " had been spotted more than the max_warning amount of " + self._stealth.logic.alert_level.max_warnings );
|
|
#/
|
|
self maps\_stealth_threat_enemy::enemy_alert_level_change( "attack" );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
enemy_recheck_time = 500;
|
|
|
|
enemy_alert_level_logic( enemy )
|
|
{
|
|
// enemy is not stealthy one bit
|
|
if ( !isdefined( enemy._stealth ) )
|
|
return true;
|
|
|
|
//add this ai to this spotted list
|
|
if ( !isdefined( enemy._stealth.logic.spotted_list[ self.unique_id ] ) )
|
|
enemy._stealth.logic.spotted_list[ self.unique_id ] = 0;
|
|
|
|
while (1)
|
|
{
|
|
enemy._stealth.logic.spotted_list[ self.unique_id ]++;
|
|
|
|
if ( enemy_alert_level_logic_start_attacking( enemy ) )
|
|
return true;
|
|
|
|
//this makes the ai look smart by being aware of your presence
|
|
number = enemy._stealth.logic.spotted_list[ self.unique_id ];
|
|
self maps\_stealth_threat_enemy::enemy_alert_level_change( "warning" + number );
|
|
|
|
//forget about him after a while
|
|
self thread enemy_alert_level_forget( enemy );
|
|
//give the player a chance to hide with this
|
|
self enemy_alert_level_waittime( enemy );
|
|
|
|
if ( gettime() - self lastKnownTime( enemy ) > enemy_recheck_time )
|
|
{
|
|
self clearenemy();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
enemy_threat_set_spotted()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "pain_death" );
|
|
|
|
enemy = self.enemy;
|
|
self.dontEverShoot = undefined;
|
|
|
|
self [[ self._stealth.logic.pre_spotted_func ]]();
|
|
|
|
if ( isdefined( enemy ) )
|
|
level._stealth.group.spotted_enemy[ self.script_stealthgroup ] = enemy;
|
|
|
|
self group_flag_set( "_stealth_spotted" );
|
|
}
|
|
|
|
enemy_prespotted_func_default()
|
|
{
|
|
wait 2.25;// randomfloatrange( 2, 2.5 ); // used to be .25, .5
|
|
}
|
|
|
|
|
|
enemy_alert_level_waittime( enemy )
|
|
{
|
|
//this makes sure that if someone else spots you...then this quits earler
|
|
//than the givin amount of time for the player to try and hide again
|
|
if ( self stealth_group_corpse_flag() || self ent_flag( "_stealth_bad_event_listener" ) )
|
|
return;
|
|
|
|
timefrac = distance( self.origin, enemy.origin ) * .0005;
|
|
waittime = level._stealth.logic.min_alert_level_duration + timefrac;
|
|
|
|
self stealth_debug_print( "WARNING time = " + waittime );
|
|
//iprintlnbold( waittime );
|
|
|
|
level endon( group_get_flagname( "_stealth_spotted" ) );
|
|
self endon( "_stealth_bad_event_listener" );
|
|
|
|
wait( waittime );
|
|
}
|
|
|
|
/************************************************************************************************************/
|
|
/* EVENTS */
|
|
/************************************************************************************************************/
|
|
enemy_event_listeners_logic( type )
|
|
{
|
|
self endon( "death" );
|
|
|
|
while ( 1 )
|
|
{
|
|
self waittill( type, subtype, param ); // subtype and param for debugging
|
|
|
|
if ( !self ent_flag( "_stealth_enabled" ) )
|
|
continue;
|
|
|
|
if ( self ent_flag_exist( "_stealth_behavior_asleep" ) && self ent_flag( "_stealth_behavior_asleep" ) )
|
|
continue;
|
|
|
|
self ent_flag_set( "_stealth_bad_event_listener" );
|
|
}
|
|
}
|
|
|
|
//this function resets all event listeners after they happen...so that we can detect each one multiple times
|
|
enemy_event_listeners_proc()
|
|
{
|
|
self endon( "death" );
|
|
|
|
while ( 1 )
|
|
{
|
|
self ent_flag_wait( "_stealth_bad_event_listener" );
|
|
|
|
wait .65;
|
|
//this time is set so high because apparently the ai can take up to .5 seconds to
|
|
//detect you as an enemy after they have received an event listener...
|
|
//EDIT: after testing i've noticed that they still miss the event because they
|
|
//receive an enemy even after .65 seconds of receiving the event...but it's more
|
|
//fun this way actually...to get away with it once in a while.
|
|
self ent_flag_clear( "_stealth_bad_event_listener" );
|
|
}
|
|
}
|
|
|
|
enemy_event_awareness_notify( type, param )
|
|
{
|
|
self ent_flag_clear( "_stealth_normal" );
|
|
|
|
self._stealth.logic.event.awareness_param[ type ] = param;
|
|
self notify( "event_awareness", type );
|
|
level notify( "event_awareness", type );
|
|
}
|
|
|
|
// for major categories with subtypes (ai_event, awareness_alert_level, awareness_corpse)
|
|
enemy_event_category_awareness( type )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "pain_death" );
|
|
|
|
while ( 1 )
|
|
{
|
|
self waittill( type, subtype, param );
|
|
|
|
if ( !self ent_flag( "_stealth_enabled" ) )
|
|
continue;
|
|
|
|
//
|
|
// special check for dogs deleted from here, see revision history #15
|
|
//
|
|
|
|
switch( type )
|
|
{
|
|
case "awareness_alert_level":
|
|
break;
|
|
|
|
case "ai_event":
|
|
if ( !isdefined( self._stealth.logic.event.aware_aievents[ subtype ] ) )
|
|
continue;
|
|
//this makes sure that magic bullets and friendly bullets that don't cause an enemy notify don't cause guys to break out of animations
|
|
if( subtype == "bulletwhizby" && ( !isdefined( param.team ) || param.team == self.team ) )
|
|
continue;
|
|
// fall through
|
|
|
|
default:
|
|
group_flag_set( "_stealth_event" );
|
|
level thread enemy_event_handle_clear( self.script_stealthgroup );
|
|
break;
|
|
}
|
|
|
|
enemy_event_awareness_notify( subtype, param );
|
|
|
|
waittillframeend;// wait a frame to make sure stealth_spotted didn't get set this frame
|
|
}
|
|
}
|
|
|
|
// for special awareness events
|
|
enemy_event_awareness( type )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "pain_death" );
|
|
|
|
//just to create the key so it exists so other scripts (mainly behavior)
|
|
//can reference it and see what awareness options it has
|
|
self._stealth.logic.event.awareness_param[ type ] = true;
|
|
|
|
while ( 1 )
|
|
{
|
|
self waittill( type, param );
|
|
|
|
if ( !self ent_flag( "_stealth_enabled" ) )
|
|
continue;
|
|
|
|
group_flag_set( "_stealth_event" );
|
|
level thread enemy_event_handle_clear( self.script_stealthgroup );
|
|
|
|
enemy_event_awareness_notify( type, param );
|
|
|
|
waittillframeend;// wait a frame to make sure stealth_spotted didn't get set this frame
|
|
}
|
|
}
|
|
|
|
enemy_event_handle_clear( name )
|
|
{
|
|
end_msg = "enemy_event_handle_clear:" + name + " Proc";
|
|
wait_msg = "enemy_event_handle_clear:" + name + " Cleared";
|
|
|
|
level notify( end_msg );
|
|
level endon( end_msg );
|
|
|
|
wait 2;
|
|
|
|
ai = group_get_ai_in_group( name );
|
|
|
|
if ( ai.size )
|
|
{
|
|
level add_wait( ::array_wait, ai, "event_awareness_waitclear_ai" );
|
|
level add_endon( end_msg );
|
|
level add_func( ::send_notify, wait_msg );
|
|
level thread do_wait();
|
|
|
|
array_thread( ai, ::event_awareness_waitclear_ai, end_msg );
|
|
|
|
level waittill( wait_msg );
|
|
}
|
|
|
|
group_flag_clear( "_stealth_event", name );
|
|
}
|
|
|
|
event_awareness_waitclear_ai( end_msg )
|
|
{
|
|
level endon( end_msg );
|
|
|
|
self event_awareness_waitclear_ai_proc();
|
|
self notify( "event_awareness_waitclear_ai" );
|
|
}
|
|
|
|
event_awareness_waitclear_ai_proc()
|
|
{
|
|
self endon( "death" );
|
|
|
|
waittillframeend;// make sure these flag's are set;
|
|
|
|
check1 = false;
|
|
if ( isdefined( self.ent_flag[ "_stealth_behavior_first_reaction" ] ) )
|
|
check1 = self ent_flag( "_stealth_behavior_first_reaction" );
|
|
|
|
check2 = false;
|
|
if ( isdefined( self.ent_flag[ "_stealth_behavior_reaction_anim" ] ) )
|
|
check1 = self ent_flag( "_stealth_behavior_reaction_anim" );
|
|
|
|
if ( !check1 && !check2 )
|
|
return;
|
|
|
|
self add_wait( ::waittill_msg, "death" );
|
|
self add_wait( ::waittill_msg, "going_back" );
|
|
do_wait_any();
|
|
|
|
self endon( "goal" );
|
|
|
|
allies = array_combine( getaiarray( "allies" ), level.players );
|
|
dist = level._stealth.logic.detect_range[ "hidden" ][ "crouch" ];
|
|
distsquared = dist * dist;
|
|
loop = true;
|
|
|
|
if ( loop )
|
|
{
|
|
loop = false;
|
|
foreach ( actor in allies )
|
|
{
|
|
if ( distancesquared( self.origin, actor.origin ) < distsquared )
|
|
continue;
|
|
loop = true;
|
|
}
|
|
wait 1;
|
|
}
|
|
}
|
|
|
|
enemy_event_declare_to_team( type, name )
|
|
{
|
|
other = undefined;
|
|
team = self.team;
|
|
|
|
while ( 1 )
|
|
{
|
|
if ( !isalive( self ) )
|
|
return;
|
|
|
|
self waittill( type, var1, var2 );
|
|
|
|
if ( isalive( self ) && !self ent_flag( "_stealth_enabled" ) )
|
|
continue;
|
|
|
|
switch( type )
|
|
{
|
|
case "death":
|
|
other = var1;
|
|
break;
|
|
case "damage":
|
|
other = var2;
|
|
break;
|
|
}
|
|
|
|
if ( !isdefined( other ) )
|
|
continue;
|
|
|
|
if ( isplayer( other ) || ( isdefined( other.team ) && other.team != team ) )
|
|
break;
|
|
}
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
// in case of deletion
|
|
return;
|
|
}
|
|
|
|
ai = getaispeciesarray( "bad_guys", "all" );
|
|
|
|
check = int( level._stealth.logic.ai_event[ name ][ level._stealth.logic.detection_level ] );
|
|
|
|
for ( i = 0; i < ai.size; i++ )
|
|
{
|
|
if ( !isalive( ai[ i ] ) )
|
|
continue;
|
|
if ( !isdefined( ai[ i ]._stealth ) )
|
|
continue;
|
|
if ( distance( ai[ i ].origin, self.origin ) > check )
|
|
continue;
|
|
if ( ai[ i ] ent_flag_exist( "_stealth_behavior_asleep" ) && ai[ i ] ent_flag( "_stealth_behavior_asleep" ) )
|
|
continue;
|
|
ai[ i ] ent_flag_set( "_stealth_bad_event_listener" );
|
|
}
|
|
}
|
|
|
|
/************************************************************************************************************/
|
|
/* SETUP */
|
|
/************************************************************************************************************/
|
|
enemy_init()
|
|
{
|
|
assertex( !isdefined( self._stealth ), "you called maps\_stealth_logic::enemy_init() twice on the same ai" );
|
|
|
|
self clearenemy();
|
|
self._stealth = spawnstruct();
|
|
self._stealth.logic = spawnstruct();
|
|
|
|
self ent_flag_init( "_stealth_enabled" );
|
|
self ent_flag_set( "_stealth_enabled" );
|
|
|
|
self ent_flag_init( "_stealth_normal" );
|
|
self ent_flag_set( "_stealth_normal" );
|
|
|
|
self ent_flag_init( "_stealth_attack" );
|
|
|
|
self group_flag_init( "_stealth_spotted" );
|
|
self group_flag_init( "_stealth_event" );
|
|
self group_flag_init( "_stealth_found_corpse" );
|
|
|
|
self group_add_to_global_list();
|
|
if ( !isdefined( level._stealth.behavior.sound[ "spotted" ][ self.script_stealthgroup ] ) )
|
|
level._stealth.behavior.sound[ "spotted" ][ self.script_stealthgroup ] = false;
|
|
|
|
self._stealth.logic.alert_level = spawnstruct();
|
|
self._stealth.logic.alert_level.max_warnings = 0;
|
|
self enemy_alert_level_default_pre_spotted_func();
|
|
|
|
self enemy_event_listeners_init();
|
|
}
|
|
|
|
enemy_event_listeners_init()
|
|
{
|
|
self ent_flag_init( "_stealth_bad_event_listener" );
|
|
|
|
self._stealth.logic.event = spawnstruct();
|
|
|
|
self addAIEventListener( "grenade danger" );
|
|
self addAIEventListener( "gunshot" );
|
|
self addAIEventListener( "gunshot_teammate" );
|
|
self addAIEventListener( "silenced_shot" );
|
|
self addAIEventListener( "bulletwhizby" );
|
|
self addAIEventListener( "projectile_impact" );
|
|
|
|
self thread enemy_event_listeners_logic( "ai_event" ); // catch all of the above eventListener events
|
|
|
|
self thread enemy_event_declare_to_team( "damage", "ai_eventDistPain" );
|
|
self thread enemy_event_declare_to_team( "death", "ai_eventDistDeath" );
|
|
|
|
self thread enemy_event_listeners_proc();
|
|
|
|
self._stealth.logic.event.awareness_param = [];
|
|
|
|
//a lot of these overlap with event listeners - because even though the event
|
|
//listeners above will cause a spotted state - we still want to know
|
|
//why the ai got an enemy and perhaps do specific animations based on that
|
|
|
|
self._stealth.logic.event.aware_aievents = [];
|
|
self._stealth.logic.event.aware_aievents[ "bulletwhizby" ] = true;
|
|
self._stealth.logic.event.aware_aievents[ "projectile_impact" ] = true;
|
|
self._stealth.logic.event.aware_aievents[ "gunshot_teammate" ] = true;
|
|
self._stealth.logic.event.aware_aievents[ "grenade danger" ] = true;
|
|
|
|
self thread enemy_event_category_awareness( "ai_event" );
|
|
self thread enemy_event_category_awareness( "awareness_alert_level" ); // this is actually notified in this script
|
|
self thread enemy_event_category_awareness( "awareness_corpse" ); // this is called from corpse
|
|
|
|
/#
|
|
//these are for extra debug prints
|
|
self thread enemy_event_debug_print( "awareness_alert_level" );
|
|
self thread enemy_event_debug_print( "awareness_corpse" );
|
|
self thread enemy_event_debug_print( "ai_event" );
|
|
#/
|
|
}
|
|
|
|
enemy_alert_level_set_pre_spotted_func( func )
|
|
{
|
|
self._stealth.logic.pre_spotted_func = func;
|
|
}
|
|
|
|
enemy_alert_level_default_pre_spotted_func()
|
|
{
|
|
self._stealth.logic.pre_spotted_func = ::enemy_prespotted_func_default;
|
|
} |