822 lines
27 KiB
Text
822 lines
27 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)
|
|
{
|
|
assert(0);//you should't get called
|
|
|
|
//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;
|
|
}
|