IW4-Dump-Files/maps/_hiding_door.gsc

467 lines
12 KiB
Plaintext

#include maps\_utility;
#include common_scripts\utility;
#include maps\_anim;
hiding_door_spawner()
{
// place a hiding_door_guy prefab and then place a spawner next to it with script_noteworthy "hiding_door_spawner".
// Spawn the guy however you like (trigger or script)
// Target the spawner to a trigger, this trigger will make the guy open the door.
// Alternatively put a script_flag_wait on the spawner. The guy will wait for the flag to be set before opening the door.
// If you put neither, a trigger_radius will be spawned, using the radius of the spawner if a radius is set
door_orgs = getentarray( "hiding_door_guy_org", "targetname" );
assertex( door_orgs.size, "Hiding door guy with export " + self.export + " couldn't find a hiding_door_org!" );
door_org = getclosest( self.origin, door_orgs );
assertex( distance( door_org.origin, self.origin ) < 256, "Hiding door guy with export " + self.export + " was not placed within 256 units of a hiding_door_org" );
door_org.targetname = undefined;// so future searches won't grab this one
//get door models (and script_brushmodel doors, if applicable)
door_models = getentarray( door_org.target, "targetname" );
door_model = undefined;
brushmodel_door = undefined;
badplaceBrush = undefined;
if( IsDefined( door_org.script_linkto ) )
{
badplaceBrush = door_org get_linked_ent();
}
//if there is only one ent, it must be the script_model door
if ( door_models.size == 1 )
{
door_model = door_models[ 0 ];
}
//if targeting more than one ent, the LD wants to substitute a custom script_brushmodel door
else
{
foreach( ent in door_models )
{
if ( ent.code_classname == "script_brushmodel" )
{
brushmodel_door = ent;
}
else if ( ent.code_classname == "script_model" )
{
door_model = ent;
}
}
assertex( isdefined( brushmodel_door ), "Hiding door org at " + door_org.origin + " targets multiple entities, but not a script_brushmodel door" );
assertex( isdefined( door_model ), "Hiding door org at " + door_org.origin + " targets multiple entities, but not a script_model door" );
}
door_clip = getent( door_model.target, "targetname" );
assert( isdefined( door_model.target ) );
pushPlayerClip = undefined;
if ( isdefined( door_clip.target ) )
pushPlayerClip = getent( door_clip.target, "targetname" );
if ( isdefined( pushPlayerClip ) )
{
door_org thread hiding_door_guy_pushplayer( pushPlayerClip );
if( !IsDefined( level._hiding_door_pushplayer_clips ) )
{
level._hiding_door_pushplayer_clips = [];
}
level._hiding_door_pushplayer_clips[ level._hiding_door_pushplayer_clips.size ] = pushPlayerClip;
}
door_model delete();// we spawn our own door, the one in the prefab is just to aid placement
door = spawn_anim_model( "hiding_door" );
door_org thread anim_first_frame_solo( door, "fire_3" );
if ( isdefined( brushmodel_door ) )
{
brushmodel_door linkTo( door, "door_hinge_jnt" );
door hide();
}
if ( isdefined( door_clip ) )
{
door_clip linkto( door, "door_hinge_jnt" );
door_clip disconnectPaths();
}
trigger = undefined;
if ( isdefined( self.target ) )
{
trigger = getent( self.target, "targetname" );
if ( !issubstr( trigger.classname, "trigger" ) )
trigger = undefined;
}
if ( !isdefined( self.script_flag_wait ) && !isdefined( trigger ) )
{
radius = 200;
if ( isdefined( self.radius ) )
radius = self.radius;
// no trigger mechanism specified, so add a radius trigger
trigger = spawn( "trigger_radius", door_org.origin, 0, radius, 48 );
}
if ( isdefined( badplaceBrush ) )
badPlace_Brush( badplaceBrush getentitynumber(), 0, badplaceBrush, "allies" );
self add_spawn_function( ::hiding_door_guy, door_org, trigger, door, door_clip, badplaceBrush );
}
hiding_door_guy( door_org, trigger, door, door_clip, badplaceBrush )
{
starts_open = hiding_door_starts_open( door_org );
self.animname = "hiding_door_guy";
self endon( "death" );
self endon( "damage" );
self.grenadeammo = 2;
self set_deathanim( "death_2" );
self.allowdeath = true;
self.health = 50000;// buffer health, he "dies" in one hit
guy_and_door = [];
guy_and_door[ guy_and_door.size ] = door;
guy_and_door[ guy_and_door.size ] = self;
thread hiding_door_guy_cleanup( door_org, self, door, door_clip, badplaceBrush );
thread hiding_door_death( door, door_org, self, door_clip, badplaceBrush );
if ( starts_open )
{
// wait for trigger before closing the door
door_org thread anim_loop( guy_and_door, "idle" );
}
else
{
door_org thread anim_first_frame( guy_and_door, "fire_3" );
}
if ( isdefined( trigger ) )
{
wait 0.05;
trigger waittill( "trigger" );
}
else
{
flag_wait( self.script_flag_wait );
}
if ( starts_open )
{
door_org notify( "stop_loop" );
door_org anim_single( guy_and_door, "close" );
}
counter = 0;
timesFired = 0;
for ( ;; )
{
//-----------------
// GET ENEMY AND ENEMY DIRECTION
//-----------------
enemy = level.player;
if ( isdefined( self.enemy ) )
enemy = self.enemy;
assert( isdefined( enemy ) );
direction = hiding_door_get_enemy_direction( door.angles, self.origin, enemy.origin );
//-----------------
// ABORT CONDITIONS
//-----------------
// Abort door behavior if the player comes up behind the AI
if ( self player_entered_backdoor( direction ) )
{
if ( self quit_door_behavior() )
return;
}
// Abort door behavior after peeking a couple times if the player can see him from behind
if ( counter >= 2 )
{
if ( self quit_door_behavior( true ) )
return;
}
//-----------------
// DETERMINE SCENE BASED ON ENEMY DIRECTION
//-----------------
scene = undefined;
if ( direction == "left" || direction == "front" )
{
scene = "fire_3";
}
else if ( direction == "right" )
{
scene = "fire_1";
if ( cointoss() )
scene = "fire_2";
}
else
{
// player or enemy is behind him so just open and close the door and peek
door_org anim_single( guy_and_door, "open" );
door_org anim_single( guy_and_door, "close" );
counter++;
continue;
}
assert( isdefined( scene ) );
//-----------------
// CHARGE CONDITION + CHANCE
//-----------------
if ( self hiding_door_guy_should_charge( direction, enemy, timesFired ) )
{
scene = "jump";
if ( coinToss() )
{
if ( self mayMoveToPoint( animscripts\utility::getAnimEndPos( level.scr_anim[ self.animname ][ "kick" ] ) ) )
scene = "kick";
}
// connect paths on the door and handle player clip
thread hiding_door_death_door_connections( door_clip, badplaceBrush );
door_org notify( "push_player" );
// stop the thread that waits for him to break out of door behavior to open the door since this anim opens it for us
self notify( "charge" );
// guy charges out
self.allowdeath = true;
self.health = 100;
self clear_deathanim();
door_org anim_single( guy_and_door, scene );
// now he goes to exposed combat
self quit_door_behavior();
return;
}
//-----------------
// THROW A GRENADE?
//-----------------
// randomly do grenade throw if the AI has grenade ammo. More likely if hte AI has more grenades
if ( self hiding_door_guy_should_throw_grenade( direction, timesFired ) )
{
self.grenadeammo--;
scene = "grenade";
}
counter = 0;
timesFired++;
//-----------------
// DO ANIM
//-----------------
door_org thread anim_single( guy_and_door, scene );
// delay the settime by a frame or it wont work
// this is so we can skip the slow creep part of the animation
delaythread( 0.05, ::anim_set_time, guy_and_door, scene, 0.3 );
door_org waittill( scene );
//-----------------
// IDLE FOR A MOMENT
//-----------------
door_org thread anim_first_frame( guy_and_door, "open" );
wait( randomfloatrange( 0.2, 1.0 ) );
door_org notify( "stop_loop" );
}
}
quit_door_behavior( sightTraceRequired, door_org )
{
if ( !isdefined( sightTraceRequired ) )
sightTraceRequired = false;
if ( sightTraceRequired )
{
if ( !sightTracePassed( level.player getEye(), self getEye(), false, self ) )
return false;
}
self.health = 100;
self clear_deathanim();
self.goalradius = 512;
self setGoalPos( self.origin );
self notify( "quit_door_behavior" );
self stopanimscripted();
self notify( "killanimscript" );
return true;
}
player_entered_backdoor( direction )
{
if ( direction != "behind" )
return false;
d = distance( self.origin, level.player.origin );
if ( d > 250 )
return false;
if ( !sightTracePassed( level.player getEye(), self getEye(), false, self ) )
return false;
return true;
}
hiding_door_guy_should_charge( direction, enemy, timesFired )
{
TIMES_FIRED_MIN = 3;
MIN_DIST = 100;
MAX_DIST = 600;
if ( timesFired < TIMES_FIRED_MIN )
return false;
if ( enemy != level.player )
return false;
if ( direction != "front" )
return false;
d = distance( self.origin, level.player.origin );
if ( d < MIN_DIST )
return false;
if ( d > MAX_DIST )
return false;
return coinToss();
}
hiding_door_guy_should_throw_grenade( direction, timesFired )
{
if ( timesFired < 1 )
return false;
if ( direction == "behind" )
return false;
if ( randomint( 100 ) < 25 * self.grenadeammo )
return true;
return false;
}
hiding_door_get_enemy_direction( viewerAngles, viewerOrigin, targetOrigin )
{
forward = anglesToForward( viewerAngles );
vFacing = vectorNormalize( forward );
anglesToFacing = vectorToAngles( vFacing );
anglesToPoint = vectorToAngles( targetOrigin - viewerOrigin );
angle = anglesToFacing[ 1 ] - anglesToPoint[ 1 ];
angle += 360;
angle = int( angle ) % 360;
direction = undefined;
if ( angle >= 90 && angle <= 270 )
direction = "behind";
else if ( angle >= 300 || angle <= 45 )
direction = "front";
else if ( angle < 90 )
direction = "right";
else if ( angle > 270 )
direction = "left";
assert( isdefined( direction ) );
return direction;
}
hiding_door_guy_cleanup( door_org, guy, door, door_clip, badplaceBrush )
{
guy endon( "charge" );
// if the guy gets deleted before the sequence happens this thread will catch that and clean up any problems that could arise
guy waittill_either( "death", "quit_door_behavior" );
// stop the looping animations because the guy is removed now
door_org notify( "stop_loop" );
thread hiding_door_death_door_connections( door_clip, badplaceBrush );
door_org notify( "push_player" );
if ( !isdefined( door.played_death_anim ) )
{
door.played_death_anim = true;
door_org thread anim_single_solo( door, "death_2" );
}
}
hiding_door_guy_pushplayer( pushPlayerClip )
{
self waittill( "push_player" );
pushPlayerClip moveto( self.origin, 1.5 );
wait 1.5;
pushPlayerClip delete();
}
hiding_door_guy_grenade_throw( guy )
{
// called from a notetrack
startOrigin = guy getTagOrigin( "J_Wrist_RI" );
strength = ( distance( level.player.origin, guy.origin ) * 2.0 );
if ( strength < 300 )
strength = 300;
if ( strength > 1000 )
strength = 1000;
vector = vectorNormalize( level.player.origin - guy.origin );
velocity = vector_multiply( vector, strength );
guy magicGrenadeManual( startOrigin, velocity, randomfloatrange( 3.0, 5.0 ) );
}
hiding_door_death( door, door_org, guy, door_clip, badplaceBrush )
{
guy endon( "charge" );
guy endon( "quit_door_behavior" );
guy waittill( "damage", dmg, attacker );
if ( !isalive( guy ) )
return;
thread hiding_door_death_door_connections( door_clip, badplaceBrush );
door_org notify( "push_player" );
door_org thread anim_single_solo( guy, "death_2" );
if ( !isdefined( door.played_death_anim ) )
{
door.played_death_anim = true;
door_org thread anim_single_solo( door, "death_2" );
}
wait( 0.5 );
if ( isalive( guy ) )
{
if ( IsDefined( attacker ) )
{
guy Kill( ( 0, 0, 0 ), attacker );
}
else
{
//guy.a.nodeath = true;
guy kill( ( 0, 0, 0 ) );
}
}
}
hiding_door_death_door_connections( door_clip, badplaceBrush )
{
wait 2;
if ( isdefined( door_clip ) )
door_clip disconnectpaths();
if ( isdefined( badplaceBrush ) )
badPlace_Delete( badplaceBrush getentitynumber() );
}
hiding_door_starts_open( door_org )
{
return ( isdefined( door_org.script_noteworthy ) && ( door_org.script_noteworthy == "starts_open" ) );
}