302 lines
6.7 KiB
Plaintext
302 lines
6.7 KiB
Plaintext
|
#include animscripts\combat_utility;
|
||
|
#include animscripts\utility;
|
||
|
#include animscripts\shared;
|
||
|
#include common_scripts\utility;
|
||
|
#include maps\_utility;
|
||
|
|
||
|
#using_animtree( "generic_human" );
|
||
|
|
||
|
|
||
|
doorGrenadeInterval = 5000;
|
||
|
maxDistDoorToEnemySq = 600 * 600;
|
||
|
|
||
|
doorEnterExitCheck()
|
||
|
{
|
||
|
self endon( "killanimscript" );
|
||
|
|
||
|
if ( isdefined( self.disableDoorBehavior ) )
|
||
|
return;
|
||
|
|
||
|
while ( 1 )
|
||
|
{
|
||
|
doorNode = self getDoorPathNode();
|
||
|
if ( isdefined( doorNode ) )
|
||
|
break;
|
||
|
|
||
|
wait 0.2;
|
||
|
}
|
||
|
|
||
|
goingInDoor = ( doorNode.type == "Door Interior" ) || self compareNodeDirToPathDir( doorNode );
|
||
|
|
||
|
if ( goingInDoor )
|
||
|
self doorEnter( doorNode );
|
||
|
else
|
||
|
self doorExit( doorNode );
|
||
|
|
||
|
// waittill doorNode changes
|
||
|
while ( 1 )
|
||
|
{
|
||
|
newDoorNode = self getDoorPathNode();
|
||
|
if ( !isdefined( newDoorNode ) || newDoorNode != doorNode )
|
||
|
break;
|
||
|
|
||
|
wait 0.2;
|
||
|
}
|
||
|
|
||
|
self thread doorEnterExitCheck();
|
||
|
}
|
||
|
|
||
|
teamFlashBangImmune()
|
||
|
{
|
||
|
self endon( "killanimscript" );
|
||
|
|
||
|
self.teamFlashbangImmunity = true;
|
||
|
wait 5;
|
||
|
self.teamFlashbangImmunity = undefined;
|
||
|
}
|
||
|
|
||
|
|
||
|
doDoorGrenadeThrow( node )
|
||
|
{
|
||
|
self thread teamFlashBangImmune();
|
||
|
|
||
|
if ( self.grenadeWeapon == "flash_grenade" )
|
||
|
self notify( "flashbang_thrown" );
|
||
|
|
||
|
self OrientMode( "face current" );
|
||
|
node.nextDoorGrenadeTime = gettime() + doorGrenadeInterval;
|
||
|
self.minInDoorTime = gettime() + 100000; // hack to not forget going indoor
|
||
|
|
||
|
self notify( "move_interrupt" );
|
||
|
self.update_move_anim_type = undefined;
|
||
|
|
||
|
self clearanim( %combatrun, 0.2 );
|
||
|
self.a.movement = "stop";
|
||
|
self waittill( "done_grenade_throw" );
|
||
|
self OrientMode( "face default" );
|
||
|
|
||
|
self.minInDoorTime = gettime() + 5000;
|
||
|
|
||
|
self.grenadeWeapon = self.oldGrenadeWeapon;
|
||
|
self.oldGrenadeWeapon = undefined;
|
||
|
|
||
|
self animscripts\run::endFaceEnemyAimTracking();
|
||
|
self thread animscripts\move::pathChangeCheck();
|
||
|
self thread animscripts\move::restartMoveLoop( true );
|
||
|
}
|
||
|
|
||
|
|
||
|
// try throwing grenade before entering door
|
||
|
doorEnter_TryGrenade( node, grenadeType, ricochet, minDistSq, checkInterval )
|
||
|
{
|
||
|
safeCheckDone = false;
|
||
|
throwAttempts = 3;
|
||
|
throwAnim = undefined;
|
||
|
throwAnim = %CQB_stand_grenade_throw;
|
||
|
|
||
|
doorForward = anglesToForward( node.angles );
|
||
|
if ( node.type == "Door Interior" && !( self compareNodeDirToPathDir( node ) ) )
|
||
|
doorForward = -1 * doorForward;
|
||
|
|
||
|
doorPos = ( node.origin[ 0 ], node.origin[ 1 ], node.origin[ 2 ] + 64 );
|
||
|
throwPos = doorPos;
|
||
|
|
||
|
if ( ricochet )
|
||
|
{
|
||
|
doorPlane = AnglesToRight( node.angles );
|
||
|
dirToDoor = node.origin - self.origin;
|
||
|
|
||
|
// upto 20 units to left or right of door depending on direction to door to make it likely to bounce off door edge
|
||
|
projLength = vectordot( doorPlane, dirToDoor );
|
||
|
if ( projLength > 20 )
|
||
|
projLength = 20;
|
||
|
else if ( projLength < - 20 )
|
||
|
projLength = -20;
|
||
|
|
||
|
throwPos = doorPos + projLength * doorPlane;
|
||
|
}
|
||
|
|
||
|
while ( throwAttempts > 0 )
|
||
|
{
|
||
|
if ( isdefined( self.grenade ) || !isdefined( self.enemy ) )
|
||
|
return;
|
||
|
|
||
|
if ( onSameSideOfDoor( node, doorForward ) )
|
||
|
return;
|
||
|
|
||
|
if ( !self seeRecently( self.enemy, 0.2 ) && self.a.pose == "stand" && distance2DAndHeightCheck( self.enemy.origin - node.origin, maxDistDoorToEnemySq, 128 * 128 ) )
|
||
|
{
|
||
|
if ( isdefined( node.nextDoorGrenadeTime ) && node.nextDoorGrenadeTime > gettime() )
|
||
|
return;
|
||
|
|
||
|
if ( self canShootEnemy() )
|
||
|
return;
|
||
|
|
||
|
// too close to door
|
||
|
dirToDoor = node.origin - self.origin;
|
||
|
if ( lengthSquared( dirToDoor ) < minDistSq )
|
||
|
return;
|
||
|
|
||
|
// don't throw backwards
|
||
|
if ( vectordot( dirToDoor, doorForward ) < 0 )
|
||
|
return;
|
||
|
|
||
|
self.oldGrenadeWeapon = self.grenadeWeapon;
|
||
|
self.grenadeWeapon = grenadeType;
|
||
|
|
||
|
self setActiveGrenadeTimer( self.enemy );
|
||
|
|
||
|
if ( !safeCheckDone )
|
||
|
{
|
||
|
checkPos = doorPos + ( doorForward * 100 );
|
||
|
if ( !( self isGrenadePosSafe( self.enemy, checkPos, 128 ) ) )
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
safeCheckDone = true; // do this only once but do isGrenadePosSafe as late as possible
|
||
|
|
||
|
if ( TryGrenadeThrow( self.enemy, throwPos, throwAnim, getGrenadeThrowOffset( throwAnim ), true, false, true ) )
|
||
|
{
|
||
|
self doDoorGrenadeThrow( node );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
throwAttempts -- ;
|
||
|
wait checkInterval;
|
||
|
|
||
|
// check if door node has past
|
||
|
newNode = self getDoorPathNode();
|
||
|
if ( !isdefined( newNode ) || newNode != node )
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
inDoorCqbToggleCheck()
|
||
|
{
|
||
|
self endon( "killanimscript" );
|
||
|
|
||
|
if ( isdefined( self.disableDoorBehavior ) )
|
||
|
return;
|
||
|
|
||
|
self.isInDoor = false;
|
||
|
|
||
|
while ( 1 )
|
||
|
{
|
||
|
if ( self isInDoor() && !self.doingAmbush )
|
||
|
{
|
||
|
doorEnter_enable_cqbwalk();
|
||
|
}
|
||
|
else if ( !isdefined( self.minInDoorTime ) || self.minInDoorTime < gettime() )
|
||
|
{
|
||
|
self.minInDoorTime = undefined;
|
||
|
doorExit_disable_cqbwalk();
|
||
|
}
|
||
|
|
||
|
wait 0.2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// substitute for enable_cqbwalk so LD can always disable cqb
|
||
|
doorEnter_enable_cqbwalk()
|
||
|
{
|
||
|
if ( !isdefined( self.neverEnableCQB ) && !self.doingAmbush )
|
||
|
{
|
||
|
self.isInDoor = true;
|
||
|
if ( !isdefined( self.cqbWalking ) || !self.cqbWalking )
|
||
|
enable_cqbwalk( true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// substitute for disable_cqbwalk so LD can force CQB even after exiting to outdoor
|
||
|
doorExit_disable_cqbwalk()
|
||
|
{
|
||
|
if ( !isdefined( self.cqbEnabled ) )
|
||
|
{
|
||
|
self.isInDoor = false;
|
||
|
if ( isdefined( self.cqbWalking ) && self.cqbWalking )
|
||
|
disable_cqbwalk();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
maxFragDistSq = 750 * 750;
|
||
|
minFragDistSq = 550 * 550;
|
||
|
maxFlashDistSq = 192 * 192;
|
||
|
minFlashDistSq = 64 * 64;
|
||
|
maxFragHeightDiffSq = 160 * 160;
|
||
|
maxFlashHeightDiffSq = 80 * 80;
|
||
|
|
||
|
distance2DAndHeightCheck( vec, dist2DSq, heightSq )
|
||
|
{
|
||
|
return( ( vec[ 0 ] * vec[ 0 ] + vec[ 1 ] * vec[ 1 ] ) < dist2DSq ) && ( ( vec[ 2 ] * vec[ 2 ] ) < heightSq );
|
||
|
}
|
||
|
|
||
|
|
||
|
onSameSideOfDoor( node, doorForward )
|
||
|
{
|
||
|
assert( isdefined( self.enemy ) );
|
||
|
|
||
|
selfToDoor = node.origin - self.origin;
|
||
|
enemyToDoor = node.origin - self.enemy.origin;
|
||
|
|
||
|
return( vectordot( selfToDoor, doorForward ) * vectordot( enemyToDoor, doorForward ) > 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
doorEnter( node )
|
||
|
{
|
||
|
// try frag
|
||
|
while ( 1 )
|
||
|
{
|
||
|
if ( isdefined( self.doorFragChance ) && ( self.doorFragChance == 0 || self.doorFragChance < randomfloat( 1 ) ) )
|
||
|
break;
|
||
|
|
||
|
if ( distance2DAndHeightCheck( self.origin - node.origin, maxFragDistSq, maxFragHeightDiffSq ) )
|
||
|
{
|
||
|
self doorEnter_TryGrenade( node, "fraggrenade", false, minFragDistSq, 0.3 );
|
||
|
|
||
|
node = self getDoorPathNode();
|
||
|
if ( !isdefined( node ) )
|
||
|
return;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
wait 0.1;
|
||
|
}
|
||
|
|
||
|
// try flashbang
|
||
|
while ( 1 )
|
||
|
{
|
||
|
if ( distance2DAndHeightCheck( self.origin - node.origin, maxFlashDistSq, maxFlashHeightDiffSq ) )
|
||
|
{
|
||
|
self doorEnter_enable_cqbwalk();
|
||
|
self.minInDoorTime = gettime() + 6000;
|
||
|
|
||
|
if ( isdefined( self.doorFlashChance ) && ( self.doorFlashChance == 0 || self.doorFlashChance < randomfloat( 1 ) ) )
|
||
|
return;
|
||
|
|
||
|
self doorEnter_TryGrenade( node, "flash_grenade", true, minFlashDistSq, 0.2 );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
wait 0.1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
doorExit( node )
|
||
|
{
|
||
|
while ( 1 )
|
||
|
{
|
||
|
if ( !self.isInDoor || distanceSquared( self.origin, node.origin ) < 32 * 32 )
|
||
|
{
|
||
|
//self doorExit_disable_cqbwalk();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
wait 0.1;
|
||
|
}
|
||
|
}
|