mirror of
https://github.com/reaaLx/nx1-gsc-dump.git
synced 2025-04-22 09:05:45 +00:00
1774 lines
44 KiB
Plaintext
1774 lines
44 KiB
Plaintext
#include common_scripts\utility;
|
|
#include animscripts\utility;
|
|
#include animscripts\combat_utility;
|
|
#include maps\_utility;
|
|
|
|
#using_animtree( "generic_human" );
|
|
|
|
|
|
//
|
|
// Damage Yaw
|
|
//
|
|
// front
|
|
// /----|----\
|
|
// / 180 \
|
|
// /\ | /\
|
|
// / -135 | 135 \
|
|
// | \ | / |
|
|
// left|-90----+----90-|right
|
|
// | / | \ |
|
|
// \ -45 | 45 /
|
|
// \/ | \/
|
|
// \ 0 /
|
|
// \----|----/
|
|
// back
|
|
|
|
main()
|
|
{
|
|
self endon( "killanimscript" );
|
|
|
|
// make sure the guy doesn't talk after death
|
|
self stopsoundchannel( "voice" );
|
|
changeTime = 0.3;
|
|
self clearanim( %scripted_talking, changeTime );
|
|
|
|
// don't abort at this point unless you're going to play another animation!
|
|
// just playing ragdoll isn't sufficient because sometimes ragdoll fails, and then
|
|
// you'll just have a corpse standing around in limbo.
|
|
|
|
if ( self.a.nodeath == true )
|
|
return;
|
|
|
|
if ( isdefined( self.deathFunction ) )
|
|
{
|
|
result = self [[ self.deathFunction ]]();
|
|
if ( !isdefined( result ) )
|
|
result = true;
|
|
if ( result )
|
|
return;
|
|
}
|
|
|
|
animscripts\utility::initialize( "death" );
|
|
|
|
// should move this to squad manager somewhere...
|
|
removeSelfFrom_SquadLastSeenEnemyPos( self.origin );
|
|
|
|
anim.numDeathsUntilCrawlingPain -- ;
|
|
anim.numDeathsUntilCornerGrenadeDeath -- ;
|
|
|
|
self notify( "deathanim" );
|
|
|
|
if ( isDefined( self.ragdoll_immediate ) || self.forceRagdollImmediate )
|
|
{
|
|
self doImmediateRagdollDeath();
|
|
// ragdoll can fail so don't assume that we can quit the function
|
|
}
|
|
|
|
if ( isDefined( self.deathanim ) )
|
|
{
|
|
playDeathAnim( self.deathAnim );
|
|
|
|
// Added so that I can do special stuff in Level scripts on an ai
|
|
if ( isdefined( self.deathanimscript ) )
|
|
self [[ self.deathanimscript ]]();
|
|
return;
|
|
}
|
|
|
|
if ( isDefined( self.deathscript ) )
|
|
{
|
|
self [[ self.deathscript ]]();
|
|
return;
|
|
}
|
|
|
|
explosiveDamage = self animscripts\pain::wasDamagedByExplosive();
|
|
if ( is_railgun( self.damageWeapon ) )
|
|
{
|
|
if ( RandomInt( 3 ) == 0 )
|
|
{
|
|
explosiveDamage = true;
|
|
}
|
|
}
|
|
|
|
if ( self.damageLocation == "helmet" || self.damageLocation == "head" )
|
|
self helmetPop( true );
|
|
else if ( explosiveDamage && randomint( 3 ) == 0 )
|
|
self helmetPop( false );
|
|
|
|
self clearanim( %root, 0.3 );
|
|
|
|
if ( !damageLocationIsAny( "head", "helmet" ) )
|
|
{
|
|
if ( self.dieQuietly )
|
|
{
|
|
// replace with actual die quietly gurglesque sound
|
|
// if ( randomint(3) < 2 )
|
|
// self animscripts\face::SayGenericDialogue("pain");
|
|
}
|
|
else
|
|
{
|
|
PlayDeathSound();
|
|
}
|
|
}
|
|
|
|
if ( explosiveDamage && playExplodeDeathAnim() )
|
|
return;
|
|
|
|
// different from deathFunction above, doesn't skip explosion deaths, immediate ragdoll, sounds, etc
|
|
if ( isdefined( self.specialDeathFunc ) )
|
|
{
|
|
if ( [[ self.specialDeathFunc ]]() )
|
|
return;
|
|
}
|
|
|
|
// TODO: replace these with the above specialDeathFunc
|
|
if ( specialDeath() )
|
|
return;
|
|
|
|
// Dan - Commenting out gib.
|
|
//if ( play_bulletgibbed_death_anim())
|
|
//{
|
|
// return;
|
|
//}
|
|
|
|
deathAnim = getDeathAnim();
|
|
|
|
/#
|
|
if ( getdvarint( "scr_paindebug" ) == 1 )
|
|
println( "^2Playing pain: ", deathAnim, " ; pose is ", self.a.pose );
|
|
#/
|
|
|
|
playDeathAnim( deathAnim );
|
|
}
|
|
|
|
doImmediateRagdollDeath()
|
|
{
|
|
self animscripts\shared::DropAllAIWeapons();
|
|
self.skipDeathAnim = true; // this helps playDeathAnim() do failsafes for ragdoll failures later
|
|
|
|
initialImpulse = 10;
|
|
|
|
damageType = common_scripts\_destructible::getDamageType( self.damageMod );
|
|
if( IsDefined( self.attacker ) && self.attacker == level._player && damageType == "melee" )
|
|
{
|
|
initialImpulse = 5;
|
|
}
|
|
|
|
if ( self.subclass == "moon" )
|
|
{
|
|
initialImpulse = 5;
|
|
|
|
// tagBR< note >: This is to fix explosion barrel deaths during traversals (impulse was way too high)
|
|
if ( damageType == "splash" )
|
|
{
|
|
initialImpulse = 2;
|
|
}
|
|
}
|
|
|
|
damageTaken = self.damagetaken;
|
|
if ( damageType == "bullet" )
|
|
damageTaken = max( damageTaken, 300 );
|
|
|
|
if ( GetDVarInt( "zero_g_proto" ) == 1 )
|
|
{
|
|
self animscripts\shared::detachAllWeaponModels();
|
|
initialImpulse = 4;
|
|
damageTaken /= 3;
|
|
}
|
|
|
|
directionScale = initialImpulse * damageTaken;
|
|
directionUp = max( 0.3, self.damagedir[ 2 ] );
|
|
direction = ( self.damagedir[ 0 ], self.damagedir[ 1 ], directionUp );
|
|
direction *= directionScale;
|
|
|
|
if ( self.forceRagdollImmediate )
|
|
direction += self.prevAnimDelta * 20 * 10; // 20 frames/sec
|
|
|
|
self startragdollfromimpact( self.damagelocation, direction );
|
|
|
|
// wait a bit so that the ragdoll can start before the death script tries to play a regular
|
|
// death animation as a failsafe - if ragdolling, the regular death anim won't do anything when called
|
|
wait( 0.2 );
|
|
}
|
|
|
|
playDeathAnim( deathAnim )
|
|
{
|
|
if ( !animHasNoteTrack( deathAnim, "dropgun" ) && !animHasNoteTrack( deathAnim, "fire_spray" ) )// && !animHasNotetrack( deathAnim, "gun keep" )
|
|
self animscripts\shared::DropAllAIWeapons();
|
|
|
|
if ( isdefined( self.locked_combat ) )
|
|
{
|
|
self StopAnimScripted();
|
|
}
|
|
|
|
//if ( isdefined( self.faceDamageDir ) )
|
|
// self orientmode( "face angle", self.damageYaw );
|
|
|
|
self setFlaggedAnimKnobAllRestart( "deathanim", deathAnim, %body, 1, .1 );
|
|
|
|
if ( IsDefined( self.skipDeathAnim ) )
|
|
{
|
|
ASSERTEX( self.skipDeathAnim, "self.skipDeathAnim must be either true or undefined." );
|
|
|
|
//self thread do_gib();
|
|
|
|
if( !isdefined( self.noragdoll ) || !self.noRagdoll )
|
|
self startRagDoll();
|
|
|
|
wait( 0.05 );
|
|
// failsafe in case ragdoll fails: he'll still be playing a deathanim,
|
|
// but at least he'll fall to the ground
|
|
self AnimMode( "gravity" );
|
|
}
|
|
else if ( !animHasNotetrack( deathanim, "start_ragdoll" ) )
|
|
{
|
|
//self thread do_gib();
|
|
self thread waitForRagdoll( getanimlength( deathanim ) * 0.35 );
|
|
}
|
|
|
|
// do we really need this anymore?
|
|
/#
|
|
if ( getdebugdvar( "debug_grenadehand" ) == "on" )
|
|
{
|
|
if ( animhasnotetrack( deathAnim, "bodyfall large" ) )
|
|
return;
|
|
if ( animhasnotetrack( deathAnim, "bodyfall small" ) )
|
|
return;
|
|
|
|
println( "Death animation ", deathAnim, " does not have a bodyfall notetrack" );
|
|
iprintlnbold( "Death animation needs fixing (check console and report bug in the animation to Boon)" );
|
|
}
|
|
#/
|
|
|
|
// SRS 11/20/08: blood pools don't always line up with ragdoll corpses, so skip them if
|
|
// we did ragdoll without a death anim (which usually sends the body farther away from the death spot)
|
|
if ( !IsDefined( self.skipDeathAnim ) )
|
|
{
|
|
self thread playDeathFX();
|
|
}
|
|
|
|
self animscripts\shared::DoNoteTracks( "deathanim" );
|
|
self animscripts\shared::DropAllAIWeapons();
|
|
}
|
|
|
|
waitForRagdoll( time )
|
|
{
|
|
wait( time );
|
|
if ( isdefined( self ) )
|
|
self animscripts\shared::DropAllAIWeapons();
|
|
if ( isdefined( self ) && ( !isdefined( self.noragdoll ) || !self.noRagdoll ) )
|
|
self startragdoll();
|
|
}
|
|
|
|
playDeathFX()
|
|
{
|
|
self endon( "killanimscript" );
|
|
//iprintlnbold("bleed'n");
|
|
|
|
if ( self.stairsState != "none" )
|
|
return;
|
|
|
|
wait 2;
|
|
|
|
play_blood_pool();
|
|
}
|
|
|
|
play_blood_pool( note, flagName )
|
|
{
|
|
if ( !isdefined( self ) )
|
|
return;
|
|
|
|
if ( isdefined( self.skipBloodPool ) )
|
|
{
|
|
assertex( self.skipBloodPool, "Setting must be either true or undefined" );
|
|
return;
|
|
}
|
|
/*
|
|
//play vacuum blood pool fx
|
|
if( GetDvar("environment_pressurized") == "0" )
|
|
{
|
|
iprintlnbold("got moon blood");
|
|
tagPos = self gettagorigin( "j_SpineUpper" ); // rough tag to play fx on
|
|
tagAngles = self gettagangles( "j_SpineUpper" );
|
|
forward = anglestoforward( tagAngles );
|
|
up = anglestoup( tagAngles );
|
|
right = anglestoright( tagAngles );
|
|
|
|
tagPos = tagPos + vector_multiply( forward, -8.5 ) + vector_multiply( up, 5 ) + vector_multiply( right, 0 );
|
|
|
|
trace = bulletTrace( tagPos + ( 0, 0, 30 ), tagPos - ( 0, 0, 100 ), false, undefined );
|
|
|
|
if ( trace[ "normal" ][2] > 0.9 )
|
|
{
|
|
playfx( level._effect[ "blood_pool_vacuum" ], tagPos, forward, up );
|
|
}
|
|
return;
|
|
}
|
|
*/
|
|
//play normal blood pool fx
|
|
tagPos = self gettagorigin( "j_SpineUpper" ); // rough tag to play fx on
|
|
tagAngles = self gettagangles( "j_SpineUpper" );
|
|
forward = anglestoforward( tagAngles );
|
|
up = anglestoup( tagAngles );
|
|
right = anglestoright( tagAngles );
|
|
|
|
tagPos = tagPos + vector_multiply( forward, -8.5 ) + vector_multiply( up, 5 ) + vector_multiply( right, 0 );
|
|
|
|
trace = bulletTrace( tagPos + ( 0, 0, 30 ), tagPos - ( 0, 0, 100 ), false, undefined );
|
|
|
|
if( GetDvar("environment_pressurized") == "0" )
|
|
{
|
|
for ( i = 0; i < 4; i++ )
|
|
{
|
|
rand_x = RandomIntRange( -30, 30 );
|
|
rand_y = RandomIntRange( -10, 10 );
|
|
rand_z = RandomIntRange( 0, 1 );
|
|
randomoffset = ( rand_x, rand_y, rand_z );
|
|
bloodpos = tagpos + randomoffset;
|
|
newtrace = bulletTrace( bloodpos + ( 0, 0, 30 ), bloodpos - ( 0, 0, 100 ), false, undefined );
|
|
if ( newtrace[ "normal" ][2] > 0.9 )
|
|
{
|
|
if ( newtrace[ "position" ][2] < bloodpos[2] )
|
|
{
|
|
playfx( level._effect[ "blood_pool_vacuum" ], bloodpos );
|
|
//newbloodpos = vector_multiply( newtrace[ "position" ][0], bloodpos[0] ) + vector_multiply( newtrace[ "position" ][1], bloodpos[1] ) + vector_multiply( newtrace[ "position" ][2], bloodpos[2] );
|
|
|
|
//playfx( level._effect[ "blood_pool_steam" ], newbloodpos );
|
|
|
|
//iprintlnbold( newbloodpos );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( trace[ "normal" ][2] > 0.9 )
|
|
{
|
|
playfx( level._effect[ "deathfx_bloodpool_generic" ], tagPos );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// TODO: replace these with specialDeathFunc
|
|
// Special death is for corners, rambo behavior, mg42's, anything out of the ordinary stand, crouch and prone.
|
|
// It returns true if it handles the death for the special animation state, or false if it wants the regular
|
|
// death function to handle it.
|
|
specialDeath()
|
|
{
|
|
if ( self.a.special == "none" )
|
|
return false;
|
|
|
|
switch( self.a.special )
|
|
{
|
|
case "cover_right":
|
|
if ( self.a.pose == "stand" )
|
|
{
|
|
DoDeathFromArray( getDeathAnimByName("cover_right_stand") );
|
|
}
|
|
else
|
|
{
|
|
deathArray = [];
|
|
//TagCC<NOTE>: why is this different from the left? it doesnt cut out death back.
|
|
if ( damageLocationIsAny( "head", "neck" ) )
|
|
{
|
|
DoDeathFromArray( getDeathAnimByName("cover_right_crouch_head_neck") );
|
|
}
|
|
else
|
|
{
|
|
DoDeathFromArray( getDeathAnimByName("cover_right_crouch") );
|
|
}
|
|
}
|
|
return true;
|
|
|
|
case "cover_left":
|
|
if ( self.a.pose == "stand" )
|
|
{
|
|
DoDeathFromArray( getDeathAnimByName("cover_left_stand") );
|
|
}
|
|
else
|
|
{
|
|
DoDeathFromArray( getDeathAnimByName("cover_left_crouch") );
|
|
}
|
|
return true;
|
|
|
|
case "cover_stand":
|
|
DoDeathFromArray( getDeathAnimByName("cover_stand") );
|
|
return true;
|
|
|
|
case "cover_crouch":
|
|
deathArray = [];
|
|
if ( damageLocationIsAny( "head", "neck" ) && ( self.damageyaw > 135 || self.damageyaw <= -45 ) ) // Front / Left quadrant
|
|
{
|
|
deathArray = getDeathAnimByName("cover_crouch_head");
|
|
}
|
|
else if ( ( self.damageyaw > - 45 ) && ( self.damageyaw <= 45 ) ) // Back quadrant
|
|
{
|
|
deathArray = getDeathAnimByName("cover_crouch_back");
|
|
}
|
|
|
|
deathArray = array_combine( deathArray, getDeathAnimByName( "cover_crouch" ) );
|
|
DoDeathFromArray( deathArray );
|
|
|
|
return true;
|
|
|
|
case "saw":
|
|
if ( self.a.pose == "stand" )
|
|
{
|
|
DoDeathFromArray( getDeathAnimByName("saw_stand") );
|
|
}
|
|
else if ( self.a.pose == "crouch" )
|
|
{
|
|
DoDeathFromArray( getDeathAnimByName("saw_crouch") );
|
|
}
|
|
else
|
|
{
|
|
DoDeathFromArray( getDeathAnimByName("saw_prone") );
|
|
}
|
|
return true;
|
|
|
|
case "dying_crawl":
|
|
if ( isdefined( self.a.onback ) && self.a.pose == "crouch" )
|
|
{
|
|
DoDeathFromArray( getDeathAnimByName("crawl_crouch") );
|
|
}
|
|
else
|
|
{
|
|
DoDeathFromArray( getDeathAnimByName("crawl_prone") );
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
DoDeathFromArray( deathArray )
|
|
{
|
|
deathAnim = deathArray[ randomint( deathArray.size ) ];
|
|
|
|
playDeathAnim( deathAnim );
|
|
//nate - adding my own special death flag on top of special death.
|
|
if ( isdefined( self.deathanimscript ) )
|
|
self [[ self.deathanimscript ]]();
|
|
}
|
|
|
|
|
|
PlayDeathSound()
|
|
{
|
|
self animscripts\face::SayGenericDialogue( "death" );
|
|
}
|
|
|
|
print3dfortime( place, text, time )
|
|
{
|
|
numframes = time * 20;
|
|
for ( i = 0; i < numframes; i++ )
|
|
{
|
|
print3d( place, text );
|
|
wait .05;
|
|
}
|
|
}
|
|
|
|
helmetPop( was_headshot )
|
|
{
|
|
if ( !isdefined( self ) )
|
|
return;
|
|
// used to check self removableHat() in cod2... probably not necessary though
|
|
|
|
// Potentially switch to a headshot version of the head model.
|
|
if ( was_headshot && isdefined( self.headshotModel ) )
|
|
{
|
|
self Detach( self.headmodel );
|
|
self.headmodel = self.headshotModel;
|
|
self Attach( self.headshotModel );
|
|
}
|
|
|
|
// Potentially pop a hat off.
|
|
if ( isdefined( self.hatModel ) )
|
|
{
|
|
partName = GetPartName( self.hatModel, 0 );
|
|
model = spawn( "script_model", self.origin + ( 0, 0, 64 ) );
|
|
model setmodel( self.hatModel );
|
|
model.origin = self GetTagOrigin( partName );// self . origin + ( 0, 0, 64 );
|
|
model.angles = self GetTagAngles( partName );// ( -90, 0 + randomint( 90 ), 0 + randomint( 90 ) );
|
|
model thread helmetLaunch( self.damageDir );
|
|
|
|
hatModel = self.hatModel;
|
|
self.hatModel = undefined;
|
|
|
|
wait 0.05;
|
|
|
|
if ( !isdefined( self ) )
|
|
return;
|
|
self detach( hatModel, "" );
|
|
}
|
|
}
|
|
|
|
helmetLaunch( damageDir )
|
|
{
|
|
launchForce = damageDir;
|
|
launchForce = launchForce * randomFloatRange( 2000, 4000 );
|
|
|
|
forcex = launchForce[ 0 ];
|
|
forcey = launchForce[ 1 ];
|
|
forcez = randomFloatRange( 1500, 3000 );
|
|
|
|
contactPoint = self.origin + ( randomfloatrange( -1, 1 ), randomfloatrange( -1, 1 ), randomfloatrange( -1, 1 ) ) * 5;
|
|
|
|
self PhysicsLaunchClient( contactPoint, ( forcex, forcey, forcez ) );
|
|
|
|
wait 60;
|
|
|
|
while ( 1 )
|
|
{
|
|
if ( !isdefined( self ) )
|
|
return;
|
|
|
|
if ( distanceSquared( self.origin, level._player.origin ) > 512 * 512 )
|
|
break;
|
|
|
|
wait 30;
|
|
}
|
|
|
|
self delete();
|
|
}
|
|
|
|
|
|
removeSelfFrom_SquadLastSeenEnemyPos( org )
|
|
{
|
|
for ( i = 0;i < anim.squadIndex.size;i++ )
|
|
anim.squadIndex[ i ] clearSightPosNear( org );
|
|
}
|
|
|
|
|
|
clearSightPosNear( org )
|
|
{
|
|
if ( !isdefined( self.sightPos ) )
|
|
return;
|
|
|
|
if ( distance( org, self.sightPos ) < 80 )
|
|
{
|
|
self.sightPos = undefined;
|
|
self.sightTime = gettime();
|
|
}
|
|
}
|
|
|
|
|
|
shouldDoRunningForwardDeath()
|
|
{
|
|
if ( self.a.movement != "run" )
|
|
return false;
|
|
|
|
if ( self getMotionAngle() > 60 || self getMotionAngle() < - 60 )
|
|
return false;
|
|
|
|
/*
|
|
if ( ( self.damageyaw >= 120 ) || ( self.damageyaw <= -120 ) )// Front quadrant
|
|
return true;
|
|
|
|
if ( ( self.damageyaw >= -45 ) && ( self.damageyaw <= 45 ) )// Back quadrant
|
|
return true;
|
|
|
|
return false;
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
shouldDoStrongBulletDamage( damageWeapon, damageMod, damagetaken, attacker )
|
|
{
|
|
ASSERT( IsDefined( damageWeapon ) );
|
|
|
|
if ( isdefined( self.a.doingLongDeath ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( self.a.pose == "prone" || isdefined( self.a.onback ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( damageWeapon == "none" )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( damagetaken > 500 )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if( damageMod == "MOD_MELEE" )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// if I'm running, and the attacker is far enough away, sometimes let me do
|
|
// a running death instead. this helps minimize repetition of strong damage animations
|
|
// when a line of dudes is running towards you and you're mowing them down, etc.
|
|
if( self.a.movement == "run" && !isAttackerWithinDist( attacker, 275 ) )
|
|
{
|
|
if( RandomInt( 100 ) < 65 )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( isSniperRifle( damageWeapon ) && self.maxHealth < damageTaken )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if( isShotgun( damageWeapon ) && isAttackerWithinDist( attacker, 512 ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if( isDesertEagle( damageWeapon ) && isAttackerWithinDist( attacker, 425 ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if( is_railgun( damageWeapon ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
isDesertEagle( damageWeapon )
|
|
{
|
|
if( damageWeapon == "deserteagle" )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
is_railgun( damageWeapon )
|
|
{
|
|
if( damageWeapon == "ugv_main_turret" || damageWeapon == "ugv_main_turret_player" || damageWeapon == "ugv_main_turret_mp" || damageWeapon == "nx_chinese_lgv_turret" )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
isAttackerWithinDist( attacker, maxDist )
|
|
{
|
|
if( !IsDefined( attacker ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( Distance( self.origin, attacker.origin ) > maxDist )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
getDeathAnim()
|
|
{
|
|
if ( shouldDoStrongBulletDamage( self.damageWeapon, self.damageMod, self.damagetaken, self.attacker ) )
|
|
{
|
|
deathAnim = getStrongBulletDamageDeathAnim();
|
|
|
|
if ( IsDefined( deathAnim ) )
|
|
{
|
|
return deathAnim;
|
|
}
|
|
}
|
|
|
|
if ( isdefined( self.a.onback ) )
|
|
{
|
|
if ( self.a.pose == "crouch" )
|
|
return getBackDeathAnim();
|
|
else
|
|
animscripts\shared::stopOnBack();
|
|
}
|
|
|
|
if ( self.a.pose == "stand" )
|
|
{
|
|
if ( shouldDoRunningForwardDeath() )
|
|
{
|
|
return getRunningForwardDeathAnim();
|
|
}
|
|
else
|
|
{
|
|
return getStandDeathAnim();
|
|
}
|
|
}
|
|
else if ( self.a.pose == "crouch" )
|
|
{
|
|
return getCrouchDeathAnim();
|
|
}
|
|
else if ( self.a.pose == "prone" )
|
|
{
|
|
return getProneDeathAnim();
|
|
}
|
|
}
|
|
|
|
|
|
// may return undefined
|
|
// large death animation for shotguns, snipers etc.
|
|
getStrongBulletDamageDeathAnim()
|
|
{
|
|
damageYaw = abs( self.damageYaw );
|
|
|
|
//TagCC<NOTE>: note, it's using ABS of damageYaw
|
|
//TagCC<NOTE>: Shot from behind?
|
|
if ( damageYaw < 45 )
|
|
return;
|
|
|
|
//TagCC<NOTE>: This probably can all be optimized more by just using logic to decide array name. I'm not sure if it's passing
|
|
//a handle to the array, or the actual array around (my guess is it's the actual array). It's atleast not any worse than before.
|
|
|
|
//TagCC<NOTE>: Front
|
|
if ( damageYaw > 150 )
|
|
{
|
|
if ( damageLocationIsAny( "left_leg_upper", "left_leg_lower", "right_leg_upper", "right_leg_lower", "left_foot", "right_foot" ) )
|
|
{
|
|
deathArray = getDeathAnimByName("strong_leg_front");
|
|
}
|
|
else
|
|
{
|
|
if ( self.damageLocation == "torso_lower" )
|
|
{
|
|
deathArray = getDeathAnimByName("strong_lower_torso_front");
|
|
}
|
|
else
|
|
{
|
|
deathArray = getDeathAnimByName("strong_torso_front");
|
|
}
|
|
}
|
|
}
|
|
else if ( self.damageYaw < 0 ) // LEFT
|
|
{
|
|
deathArray = getDeathAnimByName("strong_left");
|
|
}
|
|
else // RIGHT
|
|
{
|
|
deathArray = getDeathAnimByName("strong_right");
|
|
}
|
|
|
|
return deathArray[ randomint( deathArray.size ) ];
|
|
}
|
|
|
|
getRunningForwardDeathAnim()
|
|
{
|
|
deathArray = getDeathAnimByName("running_forward");
|
|
sanityCheckDeathArray( deathArray );
|
|
|
|
deathArray = animscripts\pain::removeBlockedAnims( deathArray );
|
|
|
|
if ( !deathArray.size )
|
|
return getStandDeathAnim();
|
|
|
|
return deathArray[ randomint( deathArray.size ) ];
|
|
}
|
|
|
|
// remove undefined entries from array
|
|
removeUndefined( array )
|
|
{
|
|
newArray = [];
|
|
for ( index = 0; index < array.size; index++ )
|
|
{
|
|
if ( !isDefined( array[ index ] ) )
|
|
continue;
|
|
|
|
newArray[ newArray.size ] = array[ index ];
|
|
}
|
|
return newArray;
|
|
}
|
|
|
|
getStandPistolDeathAnim()
|
|
{
|
|
deathArray = [];
|
|
|
|
if ( abs( self.damageYaw ) < 50 )
|
|
{
|
|
deathArray[ deathArray.size ] = getDeathAnimByName("stand_pistol_back");// falls forwards
|
|
}
|
|
else
|
|
{
|
|
if ( abs( self.damageYaw ) < 110 )
|
|
deathArray[ deathArray.size ] = getDeathAnimByName("stand_pistol_back");// falls forwards
|
|
|
|
if ( damageLocationIsAny( "torso_lower", "torso_upper", "left_leg_upper", "left_leg_lower", "right_leg_upper", "right_leg_lower" ) )
|
|
{
|
|
deathArray[ deathArray.size ] = getDeathAnimByName("stand_pistol_legs");// hit in groin from front
|
|
if ( !damageLocationIsAny( "torso_upper" ) )
|
|
deathArray[ deathArray.size ] = getDeathAnimByName("stand_pistol_legs");// ( twice as likely )
|
|
}
|
|
|
|
if ( !damageLocationIsAny( "head", "neck", "helmet", "left_foot", "right_foot", "left_hand", "right_hand", "gun" ) && randomint( 2 ) == 0 )
|
|
deathArray[ deathArray.size ] = getDeathAnimByName("stand_pistol_chest");// hit at top and falls backwards, but more dragged out
|
|
|
|
if ( deathArray.size == 0 || damageLocationIsAny( "torso_lower", "torso_upper", "neck", "head", "helmet", "right_arm_upper", "left_arm_upper" ) )
|
|
deathArray[ deathArray.size ] = getDeathAnimByName("stand_pistol_head");// falls backwards
|
|
}
|
|
|
|
return deathArray;
|
|
}
|
|
|
|
getStandDeathAnim()
|
|
{
|
|
deathArray = [];
|
|
extendedDeathArray = [];
|
|
|
|
if ( usingSidearm() )
|
|
{
|
|
deathArray = getStandPistolDeathAnim();
|
|
}
|
|
else
|
|
{
|
|
// torso or legs
|
|
if ( damageLocationIsAny( "torso_lower", "left_leg_upper", "left_leg_lower", "right_leg_lower", "right_leg_lower" ) )
|
|
{
|
|
deathArray = array_combine( deathArray, getDeathAnimByName("stand_legs") );
|
|
extendedDeathArray = array_combine( extendedDeathArray, getDeathAnimByName("stand_legs_extended") );
|
|
}
|
|
|
|
if ( damageLocationIsAny( "head", "helmet" ) )
|
|
{
|
|
deathArray = array_combine( deathArray, getDeathAnimByName("stand_head") );
|
|
}
|
|
|
|
if ( damageLocationIsAny( "neck" ) )
|
|
{
|
|
deathArray = array_combine( deathArray, getDeathAnimByName("stand_neck") );
|
|
}
|
|
|
|
if ( damageLocationIsAny( "left_arm_upper" ) )
|
|
{
|
|
deathArray = array_combine( deathArray, getDeathAnimByName("stand_upper_left") );
|
|
}
|
|
|
|
if ( damageLocationIsAny( "torso_upper" ) )
|
|
{
|
|
deathArray = array_combine( deathArray, getDeathAnimByName("stand_upper_torso") );
|
|
extendedDeathArray = array_combine( extendedDeathArray, getDeathAnimByName("stand_upper_torso_extended") );
|
|
}
|
|
|
|
// quadrants
|
|
if ( ( self.damageyaw > 135 ) || ( self.damageyaw <= -135 ) )// Front quadrant
|
|
{
|
|
if ( damageLocationIsAny( "neck", "head", "helmet" ) )
|
|
{
|
|
deathArray = array_combine( deathArray, getDeathAnimByName("stand_front_head") );
|
|
extendedDeathArray = array_combine( extendedDeathArray, getDeathAnimByName("stand_front_head_extended") );
|
|
}
|
|
|
|
if ( damageLocationIsAny( "torso_upper" ) )
|
|
{
|
|
deathArray = array_combine( deathArray, getDeathAnimByName("stand_front_torso") );
|
|
extendedDeathArray = array_combine( extendedDeathArray, getDeathAnimByName("stand_front_torso_extended") );
|
|
}
|
|
}
|
|
else if ( ( self.damageyaw > -45 ) && ( self.damageyaw <= 45 ) )// Back quadrant
|
|
{
|
|
deathArray = array_combine( deathArray, getDeathAnimByName("stand_back") );
|
|
}
|
|
|
|
foundLocDamageDeath = ( deathArray.size > 0 );
|
|
|
|
if ( !foundLocDamageDeath || RandomInt( 100 ) < 15 )
|
|
{
|
|
deathArray = array_combine( deathArray, getDeathAnimByName("stand_generic") );
|
|
}
|
|
|
|
sanityCheckDeathArray( deathArray );
|
|
|
|
if ( RandomInt( 100 ) < 10 && firingDeathAllowed() )
|
|
{
|
|
standing_file = getDeathAnimByName("stand_firing");
|
|
sanityCheckFireDeathArray( standing_file );
|
|
deathArray = array_combine( deathArray, getDeathAnimByName("stand_firing") );
|
|
}
|
|
|
|
deathArray = removeUndefined( deathArray );
|
|
}
|
|
|
|
assertex( deathArray.size > 0, deathArray.size );
|
|
if ( deathArray.size == 0 )
|
|
deathArray = getDeathAnimByName("stand_exposed");
|
|
|
|
if ( !self.a.disableLongDeath && self.stairsState == "none" && !isdefined( self.a.painOnStairs ) )
|
|
{
|
|
index = randomint( deathArray.size + extendedDeathArray.size );
|
|
if ( index < deathArray.size )
|
|
return deathArray[ index ];
|
|
else
|
|
return extendedDeathArray[ index - deathArray.size ];
|
|
}
|
|
|
|
assertex( deathArray.size > 0, deathArray.size );
|
|
return deathArray[ randomint( deathArray.size ) ];
|
|
}
|
|
|
|
|
|
getCrouchDeathAnim()
|
|
{
|
|
deathArray = [];
|
|
|
|
if ( damageLocationIsAny( "head", "neck" ) ) // Front / Left quadrant
|
|
deathArray[ deathArray.size ] = getDeathAnimByName("crouch_head");
|
|
|
|
if ( damageLocationIsAny( "torso_upper", "torso_lower", "left_arm_upper", "right_arm_upper", "neck" ) )
|
|
deathArray[ deathArray.size ] = getDeathAnimByName("crouch_torso");
|
|
|
|
if ( deathArray.size < 2 )
|
|
deathArray[ deathArray.size ] = getDeathAnimByName("crouch_twist");
|
|
if ( deathArray.size < 2 )
|
|
deathArray[ deathArray.size ] = getDeathAnimByName("crouch_generic");
|
|
|
|
sanityCheckDeathArray( deathArray );
|
|
|
|
assertex( deathArray.size > 0, deathArray.size );
|
|
return deathArray[ randomint( deathArray.size ) ];
|
|
}
|
|
|
|
/* Dan - Commenting out gib.
|
|
get_gib_ref( direction )
|
|
{
|
|
anim.gibDelay = 0;
|
|
|
|
// Dvars for testing gibs.
|
|
//if ( GetDvarInt( "gib_delay" ) > 0 )
|
|
//{
|
|
// anim.gibDelay = GetDvarInt( "gib_delay" );
|
|
//}
|
|
//
|
|
//if ( GetDvar( "gib_test" ) != "" )
|
|
//{
|
|
// self.a.gib_ref = GetDvar( "gib_test" );
|
|
// return;
|
|
//}
|
|
|
|
// If already set, then use it. Useful for canned gib deaths.
|
|
if ( IsDefined( self.a.gib_ref ))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Don't gib if we haven't taken enough damage by the explosive
|
|
// Grenade damage usually range from 160 - 250, so we go above teh minimum
|
|
// so if the splash damage is near it's lowest, don't gib.
|
|
if( self.damageTaken < 165 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( GetTime() > anim.lastGibTime + anim.gibDelay && anim.totalGibs > 0 )
|
|
{
|
|
anim.totalGibs--;
|
|
|
|
anim thread set_last_gib_time();
|
|
|
|
refs = [];
|
|
switch( direction )
|
|
{
|
|
case "right":
|
|
refs[refs.size] = "left_arm";
|
|
refs[refs.size] = "left_leg";
|
|
|
|
gib_ref = get_random( refs );
|
|
break;
|
|
|
|
case "left":
|
|
refs[refs.size] = "right_arm";
|
|
refs[refs.size] = "right_leg";
|
|
|
|
gib_ref = get_random( refs );
|
|
break;
|
|
|
|
case "forward":
|
|
refs[refs.size] = "right_arm";
|
|
refs[refs.size] = "left_arm";
|
|
refs[refs.size] = "right_leg";
|
|
refs[refs.size] = "left_leg";
|
|
//refs[refs.size] = "guts";
|
|
refs[refs.size] = "no_legs";
|
|
|
|
gib_ref = get_random( refs );
|
|
break;
|
|
|
|
case "back":
|
|
refs[refs.size] = "right_arm";
|
|
refs[refs.size] = "left_arm";
|
|
refs[refs.size] = "right_leg";
|
|
refs[refs.size] = "left_leg";
|
|
refs[refs.size] = "no_legs";
|
|
|
|
gib_ref = get_random( refs );
|
|
break;
|
|
|
|
default: // "up"
|
|
refs[refs.size] = "right_arm";
|
|
refs[refs.size] = "left_arm";
|
|
refs[refs.size] = "right_leg";
|
|
refs[refs.size] = "left_leg";
|
|
refs[refs.size] = "no_legs";
|
|
//refs[refs.size] = "guts";
|
|
|
|
gib_ref = get_random( refs );
|
|
break;
|
|
}
|
|
|
|
|
|
self.a.gib_ref = gib_ref;
|
|
}
|
|
else
|
|
{
|
|
self.a.gib_ref = undefined;
|
|
}
|
|
}
|
|
|
|
set_last_gib_time()
|
|
{
|
|
anim notify( "stop_last_gib_time" );
|
|
anim endon( "stop_last_gib_time" );
|
|
|
|
wait( 0.05 );
|
|
anim.lastGibTime = GetTime();
|
|
anim.totalGibs = RandomIntRange( anim.minGibs, anim.maxGibs );
|
|
}
|
|
|
|
get_random( array )
|
|
{
|
|
return array[RandomInt( array.size )];
|
|
}
|
|
|
|
do_gib()
|
|
{
|
|
if( !IsDefined( self.a.gib_ref ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
gib_ref = self.a.gib_ref;
|
|
|
|
limb_data = get_limb_data( gib_ref );
|
|
|
|
if ( !IsDefined( limb_data ))
|
|
{
|
|
println( "^3animscripts\death.gsc - limb_data is not setup for gib_ref on model: " + self.model + " and gib_ref of: " + self.a.gib_ref );
|
|
return;
|
|
}
|
|
|
|
forward = undefined;
|
|
velocity = undefined;
|
|
|
|
pos1 = [];
|
|
pos2 = [];
|
|
velocities = [];
|
|
|
|
if ( gib_ref == "head" )
|
|
{
|
|
self Detach( self.headModel, "" );
|
|
self helmetPop();
|
|
|
|
if ( IsDefined( self.hatModel ) )
|
|
{
|
|
self detach( self.hatModel, "" );
|
|
self.hatModel = undefined;
|
|
}
|
|
}
|
|
|
|
if ( limb_data["spawn_tags"][0] != "" )
|
|
{
|
|
if ( IsDefined( self.gib_vel ) )
|
|
{
|
|
for ( i = 0; i < limb_data["spawn_tags"].size; i++ )
|
|
{
|
|
velocities[i] = self.gib_vel;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( i = 0; i < limb_data["spawn_tags"].size; i++ )
|
|
{
|
|
pos1[pos1.size] = self GetTagOrigin( limb_data["spawn_tags"][i] );
|
|
}
|
|
|
|
wait( 0.05 );
|
|
|
|
for ( i = 0; i < limb_data["spawn_tags"].size; i++ )
|
|
{
|
|
pos2[pos2.size] = self GetTagOrigin( limb_data["spawn_tags"][i] );
|
|
}
|
|
|
|
for ( i = 0; i < pos1.size; i++ )
|
|
{
|
|
forward = VectorNormalize( pos2[i] - pos1[i] );
|
|
velocities[i] = forward * RandomIntRange( 600, 1000 );
|
|
velocities[i] = velocities[i] +( 0, 0, RandomIntRange( 400, 700 ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( IsDefined( limb_data["fx"] ) )
|
|
{
|
|
for ( i = 0; i < limb_data["spawn_tags"].size; i++ )
|
|
{
|
|
if ( limb_data["spawn_tags"][i] == "" )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
PlayFxOnTag( anim._effect[limb_data["fx"]], self, limb_data["spawn_tags"][i] );
|
|
}
|
|
}
|
|
|
|
//tagrRR<TODO>: Play a sound here?
|
|
self thread throw_gib( limb_data["spawn_models"], limb_data["spawn_tags"], velocities );
|
|
|
|
// Set the upperbody model
|
|
self SetModel( limb_data["body_model"] );
|
|
|
|
// Attach the legs
|
|
self Attach( limb_data["legs_model"] );
|
|
}
|
|
|
|
precache_gib_fx()
|
|
{
|
|
anim._effect["animscript_gib_fx"] = LoadFx( "impacts/flesh_hit_head_fatal_exit" );//LoadFx( "weapon/bullet/fx_flesh_gib_fatal_01" );
|
|
anim._effect["animscript_gibtrail_fx"] = LoadFx( "impacts/flesh_hit_head_fatal_exit" );//LoadFx( "trail/fx_trail_blood_streak" );
|
|
|
|
// Not gib; split out into another function before this gets out of hand.
|
|
anim._effect["death_neckgrab_spurt"] = LoadFx( "impacts/flesh_hit_head_fatal_exit" ); //LoadFx( "impacts/fx_flesh_hit_neck_fatal" );
|
|
}
|
|
|
|
get_limb_data( gib_ref )
|
|
{
|
|
temp_array = [];
|
|
|
|
// Slightly faster, store the IsDefined stuff before checking, which will be less code-calls.
|
|
torsoDmg1_defined = IsDefined( self.torsoDmg1 );
|
|
torsoDmg2_defined = IsDefined( self.torsoDmg2 );
|
|
torsoDmg3_defined = IsDefined( self.torsoDmg3 );
|
|
torsoDmg4_defined = IsDefined( self.torsoDmg4 );
|
|
torsoDmg5_defined = IsDefined( self.torsoDmg5 );
|
|
legDmg1_defined = IsDefined( self.legDmg1 );
|
|
legDmg2_defined = IsDefined( self.legDmg2 );
|
|
legDmg3_defined = IsDefined( self.legDmg3 );
|
|
legDmg4_defined = IsDefined( self.legDmg4 );
|
|
|
|
gibSpawn1_defined = IsDefined( self.gibSpawn1 );
|
|
gibSpawn2_defined = IsDefined( self.gibSpawn2 );
|
|
gibSpawn3_defined = IsDefined( self.gibSpawn3 );
|
|
gibSpawn4_defined = IsDefined( self.gibSpawn4 );
|
|
gibSpawn5_defined = IsDefined( self.gibSpawn5 );
|
|
|
|
gibSpawnTag1_defined = IsDefined( self.gibSpawnTag1 );
|
|
gibSpawnTag2_defined = IsDefined( self.gibSpawnTag2 );
|
|
gibSpawnTag3_defined = IsDefined( self.gibSpawnTag3 );
|
|
gibSpawnTag4_defined = IsDefined( self.gibSpawnTag4 );
|
|
gibSpawnTag5_defined = IsDefined( self.gibSpawnTag5 );
|
|
|
|
// Right arm is getting blown off! /////////////////////////////////////////////////////
|
|
if ( torsoDmg2_defined && legDmg1_defined && gibSpawn1_defined && gibSpawnTag1_defined )
|
|
{
|
|
temp_array["right_arm"]["body_model"] = self.torsoDmg2;
|
|
temp_array["right_arm"]["legs_model"] = self.legDmg1;
|
|
temp_array["right_arm"]["spawn_models"][0] = self.gibSpawn1;
|
|
|
|
temp_array["right_arm"]["spawn_tags"][0] = self.gibSpawnTag1;
|
|
temp_array["right_arm"]["fx"] = "animscript_gib_fx";
|
|
}
|
|
|
|
// Left arm is getting blown off! //////////////////////////////////////////////////////
|
|
if ( torsoDmg3_defined && legDmg1_defined && gibSpawn2_defined && gibSpawnTag2_defined )
|
|
{
|
|
temp_array["left_arm"]["body_model"] = self.torsoDmg3;
|
|
temp_array["left_arm"]["legs_model"] = self.legDmg1;
|
|
temp_array["left_arm"]["spawn_models"][0] = self.gibSpawn2;
|
|
|
|
temp_array["left_arm"]["spawn_tags"][0] = self.gibSpawnTag2;
|
|
temp_array["left_arm"]["fx"] = "animscript_gib_fx";
|
|
}
|
|
|
|
// Right leg is getting blown off! ////////////////////////////////////////////////////
|
|
if ( torsoDmg1_defined && legDmg2_defined && gibSpawn3_defined && gibSpawnTag3_defined )
|
|
{
|
|
temp_array["right_leg"]["body_model"] = self.torsoDmg1;
|
|
temp_array["right_leg"]["legs_model"] = self.legDmg2;
|
|
temp_array["right_leg"]["spawn_models"][0] = self.gibSpawn3;
|
|
|
|
temp_array["right_leg"]["spawn_tags"][0] = self.gibSpawnTag3;
|
|
temp_array["right_leg"]["fx"] = "animscript_gib_fx";
|
|
}
|
|
|
|
|
|
// Left leg is getting blown off! /////////////////////////////////////////////////////
|
|
if ( torsoDmg1_defined && legDmg3_defined && gibSpawn4_defined && gibSpawnTag4_defined )
|
|
{
|
|
temp_array["left_leg"]["body_model"] = self.torsoDmg1;
|
|
temp_array["left_leg"]["legs_model"] = self.legDmg3;
|
|
temp_array["left_leg"]["spawn_models"][0] = self.gibSpawn4;
|
|
|
|
temp_array["left_leg"]["spawn_tags"][0] = self.gibSpawnTag4;
|
|
temp_array["left_leg"]["fx"] = "animscript_gib_fx";
|
|
}
|
|
|
|
// No legs! ///////////////////////////////////////////////////////////////////////////
|
|
if ( torsoDmg1_defined && legDmg4_defined && gibSpawn4_defined && gibSpawn3_defined && gibSpawnTag3_defined && gibSpawnTag4_defined )
|
|
{
|
|
temp_array["no_legs"]["body_model"] = self.torsoDmg1;
|
|
temp_array["no_legs"]["legs_model"] = self.legDmg4;
|
|
temp_array["no_legs"]["spawn_models"][0] = self.gibSpawn4;
|
|
temp_array["no_legs"]["spawn_models"][1] = self.gibSpawn3;
|
|
|
|
temp_array["no_legs"]["spawn_tags"][0] = self.gibSpawnTag4;
|
|
temp_array["no_legs"]["spawn_tags"][1] = self.gibSpawnTag3;
|
|
temp_array["no_legs"]["fx"] = "animscript_gib_fx";
|
|
}
|
|
|
|
// Guts! //////////////////////////////////////////////////////////////////////////////
|
|
if ( torsoDmg4_defined && legDmg1_defined )
|
|
{
|
|
temp_array["guts"]["body_model"] = self.torsoDmg4;
|
|
temp_array["guts"]["legs_model"] = self.legDmg1;
|
|
|
|
temp_array["guts"]["spawn_models"][0] = "";
|
|
// temp_array["guts"]["spawn_tags"][0] = "J_SpineLower";
|
|
temp_array["guts"]["spawn_tags"][0] = "";
|
|
temp_array["guts"]["fx"] = "animscript_gib_fx";
|
|
}
|
|
|
|
// Head! //////////////////////////////////////////////////////////////////////////////
|
|
if ( torsoDmg5_defined && legDmg1_defined )
|
|
{
|
|
temp_array["head"]["body_model"] = self.torsoDmg5;
|
|
temp_array["head"]["legs_model"] = self.legDmg1;
|
|
|
|
if( gibSpawn5_defined && gibSpawnTag5_defined )
|
|
{
|
|
temp_array["head"]["spawn_models"][0] = self.gibSpawn5;
|
|
temp_array["head"]["spawn_tags"][0] = self.gibSpawnTag5;
|
|
}
|
|
else
|
|
{
|
|
temp_array["head"]["spawn_models"][0] = "";
|
|
temp_array["head"]["spawn_tags"][0] = "";
|
|
}
|
|
temp_array["head"]["fx"] = "animscript_gib_fx";
|
|
}
|
|
|
|
if ( IsDefined( temp_array[gib_ref] ) )
|
|
{
|
|
return temp_array[gib_ref];
|
|
}
|
|
else
|
|
{
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
throw_gib( spawn_models, spawn_tags, velocities )
|
|
{
|
|
if ( velocities.size < 1 ) // For guts
|
|
{
|
|
return;
|
|
}
|
|
|
|
for ( i = 0; i < spawn_models.size; i++ )
|
|
{
|
|
origin = self GetTagOrigin( spawn_tags[i] );
|
|
angles = self GetTagAngles( spawn_tags[i] );
|
|
CreateDynEntAndLaunch( spawn_models[i], origin, angles, origin, velocities[i], anim._effect["animscript_gibtrail_fx"], 1 );
|
|
|
|
//gib = Spawn( "script_model", self GetTagOrigin( spawn_tags[i] ) );
|
|
//gib.angles = self GetTagAngles( spawn_tags[i] );
|
|
//gib SetModel( spawn_models[i] );
|
|
|
|
//// Play trail fX
|
|
//PlayFxOnTag( anim._effect["animscript_gibtrail_fx"], gib, "tag_fx" );
|
|
|
|
//gib PhysicsLaunch( self.origin, velocities[i] );
|
|
|
|
//gib thread gib_delete();
|
|
}
|
|
}
|
|
|
|
play_bulletgibbed_death_anim()
|
|
{
|
|
maxDist = 300;
|
|
|
|
if ( self.damagemod == "MOD_MELEE" )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Allow script to turn off gibbing.
|
|
if ( IsDefined( self.no_gib ) && ( self.no_gib == 1 ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
gib_chance = 75;
|
|
shotty_gib = false;
|
|
force_gib = IsDefined( self.force_gib ) && self.force_gib;
|
|
if ( WeaponClass( self.damageWeapon ) == "spread" ) // shotgun
|
|
{
|
|
maxDist = 300;
|
|
shotty_gib = true;
|
|
distSquared = DistanceSquared( self.origin, self.attacker.origin );
|
|
if ( distSquared < 110*110 )
|
|
{
|
|
gib_chance = 100;
|
|
}
|
|
else if ( distSquared < 200*200 )
|
|
{
|
|
gib_chance = 75;
|
|
}
|
|
else if ( distSquared < 270*270 )
|
|
{
|
|
gib_chance = 50;
|
|
}
|
|
else if ( distSquared < 330*330 )
|
|
{
|
|
if ( RandomInt( 100 ) < 50 )
|
|
{
|
|
gib_chance = 50;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if ( IsDefined(self.damageWeapon) && self.damageWeapon != "none" && IsSubStr( self.damageWeapon, "dragunov" ) )
|
|
{
|
|
// SUMEET - Adding special case for draganov for some levels with 30% chance
|
|
maxDist = WeaponMaxGibDistance( self.damageWeapon );
|
|
gib_chance = 30;
|
|
}
|
|
else if ( IsDefined(self.damageWeapon) && self.damageWeapon != "none" && WeaponDoGibbing( self.damageWeapon ))
|
|
{
|
|
maxDist = WeaponMaxGibDistance( self.damageWeapon );
|
|
gib_chance = 101;
|
|
}
|
|
else if ( !force_gib )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( force_gib )
|
|
{
|
|
maxDist = 6000;
|
|
gib_chance = 101;
|
|
}
|
|
|
|
if ( !IsDefined( self.attacker ) || !IsDefined( self.damageLocation ))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// shotgun damage is less than 50
|
|
if ( self.damagetaken < 50 && !shotty_gib && !force_gib)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
self.a.gib_ref = undefined;
|
|
|
|
distSquared = DistanceSquared( self.origin, self.attacker.origin );
|
|
|
|
if ( RandomInt( 100 ) < gib_chance
|
|
&& distSquared < maxDist*maxDist
|
|
&& ( force_gib || GetTime() > anim.lastGibTime + anim.gibDelay ))
|
|
{
|
|
anim.lastGibTime = GetTime();
|
|
|
|
refs = [];
|
|
switch( self.damageLocation )
|
|
{
|
|
case "torso_upper":
|
|
case "torso_lower":
|
|
//refs[refs.size] = "guts";
|
|
refs[refs.size] = "right_arm";
|
|
refs[refs.size] = "left_arm";
|
|
break;
|
|
case "right_arm_upper":
|
|
case "right_arm_lower":
|
|
case "right_hand":
|
|
refs[refs.size] = "right_arm";
|
|
break;
|
|
case "left_arm_upper":
|
|
case "left_arm_lower":
|
|
case "left_hand":
|
|
refs[refs.size] = "left_arm";
|
|
break;
|
|
case "right_leg_upper":
|
|
case "right_leg_lower":
|
|
case "right_foot":
|
|
refs[refs.size] = "right_leg";
|
|
refs[refs.size] = "no_legs";
|
|
break;
|
|
case "left_leg_upper":
|
|
case "left_leg_lower":
|
|
case "left_foot":
|
|
refs[refs.size] = "left_leg";
|
|
refs[refs.size] = "no_legs";
|
|
break;
|
|
case "helmet":
|
|
case "head":
|
|
refs[refs.size] = "head";
|
|
break;
|
|
}
|
|
|
|
// Allow script to customize gib parts
|
|
if ( IsDefined( self.custom_gib_refs ))
|
|
{
|
|
refs = self.custom_gib_refs;
|
|
}
|
|
|
|
if ( refs.size )
|
|
{
|
|
self.a.gib_ref = get_random( refs );
|
|
}
|
|
}
|
|
|
|
range = 600;
|
|
nrange = -600;
|
|
self.gib_vel = self.damagedir * RandomIntRange( 500, 900 );
|
|
self.gib_vel += ( RandomIntRange( nrange, range ), RandomIntRange( nrange, range ), RandomIntRange( 400, 1000 ) );
|
|
|
|
if ( try_gib_extended_death( 101 )) //50 ))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
deathAnim = getDeathAnim();
|
|
playDeathAnim( deathAnim );
|
|
|
|
//self setFlaggedAnimKnobAllRestart( "deathanim", deathAnim, %body, 1, .1 );
|
|
//
|
|
//wait 0.05;
|
|
//
|
|
//self launch_ragdoll_based_on_damage_type( 2.0 );
|
|
//self thread death_anim_short_circuit(); // do this just for consistency
|
|
//
|
|
//// wait here so that the client can get the model changes before it becomes an AI_CORPSE
|
|
//wait 0.5;
|
|
|
|
return true;
|
|
}
|
|
|
|
// checks if the gib ref provided is valid one
|
|
isValidGibRef( gib_ref )
|
|
{
|
|
// SUMEET_TODO - make this list global list so that it can be updated/edited easily
|
|
refs = [];
|
|
|
|
refs[refs.size] = "right_arm";
|
|
refs[refs.size] = "left_arm";
|
|
refs[refs.size] = "right_leg";
|
|
refs[refs.size] = "left_leg";
|
|
refs[refs.size] = "no_legs";
|
|
refs[refs.size] = "head";
|
|
|
|
if( is_in_array( refs, gib_ref ) )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
try_gib_extended_death( chance )
|
|
{
|
|
if ( RandomInt( 100 ) >= chance )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( self.a.pose == "prone" || self.a.pose == "back" )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
deathseq = get_gib_extended_death_anims();
|
|
|
|
if ( deathSeq.size == 3 )
|
|
{
|
|
do_extended_death( deathSeq );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
*/
|
|
|
|
do_extended_death( deathSeq )
|
|
{
|
|
self animscripts\shared::DropAllAIWeapons();
|
|
|
|
//self thread do_gib();
|
|
|
|
self thread end_extended_death( deathSeq );
|
|
|
|
numDeathLoops = RandomInt( 2 ) + 1;
|
|
self thread extended_death_loop( deathSeq, numDeathLoops );
|
|
|
|
// We must wait for the sequence to end, or else self will get removed before we're done.
|
|
self waittill( "extended_death_ended" );
|
|
}
|
|
|
|
end_extended_death( deathSeq )
|
|
{
|
|
assert( IsDefined( deathSeq[2] ) );
|
|
|
|
// Normally, the final death anim runs at the end of the loop, but the loop can be intterupted by shooting.
|
|
// Code sends a special notify "damage_afterdeath" if the AI is shot while in extended death
|
|
self waittill_any( "damage_afterdeath", "ending_extended_death" );
|
|
|
|
|
|
self setFlaggedAnimKnobAllRestart( "deathdieanim", deathSeq[2], %body, 1, .1 );
|
|
self animscripts\shared::DoNoteTracks( "deathdieanim" );
|
|
|
|
// All done with extended death sequence.
|
|
self notify( "extended_death_ended" );
|
|
}
|
|
|
|
extended_death_loop( deathSeq, numLoops )
|
|
{
|
|
// If someone shoots or damages self in any way, play final death immediately.
|
|
self endon( "damage" );
|
|
|
|
assert( IsDefined( deathSeq[1] ) );
|
|
|
|
animLength = GetAnimLength( deathSeq[1] );
|
|
for ( i = 0; i < numLoops; i++ )
|
|
{
|
|
self setFlaggedAnimKnobAllRestart( "deathloopanim", deathSeq[1], %body, 1, .1 );
|
|
self animscripts\shared::DoNoteTracks( "deathloopanim" );
|
|
}
|
|
|
|
// If the loop hasn't already been cut short by the actor taking further damage,
|
|
// go into the final death anim.
|
|
self notify( "ending_extended_death" );
|
|
}
|
|
|
|
get_gib_extended_death_anims()
|
|
{
|
|
hitfrom = undefined;
|
|
|
|
if (( self.damageyaw > 90 ) || ( self.damageyaw <= -90 ))
|
|
{
|
|
hitfrom = "front";
|
|
}
|
|
else
|
|
{
|
|
hitfrom = "back";
|
|
}
|
|
|
|
gib_ref = self.a.gib_ref;
|
|
|
|
deathSeq = [];
|
|
if ( IsDefined( hitfrom ) && IsDefined( gib_ref ) && gib_ref != "head" )
|
|
{
|
|
hitIndex = 0;
|
|
loopIndex = 1;
|
|
dieIndex = 2;
|
|
|
|
if ( gib_ref == "guts" || gib_ref == "no_legs" ) // don't have directional anims
|
|
{
|
|
hitfrom = "";
|
|
}
|
|
else
|
|
{
|
|
hitfrom = "_" + hitfrom;
|
|
}
|
|
|
|
// TEMP
|
|
deathSeq[hitIndex] = anim.animsets.gibAnimSet["gib_shoulder_twist"];
|
|
deathSeq[loopIndex] = anim.animsets.gibAnimSet["gib_shoulder_spin"];
|
|
deathSeq[dieIndex] = anim.animsets.gibAnimSet["gib_shoulder_back"];
|
|
//deathSeq[hitIndex] = animArray("gib_" + gib_ref + hitfrom + "_start");
|
|
//deathSeq[loopIndex] = animArray("gib_" + gib_ref + hitfrom + "_loop");
|
|
//deathSeq[dieIndex] = animArray("gib_" + gib_ref + hitfrom + "_end");
|
|
}
|
|
|
|
return deathSeq;
|
|
}
|
|
|
|
getProneDeathAnim()
|
|
{
|
|
if ( isdefined( self.a.proneAiming ) )
|
|
return getDeathAnimByName("prone_aiming");
|
|
else
|
|
return getDeathAnimByName("prone");
|
|
}
|
|
|
|
getBackDeathAnim()
|
|
{
|
|
deathArray = getDeathAnimByName("back");
|
|
return deathArray[ randomint( deathArray.size ) ];
|
|
}
|
|
|
|
firingDeathAllowed()
|
|
{
|
|
if ( !isdefined( self.weapon ) || !usingRifleLikeWeapon() || !weaponIsAuto( self.weapon ) || self.dieQuietly )
|
|
return false;
|
|
|
|
if ( self.a.weaponPos[ "right" ] == "none" )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
tryAddDeathAnim( animName )
|
|
{
|
|
assert( !animHasNoteTrack( animName, "fire" ) && !animHasNoteTrack( animName, "fire_spray" ) );
|
|
return animName;
|
|
}
|
|
|
|
sanityCheckDeathArray( array )
|
|
{
|
|
for( i = 0; i < array.size; i = i+1 )
|
|
{
|
|
assert( !animHasNoteTrack( array[i], "fire" ) && !animHasNoteTrack( array[i], "fire_spray" ) );
|
|
}
|
|
}
|
|
|
|
sanityCheckFireDeathArray( array )
|
|
{
|
|
for( i = 0; i < array.size; i = i+1 )
|
|
{
|
|
assert( animHasNoteTrack( array[i], "fire" ) || animHasNoteTrack( array[i], "fire_spray" ) );
|
|
}
|
|
}
|
|
|
|
getDeathAnimByName( name )
|
|
{
|
|
assert( IsDefined( name ) );
|
|
assert( IsDefined( anim.animsets.deathAnimSet[name] ) );
|
|
|
|
if( IsDefined( self.customDeathAnimSet ) && IsDefined( self.customDeathAnimSet[name] ) )
|
|
{
|
|
return self.customDeathAnimSet[name];
|
|
}
|
|
else
|
|
{
|
|
return anim.animsets.deathAnimSet[name];
|
|
}
|
|
}
|
|
|
|
tryAddFiringDeathAnim( animName )
|
|
{
|
|
assert( animHasNoteTrack( animName, "fire" ) || animHasNoteTrack( animName, "fire_spray" ) );
|
|
return animName;
|
|
}
|
|
|
|
playExplodeDeathAnim()
|
|
{
|
|
if ( isdefined( self.juggernaut ) || isdefined( self.ares ) )
|
|
return false;
|
|
|
|
if ( self.damageLocation != "none" && self.damageMod != "MOD_GRENADE" )
|
|
{
|
|
if( !is_railgun( self.damageWeapon ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
deathArray = [];
|
|
|
|
if ( self.a.movement != "run" )
|
|
{
|
|
if ( ( self.damageyaw > 135 ) || ( self.damageyaw <= -135 ) ) // Front quadrant
|
|
{
|
|
deathArray = getDeathAnimByName("explode_stand_front");
|
|
//get_gib_ref( "back" );
|
|
}
|
|
else if ( ( self.damageyaw > 45 ) && ( self.damageyaw <= 135 ) ) // Right quadrant
|
|
{
|
|
deathArray = getDeathAnimByName("explode_stand_right");
|
|
//get_gib_ref( "left" );
|
|
}
|
|
else if ( ( self.damageyaw > - 45 ) && ( self.damageyaw <= 45 ) ) // Back quadrant
|
|
{
|
|
deathArray = getDeathAnimByName("explode_stand_back");
|
|
//get_gib_ref( "forward" );
|
|
}
|
|
else
|
|
{ // Left quadrant
|
|
deathArray = getDeathAnimByName("explode_stand_left");
|
|
//get_gib_ref( "right" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ( self.damageyaw > 135 ) || ( self.damageyaw <= -135 ) ) // Front quadrant
|
|
{
|
|
deathArray = getDeathAnimByName("explode_run_front");
|
|
//get_gib_ref( "back" );
|
|
}
|
|
else if ( ( self.damageyaw > 45 ) && ( self.damageyaw <= 135 ) ) // Right quadrant
|
|
{
|
|
deathArray = getDeathAnimByName("explode_run_right");
|
|
//get_gib_ref( "left" );
|
|
}
|
|
else if ( ( self.damageyaw > - 45 ) && ( self.damageyaw <= 45 ) ) // Back quadrant
|
|
{
|
|
deathArray = getDeathAnimByName("explode_run_back");
|
|
//get_gib_ref( "forward" );
|
|
}
|
|
else
|
|
{ // Left quadrant
|
|
deathArray = getDeathAnimByName("explode_run_left");
|
|
//get_gib_ref( "right" );
|
|
}
|
|
}
|
|
|
|
//gib_chance = 50;
|
|
|
|
deathAnim = deathArray[ randomint( deathArray.size ) ];
|
|
|
|
if ( getdvar( "scr_expDeathMayMoveCheck", "on" ) == "on" )
|
|
{
|
|
ragdoll_point_array = GetNotetrackTimes( deathAnim, "start_ragdoll" );
|
|
if ( ragdoll_point_array.size > 0 )
|
|
{
|
|
ragdoll_point = ragdoll_point_array[ 0 ];
|
|
}
|
|
else
|
|
{
|
|
ragdoll_point = 1;
|
|
}
|
|
|
|
localDeltaVector = getMoveDelta( deathAnim, 0, ragdoll_point );
|
|
endPoint = self localToWorldCoords( localDeltaVector );
|
|
|
|
// Draw trajectory.
|
|
// line( self.origin, endPoint, ( 1, 0, 0 ), 1, 0, 5000 );
|
|
|
|
if ( !self mayMoveToPoint( endPoint, false ) )
|
|
{
|
|
//if( try_gib_extended_death( gib_chance ) )
|
|
//{
|
|
// return true;
|
|
//}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// this should really be in the notetracks
|
|
self animMode( "nogravity" );
|
|
|
|
//if( try_gib_extended_death( gib_chance ) )
|
|
//{
|
|
// return true;
|
|
//}
|
|
|
|
playDeathAnim( deathAnim );
|
|
return true;
|
|
}
|
|
|