/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
_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( "dust/door_kick" );
	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.animEnt.type = "Cover Right";
		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, "bad_guys" );


	ai = getaiarray( "bad_guys" );
	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 );

	if ( !isdefined( self.usebreachapproach ) || self.usebreachapproach )
	{
		eVolume.animEnt anim_generic_reach( self, sAnimStart );
	}
	else
	{
		self.scriptedarrivalent = eVolume.animEnt;
		eVolume.animEnt anim_generic_reach_and_arrive( self, sAnimStart );
	}

	eVolume.animEnt anim_generic( self, sAnimStart );
	eVolume.animEnt thread anim_generic_loop( self, sAnimIdle, self.ender );
	self.setGoalPos = self.origin;
	eVolume.breachers++ ;

	self.scriptedarrivalent = undefined;
	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, 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 flashBangStart( 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 ( isdefined( self.weapon ) )
	{
		if ( ( isdefined( self.weapon ) ) && ( self.weapon == "none" ) )
			break;
		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;
}
