821 lines
28 KiB
Text
821 lines
28 KiB
Text
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||
|
_breach global script
|
||
|
|
||
|
NOTE: Load \test\breach.d3dbsp to test all supported breaches
|
||
|
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- --
|
||
|
Currently supported breaches( sBreachType )
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- --
|
||
|
"explosive_breach_left"
|
||
|
"shotgunhinges_breach_left":
|
||
|
"flash_breach_no_door_right":
|
||
|
|
||
|
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- --
|
||
|
How to use
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- --
|
||
|
|
||
|
1 ) Add any of the following above _load:::main();
|
||
|
|
||
|
maps\_breach_explosive_left::main();
|
||
|
maps\_breach_flash_right::main();
|
||
|
maps\_breach_hinges_left::main();
|
||
|
|
||
|
|
||
|
* * * * AND * * * * *
|
||
|
|
||
|
maps\_breach::main();
|
||
|
|
||
|
|
||
|
|
||
|
2 ) ROOM VOLUME: Create an info_volume that encompasses the room being assaulted.
|
||
|
( used to stun enemies during explosive breaches, determine where AI throws flash grenades,
|
||
|
and to detect when the room is cleared )
|
||
|
|
||
|
3 ) DOOR: Have the room volume script_linkTo a door( script_model or script_brushmodel ) with an origin that
|
||
|
points in towards the interior of the room( all stacking / breaching anims play on this origin ).
|
||
|
see model com_door_01_handleleft for an example.
|
||
|
|
||
|
If you use a script_brushmodel, you will need to manually target it to a script_origin on the
|
||
|
lower right corner of the door frame that points in towards the interior of the room.
|
||
|
|
||
|
4 ) NO DOOR: If the breach does not require a door( like for flashbang only breaches ), you need to have the
|
||
|
room volume script_linkTo a script_origin on the edge of the door frame pointing in towards the room.
|
||
|
|
||
|
5 ) BLOCKER: The model door needs to target a script_brushmodel blocker( not necessary if you use
|
||
|
a script_brushmodel door instead )
|
||
|
|
||
|
5 ) EXPLODER: All doors must script_linkTo a script_origin in the center of the door with a 'script_exploder' key
|
||
|
of any number. Used to play default fx and will later by used by fx artists for additional
|
||
|
smoke or custom effects in the room
|
||
|
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- --
|
||
|
Function arguments
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- --
|
||
|
< volume > thread breach_think( aBreachers, sBreachType, sHintString, bSpawnHostiles, bPlayDefaultFx, bShoot );
|
||
|
|
||
|
< volume > = The room volume being breached
|
||
|
aBreachers = The array of friendlies performing the breach( can not be more than 2 )
|
||
|
sBreachType = which breach to perform. See / test / breach to see currently supported breaches
|
||
|
sHintString = Pass a hintstrig to display if you trigger the breach with a "use" trigger
|
||
|
bSpawnHostiles = true / false value if you want to spawn hostiles inside right before the breach is started
|
||
|
bPlayDefaultFx = defaults to true. Set to false and add effects to the exploder instead if you like
|
||
|
bShoot = some breaches have the AI firing randomly as they storm into a room while they are still playing the scripted anim. True by default
|
||
|
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- --
|
||
|
PROPERTIES
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- --
|
||
|
.firstBreacher - to have a specific guy be the first to a breach. If undefined, will choose whichever AI is closest to the door at the time the breach script is called
|
||
|
|
||
|
|
||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * **/
|
||
|
|
||
|
#include common_scripts\utility;
|
||
|
#include maps\_utility;
|
||
|
#include maps\_anim;
|
||
|
#using_animtree( "generic_human" );
|
||
|
|
||
|
main()
|
||
|
{
|
||
|
level._effect[ "_breach_doorbreach_detpack" ] = loadfx( "explosions/exp_pack_doorbreach" );
|
||
|
level._effect[ "_breach_doorbreach_kick" ] = loadfx( "explosions/grenadeExp_wood" );
|
||
|
level.scr_sound[ "breach_wooden_door" ] = "detpack_explo_main";
|
||
|
level.scr_sound[ "breach_wood_door_kick" ] = "wood_door_kick";
|
||
|
flag_init( "begin_the_breach" );
|
||
|
}
|
||
|
|
||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||
|
BREACH CORE FUNCTIONS
|
||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * **/
|
||
|
|
||
|
breach_think( aBreachers, sBreachType, sHintString, bSpawnHostiles, bPlayDefaultFx, bShoot )
|
||
|
{
|
||
|
// self == > the room volume being breached
|
||
|
self endon( "breach_abort" );
|
||
|
|
||
|
// Changing core function called when any scripted sequence calls a fire notetrack
|
||
|
// used to determine whether "fire" notetracks should be obeyed or not
|
||
|
if ( isdefined( bShoot ) && ( bShoot == false ) )
|
||
|
{
|
||
|
anim.fire_notetrack_functions[ "scripted" ] = ::breach_fire_straight;
|
||
|
}
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
VARIABLE SETUP
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
self.flashthrown = false;
|
||
|
self.closestAI = undefined;
|
||
|
self.animEnt = undefined;
|
||
|
self.breached = false;
|
||
|
self.breachers = 0;
|
||
|
self.breachersReady = false;
|
||
|
self.singleBreacher = false;
|
||
|
self.readyToBreach = false;
|
||
|
self.AIareInTheRoom = false;
|
||
|
self.aboutToBeBreached = false;
|
||
|
self.cleared = false;
|
||
|
self.hasDoor = true;
|
||
|
self.hasFlashbangs = false;
|
||
|
self.hostilesSpawned = false;
|
||
|
assertEx( ( aBreachers.size <= 2 ), "You cannot send more than 2 AI to perform a breach" );
|
||
|
assertEx( ( isdefined( self.targetname ) ), "Room volume must have a targetname to use the breach fuctions" );
|
||
|
aVolumes = getentarray( self.targetname, "targetname" );
|
||
|
assertEx( ( aVolumes.size == 1 ), "There are multiple room volumes with the same targetname: " + self.targetname );
|
||
|
sRoomName = self.targetname;
|
||
|
self.sBadplaceName = "badplace_" + sRoomName;
|
||
|
self.badplace = getent( "badplace_" + sRoomName, "targetname" );
|
||
|
if ( isdefined( self.badplace ) )
|
||
|
assertEx( ( self.badplace.classname == "script_origin" ), "The badplace entity for volume " + self.targetname + " needs to be a script_origin" );
|
||
|
self.breachtrigger = getent( "trigger_" + sRoomName, "targetname" );
|
||
|
if ( !isdefined( bPlayDefaultFx ) )
|
||
|
bPlayDefaultFx = true;
|
||
|
if ( isdefined( self.breachtrigger ) )
|
||
|
{
|
||
|
switch( self.breachtrigger.classname )
|
||
|
{
|
||
|
case "trigger_use":
|
||
|
assertEx( ( isdefined( sHintString ) ), "You need to pass a hintstring to the function 'breach_think' for the trigger_use " + self.breachtrigger.targetname );
|
||
|
self.triggerHintString = sHintString;
|
||
|
break;
|
||
|
case "trigger_use_touch":
|
||
|
assertEx( ( isdefined( sHintString ) ), "You need to pass a hintstring to the function 'breach_think' for the trigger_use " + self.breachtrigger.targetname );
|
||
|
self.triggerHintString = sHintString;
|
||
|
break;
|
||
|
case "trigger_radius":
|
||
|
break;
|
||
|
case "trigger_multiple":
|
||
|
break;
|
||
|
default:
|
||
|
assertmsg( "entity with targetname '" + self.breachtrigger.targetname + "' must be a trigger_multiple, trigger_radius, trigger_use or trigger_use_touch" );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
switch( sBreachType )
|
||
|
{
|
||
|
case "explosive_breach_left":
|
||
|
break;
|
||
|
case "shotgunhinges_breach_left":
|
||
|
break;
|
||
|
case "flash_breach_no_door_right":
|
||
|
self.hasDoor = false;
|
||
|
self.hasFlashbangs = true;
|
||
|
break;
|
||
|
default:
|
||
|
assertmsg( sBreachType + " is not a valid breachType" );
|
||
|
break;
|
||
|
}
|
||
|
if ( self.hasDoor == true )
|
||
|
{
|
||
|
self.eDoor = getent( self.script_linkto, "script_linkname" );
|
||
|
assertEx( ( isdefined( self.eDoor ) ), "Explosive breach room volume " + self.targetname + " needs to scriptLinkto a single door" );
|
||
|
if ( self.eDoor.classname == "script_model" )
|
||
|
{
|
||
|
self.animEnt = spawn( "script_origin", self.eDoor.origin );
|
||
|
self.animEnt.angles = self.eDoor.angles;
|
||
|
}
|
||
|
else if ( self.eDoor.classname == "script_brushmodel" )
|
||
|
{
|
||
|
self.animEnt = getent( self.eDoor.target, "targetname" );
|
||
|
assertEx( ( isdefined( self.animEnt ) ), "Room volume " + self.targetname + " needs it's script_brushmodel door door to target a script_origin in the lower right hand corner of the door frame. Make this script_origin point in towards the room being breached." );
|
||
|
assertEx( ( self.animEnt.classname == "script_origin" ), "Room volume " + self.targetname + " needs it's script_brushmodel door door to target a script_origin in the lower right hand corner of the door frame. Make this script_origin point in towards the room being breached." );
|
||
|
self.eDoor.vector = anglestoforward( self.animEnt.angles );
|
||
|
}
|
||
|
self.eExploderOrigin = getent( self.eDoor.script_linkto, "script_linkname" );
|
||
|
assertex( isdefined( self.eExploderOrigin ), "A script_brushmodel / script_model door needs to script_linkTo an exploder( script_origin ) to play particles when opened. Targetname: " + self.targetname );
|
||
|
assertEx( ( self.eExploderOrigin.classname == "script_origin" ), "The exploder for this room volume needs to be a script_origin: " + self.targetname );
|
||
|
self.iExploderNum = self.eExploderOrigin.script_exploder;
|
||
|
assertEx( ( isdefined( self.iExploderNum ) ), "There is no exploder number in the key 'script_exploder' for volume " + self.targetname );
|
||
|
}
|
||
|
else if ( self.hasDoor == false )
|
||
|
{
|
||
|
self.animEnt = getent( self.script_linkto, "script_linkname" );
|
||
|
assertEx( ( isdefined( self.animEnt ) ), "If there is no door to be breached, you must have the room volume scriptLinkTo a script_origin instead where the AI will play their idle and enter anims." );
|
||
|
}
|
||
|
if ( self.hasFlashbangs == true )
|
||
|
{
|
||
|
self.grenadeOrigin = getent( "flashthrow_" + sRoomName, "targetname" );
|
||
|
assertEx( ( isdefined( self.grenadeOrigin ) ), "Breaches that have AI throwing flashbangs need a script origin in the center of the door frame with a targetname of: flashthrow_" + sRoomName );
|
||
|
self.grenadeDest = getent( self.grenadeOrigin.target, "targetname" );
|
||
|
assertEx( ( isdefined( self.grenadeDest ) ), "script_origin 'flashthrow_" + sRoomName + "' needs to target another script_origin where you want the flashbang to be thrown to" );
|
||
|
}
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
CLEANUP AND FX
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
self thread breach_abort( aBreachers );
|
||
|
self thread breach_cleanup( aBreachers );
|
||
|
self thread breach_play_fx( sBreachType, bPlayDefaultFx );
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
DECIDE WHO WILL TAKE UP FIRST POSITION
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
iFirstBreachers = 0;
|
||
|
for(i=0;i<aBreachers.size;i++)
|
||
|
{
|
||
|
if ( isdefined( aBreachers[i].firstBreacher ) )
|
||
|
{
|
||
|
iFirstBreachers ++;
|
||
|
self.closestAI = aBreachers[i];
|
||
|
}
|
||
|
}
|
||
|
if ( iFirstBreachers > 0 )
|
||
|
assertEx( iFirstBreachers == 1, ".firstBreacher property has been set on " + iFirstBreachers + " AI. Max is one AI " );
|
||
|
else
|
||
|
self.closestAI = getClosest( self.animEnt.origin, aBreachers );
|
||
|
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
SEND EACH AI TO IDLE
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
if ( aBreachers.size == 1 )
|
||
|
self.singleBreacher = true;
|
||
|
for ( i = 0;i < aBreachers.size;i++ )
|
||
|
aBreachers[ i ] thread breacher_think( self, sBreachType, bShoot );
|
||
|
|
||
|
while ( self.breachers < aBreachers.size )
|
||
|
wait( 0.05 );
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
AI IS READY TO BREACH
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
self notify( "ready_to_breach" );
|
||
|
self.readyToBreach = true;
|
||
|
if ( isdefined( self.breachtrigger ) )
|
||
|
{
|
||
|
self.breachtrigger thread breach_trigger_think( self );
|
||
|
self waittill( "execute_the_breach" );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self notify( "execute_the_breach" );
|
||
|
}
|
||
|
|
||
|
flag_set( "begin_the_breach" );
|
||
|
self.aboutToBeBreached = true;
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
SPAWN HOSTILES RIGHT AS ROOM IS BEING BREACHED( IF SPECIFIED IN ARGUMENT )
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
|
||
|
if ( isdefined( bSpawnHostiles ) && ( bSpawnHostiles == true ) )
|
||
|
{
|
||
|
spawners = getentarray( "hostiles_" + sRoomName, "targetname" );
|
||
|
assertEx( ( isdefined( spawners ) ), "Could not find spawners with targetname of hostiles_" + sRoomName + " for room volume " + self.targetname );
|
||
|
// wait for the AI to start breaching the room before spawning hostiles
|
||
|
self waittill( "spawn_hostiles" );
|
||
|
spawnBreachHostiles( spawners );
|
||
|
self.hostilesSpawned = true;
|
||
|
}
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
GET ARRAY OF ALL HOSTILES TOUCHING THE ROOM VOLUME
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
// badplace to get AI out of the way of the door
|
||
|
if ( isdefined( self.badplace ) )
|
||
|
badplace_cylinder( self.sBadplaceName, -1, self.badplace.origin, self.badplace.radius, 200, "axis" );
|
||
|
|
||
|
|
||
|
ai = getaiarray( "axis" );
|
||
|
aHostiles = [];
|
||
|
for ( i = 0;i < ai.size;i++ )
|
||
|
{
|
||
|
if ( ai[ i ] isTouching( self ) )
|
||
|
aHostiles[ aHostiles.size ] = ai[ i ];
|
||
|
}
|
||
|
if ( aHostiles.size > 0 )
|
||
|
array_thread( aHostiles, ::breach_enemies_stunned, self );
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
WAIT FOR ALL THE AI TO BE IN THE ROOM
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
while ( !self.AIareInTheRoom )
|
||
|
wait( 0.05 );
|
||
|
|
||
|
self notify( "breach_complete" );
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
WAIT FOR ROOM TO BE CLEARED
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
if ( !aHostiles.size )
|
||
|
return;
|
||
|
|
||
|
while ( !self.cleared )
|
||
|
{
|
||
|
wait( 0.05 );
|
||
|
for ( i = 0;i < aHostiles.size;i++ )
|
||
|
{
|
||
|
if ( !isalive( aHostiles[ i ] ) )
|
||
|
aHostiles = array_remove( aHostiles, aHostiles[ i ] );
|
||
|
if ( aHostiles.size == 0 )
|
||
|
self.cleared = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
breach_dont_fire()
|
||
|
{
|
||
|
while ( self.breaching == true )
|
||
|
{
|
||
|
|
||
|
self waittillmatch( "single anim", "fire" );
|
||
|
self.a.lastShootTime = gettime();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
breacher_think( eVolume, sBreachType, bShoot )
|
||
|
{
|
||
|
// self == > the AI doing the breaching
|
||
|
self.breaching = true;
|
||
|
self.breachDoNotFire = undefined;
|
||
|
if ( !isdefined( bShoot ) )
|
||
|
bShoot = true;
|
||
|
// self disable_ai_color();
|
||
|
// self breach_set_animname( "generic" );// dont need to make people animname generic anymore
|
||
|
self pushplayer( true );
|
||
|
self thread give_infinite_ammo();
|
||
|
|
||
|
eVolume endon( "breach_abort" );
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
VARIABLE SETUP
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
self.ender = "stop_idle_" + self getentitynumber();
|
||
|
AInumber = undefined;
|
||
|
sAnimStart = undefined;
|
||
|
sAnimIdle = undefined;
|
||
|
sAnimBreach = undefined;
|
||
|
sAnimFlash = undefined;
|
||
|
|
||
|
|
||
|
|
||
|
if ( self == eVolume.closestAI )
|
||
|
AInumber = "01";
|
||
|
else
|
||
|
AInumber = "02";
|
||
|
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
SPECIAL CASE: SINGLE EXPLOSIVVE GUY NEEDS TO BE NUMBER TWO IN THE STACK
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
if ( ( eVolume.singleBreacher == true ) && ( sBreachType == "explosive_breach_left" ) )
|
||
|
AInumber = "02";
|
||
|
|
||
|
switch( sBreachType )
|
||
|
{
|
||
|
case "explosive_breach_left":
|
||
|
if ( ( isdefined( self.usebreachapproach ) ) && ( self.usebreachapproach == false ) )
|
||
|
sAnimStart = "detcord_stack_left_start_no_approach_" + AInumber;
|
||
|
else
|
||
|
sAnimStart = "detcord_stack_left_start_" + AInumber;
|
||
|
sAnimIdle = "detcord_stack_leftidle_" + AInumber;
|
||
|
sAnimBreach = "detcord_stack_leftbreach_" + AInumber;
|
||
|
break;
|
||
|
case "shotgunhinges_breach_left":
|
||
|
sAnimStart = "shotgunhinges_breach_left_stack_start_" + AInumber;
|
||
|
sAnimIdle = "shotgunhinges_breach_left_stack_idle_" + AInumber;
|
||
|
sAnimBreach = "shotgunhinges_breach_left_stack_breach_" + AInumber;
|
||
|
break;
|
||
|
case "flash_breach_no_door_right":
|
||
|
if ( eVolume.singleBreacher == true )
|
||
|
{
|
||
|
sAnimStart = "flash_stack_right_start_single";
|
||
|
sAnimIdle = "flash_stack_right_idle_single";
|
||
|
sAnimBreach = "flash_stack_right_breach_single";
|
||
|
sAnimFlash = "flash_stack_right_flash_single";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sAnimStart = "flash_stack_right_start_" + AInumber;
|
||
|
sAnimIdle = "flash_stack_right_idle_" + AInumber;
|
||
|
sAnimBreach = "flash_stack_right_breach_" + AInumber;
|
||
|
sAnimFlash = "flash_stack_right_flash_" + AInumber;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
assertmsg( sBreachType + " is not a valid breachType" );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
AI TO BREACH IDLE
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
self breach_set_goaladius( 64 );
|
||
|
eVolume.animEnt anim_generic_reach( self, sAnimStart );
|
||
|
eVolume.animEnt anim_generic( self, sAnimStart );
|
||
|
eVolume.animEnt thread anim_generic_loop( self, sAnimIdle, undefined, self.ender );
|
||
|
self.setGoalPos = self.origin;
|
||
|
eVolume.breachers++ ;
|
||
|
|
||
|
eVolume waittill( "execute_the_breach" );
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
AI FLASHES THE ROOM
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
if ( ( !eVolume.flashthrown ) && ( isdefined( sAnimFlash ) ) )
|
||
|
{
|
||
|
eVolume.animEnt notify( self.ender );
|
||
|
eVolume.animEnt thread anim_generic( self, sAnimFlash );
|
||
|
wait( 1 );
|
||
|
|
||
|
// magic grenade from second guy
|
||
|
if ( ( AInumber == "02" ) || ( eVolume.singleBreacher == true ) )
|
||
|
{
|
||
|
sHandTag = "J_Mid_LE_1";
|
||
|
self attach( "projectile_m84_flashbang_grenade", sHandTag );
|
||
|
oldGrenadeWeapon = self.grenadeWeapon;
|
||
|
self.grenadeWeapon = "flash_grenade";
|
||
|
self.grenadeAmmo++ ;
|
||
|
if ( AInumber == "02" )
|
||
|
self waittillmatch( "single anim", "grenade_throw" );
|
||
|
if ( ( eVolume.singleBreacher == true ) && ( AInumber == "01" ) )
|
||
|
self waittillmatch( "single anim", "fire" );
|
||
|
self magicgrenade( eVolume.grenadeOrigin.origin, eVolume.grenadeDest.origin, level.iFlashFuse );
|
||
|
self detach( "projectile_m84_flashbang_grenade", sHandTag );
|
||
|
self.grenadeWeapon = oldGrenadeWeapon;
|
||
|
self.grenadeAmmo = 0;
|
||
|
}
|
||
|
|
||
|
self waittillmatch( "single anim", "end" );
|
||
|
eVolume.animEnt thread anim_generic_loop( self, sAnimIdle, undefined, self.ender );
|
||
|
wait( .1 );
|
||
|
}
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
PLAY BREACH ANIMS ON BOTH AI
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
eVolume.animEnt notify( self.ender );
|
||
|
|
||
|
if ( bShoot == false )
|
||
|
self.breachDoNotFire = true;
|
||
|
eVolume.animEnt thread anim_generic( self, sAnimBreach );
|
||
|
// eVolume.animEnt anim_generic( self, sAnimBreach );
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
CONDITIONAL: EXPLOSIVE BREACH
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
if ( sBreachType == "explosive_breach_left" )
|
||
|
{
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
BLOW THE DOOR
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
if ( AInumber == "02" )
|
||
|
{
|
||
|
self thread detcord_logic( eVolume );
|
||
|
self waittillmatch( "single anim", "pull fuse" );
|
||
|
wait( 1 );
|
||
|
eVolume notify( "spawn_hostiles" );
|
||
|
eVolume notify( "detpack_about_to_blow" );
|
||
|
self waittillmatch( "single anim", "explosion" );
|
||
|
eVolume notify( "detpack_detonated" );
|
||
|
eVolume.breached = true;
|
||
|
eVolume.eDoor thread door_open( "explosive", eVolume );
|
||
|
eVolume notify( "play_breach_fx" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
CONDITIONAL: SHOTGUN BREACH A
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
else if ( sBreachType == "shotgunhinges_breach_left" )
|
||
|
{
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
SHOOT THE DOOR
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
if ( AInumber == "01" )
|
||
|
{
|
||
|
eVolume notify( "spawn_hostiles" );
|
||
|
self waittillmatch( "single anim", "kick" );
|
||
|
eVolume.eDoor thread door_open( "shotgun", eVolume );
|
||
|
eVolume notify( "play_breach_fx" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
CONDITIONAL: SHACK BREACH
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
else if ( sBreachType == "flash_breach_no_door_right" )
|
||
|
{
|
||
|
// Nothing conditional to do for this breach yet
|
||
|
}
|
||
|
|
||
|
/* -- -- -- -- -- -- -- -- -- -- -- -
|
||
|
AI FINISHES ENTERING
|
||
|
-- -- -- -- -- -- -- -- -- -- -- -- -*/
|
||
|
self waittillmatch( "single anim", "end" );
|
||
|
self notify ( "breach_complete" );
|
||
|
if ( bShoot == false )
|
||
|
self.breachDoNotFire = undefined;
|
||
|
|
||
|
if ( isdefined( level.friendly_breach_thread ) )
|
||
|
self thread [[ level.friendly_breach_thread ]]( eVolume );
|
||
|
|
||
|
eVolume.AIareInTheRoom = true;
|
||
|
// self setgoalvolume( eVolume );
|
||
|
self pushplayer( false );
|
||
|
self breach_reset_animname();
|
||
|
|
||
|
while ( !eVolume.cleared )
|
||
|
wait( 0.05 );
|
||
|
|
||
|
self.breaching = false;
|
||
|
}
|
||
|
|
||
|
breach_fire_straight()
|
||
|
{
|
||
|
// Changing core function called when any scripted sequence calls a fire notetrack
|
||
|
if ( isdefined( self.breachDoNotFire ) )
|
||
|
return;
|
||
|
animscripts\shared::fire_straight();
|
||
|
}
|
||
|
|
||
|
detcord_logic( eVolume )
|
||
|
{
|
||
|
// self == > the AI placing the detcord
|
||
|
self thread sound_effect_play( eVolume );
|
||
|
self waittillmatch( "single anim", "attach prop right" );
|
||
|
sHandTag = "TAG_INHAND";
|
||
|
// spawn detcord model and attach to guy's hand
|
||
|
|
||
|
// attach detcord to AI hand
|
||
|
self attach( "weapon_detcord", sHandTag );
|
||
|
self waittillmatch( "single anim", "detach prop right" );
|
||
|
// spawn detcord model and delete other one
|
||
|
org_hand = self gettagorigin( sHandTag );
|
||
|
angles_hand = self gettagangles( sHandTag );
|
||
|
self detach( "weapon_detcord", sHandTag );
|
||
|
model_detcord = spawn( "script_model", org_hand );
|
||
|
model_detcord setmodel( "weapon_detcord" );
|
||
|
model_detcord.angles = angles_hand;
|
||
|
|
||
|
// delete once door is breached
|
||
|
eVolume waittill( "detpack_detonated" );
|
||
|
radiusdamage( model_detcord.origin, 64, 50, 25 );
|
||
|
model_detcord delete();
|
||
|
}
|
||
|
|
||
|
sound_effect_play( eVolume )
|
||
|
{
|
||
|
self waittillmatch( "single anim", "sound effect" );
|
||
|
thread play_sound_in_space ( "detpack_plant_arming", eVolume.animEnt.origin );
|
||
|
}
|
||
|
|
||
|
breach_enemies_stunned( eRoomVolume )
|
||
|
{
|
||
|
// self == > the room volume being breached
|
||
|
self endon( "death" );
|
||
|
eRoomVolume endon( "breach_aborted" );
|
||
|
|
||
|
eRoomVolume waittill( "detpack_detonated" );
|
||
|
if ( distance( self.origin, eRoomVolume.animEnt.origin ) <= level.detpackStunRadius )
|
||
|
{
|
||
|
// self setFlashBanged( true, 0.25 );
|
||
|
level.stunnedAnimNumber++ ;
|
||
|
if ( level.stunnedAnimNumber > 2 )
|
||
|
level.stunnedAnimNumber = 1;
|
||
|
sStunnedAnim = "exposed_flashbang_v" + level.stunnedAnimNumber;
|
||
|
// self breach_set_animname( "generic" );// dont need to make people animname generic anymore
|
||
|
self.allowdeath = true;
|
||
|
self anim_generic_custom_animmode( self, "gravity", sStunnedAnim );
|
||
|
//self anim_generic( self, sStunnedAnim );
|
||
|
self breach_reset_animname();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
breach_trigger_think( eRoomVolume )
|
||
|
{
|
||
|
// self == > the trigger
|
||
|
eRoomVolume endon( "execute_the_breach" );
|
||
|
eRoomVolume endon( "breach_aborted" );
|
||
|
|
||
|
self thread breach_trigger_cleanup( eRoomVolume );
|
||
|
// self trigger_on();// wtf is this for?
|
||
|
if ( ( self.classname == "trigger_use" ) || ( self.classname == "trigger_use_touch" ) )
|
||
|
{
|
||
|
self setHintString( eRoomVolume.triggerHintString );
|
||
|
if ( isdefined( eRoomVolume.eDoor ) )
|
||
|
{
|
||
|
// spawn a flashing objective on door frame
|
||
|
eRoomVolume.eBreachmodel = spawn( "script_model", eRoomVolume.eDoor.origin );
|
||
|
eRoomVolume.eBreachmodel.angles = eRoomVolume.eDoor.angles;
|
||
|
eRoomVolume.eBreachmodel setmodel( level.door_objmodel );
|
||
|
}
|
||
|
}
|
||
|
self waittill( "trigger" );
|
||
|
eRoomVolume notify( "execute_the_breach" );
|
||
|
}
|
||
|
|
||
|
breach_trigger_cleanup( eRoomVolume )
|
||
|
{
|
||
|
eRoomVolume waittill( "execute_the_breach" );
|
||
|
self trigger_off();
|
||
|
if ( isdefined( eRoomVolume.eBreachmodel ) )
|
||
|
eRoomVolume.eBreachmodel delete();
|
||
|
|
||
|
}
|
||
|
breach_abort( aBreachers )
|
||
|
{
|
||
|
// self == > the room volume being breached
|
||
|
self endon( "breach_complete" );
|
||
|
self waittill( "breach_abort" );
|
||
|
|
||
|
self.cleared = true;
|
||
|
self thread breach_cleanup( aBreachers );
|
||
|
}
|
||
|
|
||
|
breach_cleanup( aBreachers )
|
||
|
{
|
||
|
// self == > the room volume being breached
|
||
|
while ( !self.cleared )
|
||
|
wait( 0.05 );
|
||
|
if ( isdefined( self.badplace ) )
|
||
|
badplace_delete( self.sBadplaceName );
|
||
|
|
||
|
while ( !self.cleared )
|
||
|
wait( 0.05 );
|
||
|
|
||
|
array_thread( aBreachers, ::breach_AI_reset, self );
|
||
|
}
|
||
|
|
||
|
breach_AI_reset( eVolume )
|
||
|
{
|
||
|
self endon( "death" );
|
||
|
|
||
|
self breach_reset_animname();
|
||
|
self breach_reset_goaladius();
|
||
|
eVolume.animEnt notify( self.ender );
|
||
|
self notify( "stop_infinite_ammo" );
|
||
|
|
||
|
self pushplayer( false );
|
||
|
}
|
||
|
|
||
|
breach_play_fx( sBreachType, bPlayDefaultFx )
|
||
|
{
|
||
|
// self == > the room volume being breached
|
||
|
self endon( "breach_aborted" );
|
||
|
self endon( "breach_complete" );
|
||
|
|
||
|
switch( sBreachType )
|
||
|
{
|
||
|
case "explosive_breach_left":
|
||
|
self waittill( "play_breach_fx" );
|
||
|
exploder( self.iExploderNum );
|
||
|
thread play_sound_in_space( level.scr_sound[ "breach_wooden_door" ], self.eExploderOrigin.origin );
|
||
|
if ( bPlayDefaultFx )
|
||
|
playfx( level._effect[ "_breach_doorbreach_detpack" ], self.eExploderOrigin.origin, anglestoforward( self.eExploderOrigin.angles ) );
|
||
|
break;
|
||
|
case "shotgunhinges_breach_left":
|
||
|
self waittill( "play_breach_fx" );
|
||
|
exploder( self.iExploderNum );
|
||
|
if ( bPlayDefaultFx )
|
||
|
playfx( level._effect[ "_breach_doorbreach_kick" ], self.eExploderOrigin.origin, anglestoforward( self.eExploderOrigin.angles ) );
|
||
|
break;
|
||
|
case "flash_breach_no_door_right":
|
||
|
// no effects since there is no door
|
||
|
break;
|
||
|
default:
|
||
|
assertmsg( sBreachType + " is not a valid breachType" );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||
|
BREACH UTILITY FUNCTIONS
|
||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * **/
|
||
|
|
||
|
spawnHostile( eEntToSpawn )
|
||
|
{
|
||
|
spawnedGuy = eEntToSpawn dospawn();
|
||
|
spawn_failed( spawnedGuy );
|
||
|
assert( isDefined( spawnedGuy ) );
|
||
|
return spawnedGuy;
|
||
|
}
|
||
|
|
||
|
spawnBreachHostiles( arrayToSpawn )
|
||
|
{
|
||
|
assertEx( ( arrayToSpawn.size > 0 ), "The array passed to spawnBreachHostiles function is empty" );
|
||
|
spawnedGuys = [];
|
||
|
for ( i = 0;i < arrayToSpawn.size;i++ )
|
||
|
{
|
||
|
guy = spawnHostile( arrayToSpawn[ i ] );
|
||
|
spawnedGuys[ spawnedGuys.size ] = guy;
|
||
|
|
||
|
}
|
||
|
// check to ensure all the guys were spawned
|
||
|
assertEx( ( arrayToSpawn.size == spawnedGuys.size ), "Not all guys were spawned successfully from spawnBreachHostiles" );
|
||
|
|
||
|
// Return an array containing all the spawned guys
|
||
|
return spawnedGuys;
|
||
|
}
|
||
|
|
||
|
give_infinite_ammo()
|
||
|
{
|
||
|
self endon( "death" );
|
||
|
self endon( "stop_infinite_ammo" );
|
||
|
while ( true )
|
||
|
{
|
||
|
self.bulletsInClip = weaponClipSize( self.weapon );
|
||
|
wait( .5 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
door_open( sType, eVolume, bPlaySound )
|
||
|
{
|
||
|
if ( !isDefined( bPlaySound ) )
|
||
|
bPlaySound = true;
|
||
|
|
||
|
if ( bPlaysound == true )
|
||
|
self playsound( level.scr_sound[ "breach_wood_door_kick" ] );
|
||
|
|
||
|
|
||
|
switch( sType )
|
||
|
{
|
||
|
case "explosive":
|
||
|
self thread door_fall_over( eVolume.animEnt );
|
||
|
self door_connectpaths();
|
||
|
self playsound( level.scr_sound[ "breach_wooden_door" ] );
|
||
|
earthquake( 0.4, 1, self.origin, 1000 );
|
||
|
radiusdamage( self.origin, 56, level.maxDetpackDamage, level.minDetpackDamage );
|
||
|
break;
|
||
|
case "shotgun":
|
||
|
self thread door_fall_over( eVolume.animEnt );
|
||
|
self door_connectpaths();
|
||
|
self playsound( level.scr_sound[ "breach_wooden_door" ] );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
door_connectpaths()
|
||
|
{
|
||
|
|
||
|
if ( self.classname == "script_brushmodel" )
|
||
|
self connectpaths();
|
||
|
else
|
||
|
{
|
||
|
blocker = getent( self.target, "targetname" );
|
||
|
assertex( isdefined( blocker ), "A script_model door needs to target a script_brushmodel that blocks the door." );
|
||
|
blocker hide();
|
||
|
blocker notsolid();
|
||
|
blocker connectpaths();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
door_fall_over( animEnt )
|
||
|
{
|
||
|
assert( isdefined( animEnt ) );
|
||
|
vector = undefined;
|
||
|
if ( self.classname == "script_model" )
|
||
|
vector = anglestoforward( self.angles );
|
||
|
else if ( self.classname == "script_brushmodel" )
|
||
|
vector = self.vector;
|
||
|
else
|
||
|
assertmsg( "door needs to be either a script_model or a script_brushmodel" );
|
||
|
dist = ( vector[ 0 ] * 20, vector[ 1 ] * 20, vector[ 2 ] * 20 );
|
||
|
|
||
|
self moveto( self.origin + dist, .5, 0, .5 );
|
||
|
|
||
|
rotationDummy = spawn( "script_origin", ( 0, 0, 0 ) );
|
||
|
rotationDummy.angles = animEnt.angles;
|
||
|
rotationDummy.origin = ( self.origin[ 0 ], self.origin[ 1 ], animEnt.origin[ 2 ] );
|
||
|
|
||
|
self linkTo( rotationDummy );
|
||
|
|
||
|
rotationDummy rotatepitch( 90, 0.45, 0.40 );
|
||
|
wait 0.45;
|
||
|
rotationDummy rotatepitch( - 4, 0.2, 0, 0.2 );
|
||
|
wait 0.2;
|
||
|
rotationDummy rotatepitch( 4, 0.15, 0.15 );
|
||
|
wait 0.15;
|
||
|
self unlink();
|
||
|
rotationDummy delete();
|
||
|
}
|
||
|
|
||
|
breach_set_goaladius( fRadius )
|
||
|
{
|
||
|
if ( !isdefined( self.old_goalradius ) )
|
||
|
self.old_goalradius = self.goalradius;
|
||
|
self.goalradius = fRadius;
|
||
|
}
|
||
|
|
||
|
breach_reset_goaladius()
|
||
|
{
|
||
|
if ( isdefined( self.old_goalradius ) )
|
||
|
self.goalradius = self.old_goalradius;
|
||
|
self.old_goalradius = undefined;
|
||
|
}
|
||
|
|
||
|
|
||
|
breach_set_animname( animname )
|
||
|
{
|
||
|
if ( !isdefined( self.old_animname ) )
|
||
|
self.old_animname = self.animname;
|
||
|
self.animname = animname;
|
||
|
}
|
||
|
|
||
|
breach_reset_animname()
|
||
|
{
|
||
|
if ( isdefined( self.old_animname ) )
|
||
|
self.animname = self.old_animname;
|
||
|
self.old_animname = undefined;
|
||
|
}
|