957 lines
25 KiB
Plaintext
957 lines
25 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 -- ;
|
|
|
|
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;
|
|
}
|
|
|
|
explosiveDamage = self animscripts\pain::wasDamagedByExplosive();
|
|
|
|
if ( self.damageLocation == "helmet" || self.damageLocation == "head" )
|
|
self helmetPop();
|
|
else if ( explosiveDamage && randomint( 3 ) == 0 )
|
|
self helmetPop();
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
damageTaken = self.damagetaken;
|
|
if ( damageType == "bullet" )
|
|
damageTaken = max( damageTaken, 300 );
|
|
|
|
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.05 );
|
|
}
|
|
|
|
playDeathAnim( deathAnim )
|
|
{
|
|
if ( !animHasNoteTrack( deathAnim, "dropgun" ) && !animHasNoteTrack( deathAnim, "fire_spray" ) )// && !animHasNotetrack( deathAnim, "gun keep" )
|
|
self animscripts\shared::DropAllAIWeapons();
|
|
|
|
//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." );
|
|
|
|
if( !isdefined( 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 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 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;
|
|
}
|
|
|
|
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[ "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" )
|
|
{
|
|
deathArray = [];
|
|
deathArray[ 0 ] = %corner_standr_deathA;
|
|
deathArray[ 1 ] = %corner_standr_deathB;
|
|
DoDeathFromArray( deathArray );
|
|
}
|
|
else
|
|
{
|
|
deathArray = [];
|
|
|
|
if ( damageLocationIsAny( "head", "neck" ) )
|
|
{
|
|
deathArray[ 0 ] = %CornerCrR_alert_death_slideout;
|
|
}
|
|
else
|
|
{
|
|
deathArray[ 0 ] = %CornerCrR_alert_death_slideout;
|
|
deathArray[ 1 ] = %CornerCrR_alert_death_back;
|
|
}
|
|
DoDeathFromArray( deathArray );
|
|
}
|
|
return true;
|
|
|
|
case "cover_left":
|
|
if ( self.a.pose == "stand" )
|
|
{
|
|
deathArray = [];
|
|
deathArray[ 0 ] = %corner_standl_deathA;
|
|
deathArray[ 1 ] = %corner_standl_deathB;
|
|
DoDeathFromArray( deathArray );
|
|
}
|
|
else
|
|
{
|
|
deathArray = [];
|
|
deathArray[ 0 ] = %CornerCrL_death_side;
|
|
deathArray[ 1 ] = %CornerCrL_death_back;
|
|
DoDeathFromArray( deathArray );
|
|
}
|
|
return true;
|
|
|
|
case "cover_stand":
|
|
deathArray = [];
|
|
deathArray[ 0 ] = %coverstand_death_left;
|
|
deathArray[ 1 ] = %coverstand_death_right;
|
|
DoDeathFromArray( deathArray );
|
|
return true;
|
|
|
|
case "cover_crouch":
|
|
deathArray = [];
|
|
if ( damageLocationIsAny( "head", "neck" ) && ( self.damageyaw > 135 || self.damageyaw <= -45 ) ) // Front / Left quadrant
|
|
deathArray[ deathArray.size ] = %covercrouch_death_1;
|
|
|
|
if ( ( self.damageyaw > - 45 ) && ( self.damageyaw <= 45 ) ) // Back quadrant
|
|
deathArray[ deathArray.size ] = %covercrouch_death_3;
|
|
|
|
deathArray[ deathArray.size ] = %covercrouch_death_2;
|
|
|
|
DoDeathFromArray( deathArray );
|
|
return true;
|
|
|
|
case "saw":
|
|
if ( self.a.pose == "stand" )
|
|
DoDeathFromArray( array( %saw_gunner_death ) );
|
|
else if ( self.a.pose == "crouch" )
|
|
DoDeathFromArray( array( %saw_gunner_lowwall_death ) );
|
|
else
|
|
DoDeathFromArray( array( %saw_gunner_prone_death ) );
|
|
return true;
|
|
|
|
case "dying_crawl":
|
|
if ( isdefined( self.a.onback ) && self.a.pose == "crouch" )
|
|
{
|
|
deathArray = array( %dying_back_death_v2, %dying_back_death_v3, %dying_back_death_v4 );
|
|
DoDeathFromArray( deathArray );
|
|
}
|
|
else
|
|
{
|
|
assertex( self.a.pose == "prone", self.a.pose );
|
|
deathArray = array( %dying_crawl_death_v1, %dying_crawl_death_v2 );
|
|
DoDeathFromArray( deathArray );
|
|
}
|
|
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()
|
|
{
|
|
// if (self.team == "allies")
|
|
// self playsound("allied_death");
|
|
// else
|
|
// self playsound("german_death");
|
|
self animscripts\face::SayGenericDialogue( "death" );
|
|
}
|
|
|
|
print3dfortime( place, text, time )
|
|
{
|
|
numframes = time * 20;
|
|
for ( i = 0; i < numframes; i++ )
|
|
{
|
|
print3d( place, text );
|
|
wait .05;
|
|
}
|
|
}
|
|
|
|
helmetPop()
|
|
{
|
|
if ( !isdefined( self ) )
|
|
return;
|
|
if ( !isdefined( self.hatModel ) )
|
|
return;
|
|
// used to check self removableHat() in cod2... probably not necessary though
|
|
|
|
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;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
isDesertEagle( damageWeapon )
|
|
{
|
|
if( damageWeapon == "deserteagle" )
|
|
{
|
|
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 );
|
|
|
|
if ( damageYaw < 45 )
|
|
return;
|
|
|
|
if ( damageYaw > 150 )
|
|
{
|
|
if ( damageLocationIsAny( "left_leg_upper", "left_leg_lower", "right_leg_upper", "right_leg_lower", "left_foot", "right_foot" ) )
|
|
{
|
|
deathArray = array( %death_shotgun_legs, %death_stand_sniper_leg );
|
|
}
|
|
else
|
|
{
|
|
deathArray = [];
|
|
|
|
if ( self.damageLocation == "torso_lower" )
|
|
{
|
|
deathArray[ deathArray.size ] = %death_shotgun_legs;
|
|
deathArray[ deathArray.size ] = %death_stand_sniper_leg;
|
|
}
|
|
|
|
deathArray[ deathArray.size ] = %death_shotgun_back_v1;
|
|
deathArray[ deathArray.size ] = %exposed_death_blowback;
|
|
deathArray[ deathArray.size ] = %death_stand_sniper_chest1;
|
|
deathArray[ deathArray.size ] = %death_stand_sniper_chest2;
|
|
deathArray[ deathArray.size ] = %death_stand_sniper_spin1;
|
|
}
|
|
|
|
//self.faceDamageDir = true;
|
|
}
|
|
else if ( self.damageYaw < 0 )
|
|
{
|
|
deathArray = array( %death_shotgun_spinL, %death_stand_sniper_spin1, %death_stand_sniper_chest1, %death_stand_sniper_chest2 );
|
|
}
|
|
else
|
|
{
|
|
deathArray = array( %death_shotgun_spinR, %death_stand_sniper_spin2, %death_stand_sniper_chest1, %death_stand_sniper_chest2 );
|
|
}
|
|
|
|
return deathArray[ randomint( deathArray.size ) ];
|
|
}
|
|
|
|
getRunningForwardDeathAnim()
|
|
{
|
|
deathArray = [];
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %run_death_facedown );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %run_death_roll );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %run_death_fallonback );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %run_death_flop );
|
|
|
|
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 ] = %pistol_death_2;// falls forwards
|
|
}
|
|
else
|
|
{
|
|
if ( abs( self.damageYaw ) < 110 )
|
|
deathArray[ deathArray.size ] = %pistol_death_2;// falls forwards
|
|
|
|
if ( damageLocationIsAny( "torso_lower", "torso_upper", "left_leg_upper", "left_leg_lower", "right_leg_upper", "right_leg_lower" ) )
|
|
{
|
|
deathArray[ deathArray.size ] = %pistol_death_3;// hit in groin from front
|
|
if ( !damageLocationIsAny( "torso_upper" ) )
|
|
deathArray[ deathArray.size ] = %pistol_death_3;// ( twice as likely )
|
|
}
|
|
|
|
if ( !damageLocationIsAny( "head", "neck", "helmet", "left_foot", "right_foot", "left_hand", "right_hand", "gun" ) && randomint( 2 ) == 0 )
|
|
deathArray[ deathArray.size ] = %pistol_death_4;// 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 ] = %pistol_death_1;// 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[ deathArray.size ] = tryAddDeathAnim( %exposed_death_groin );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %stand_death_leg );
|
|
extendedDeathArray[ extendedDeathArray.size ] = tryAddDeathAnim( %stand_death_crotch );
|
|
extendedDeathArray[ extendedDeathArray.size ] = tryAddDeathAnim( %stand_death_guts );
|
|
}
|
|
|
|
if ( damageLocationIsAny( "head", "helmet" ) )
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %exposed_death_headshot );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %exposed_death_flop );
|
|
}
|
|
|
|
if ( damageLocationIsAny( "neck" ) )
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %exposed_death_neckgrab );
|
|
}
|
|
|
|
if ( damageLocationIsAny( "torso_upper", "left_arm_upper" ) )
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %exposed_death_twist );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %stand_death_shoulder_spin );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %stand_death_shoulderback );
|
|
}
|
|
|
|
if ( damageLocationIsAny( "torso_upper" ) )
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %stand_death_tumbleforward );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %stand_death_stumbleforward );
|
|
extendedDeathArray[ extendedDeathArray.size ] = tryAddDeathAnim( %stand_death_fallside );
|
|
}
|
|
|
|
// quadrants
|
|
if ( ( self.damageyaw > 135 ) || ( self.damageyaw <= -135 ) )// Front quadrant
|
|
{
|
|
if ( damageLocationIsAny( "neck", "head", "helmet" ) )
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %stand_death_face );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %stand_death_headshot_slowfall );
|
|
extendedDeathArray[ extendedDeathArray.size ] = tryAddDeathAnim( %stand_death_head_straight_back );
|
|
}
|
|
|
|
if ( damageLocationIsAny( "torso_upper" ) )
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %stand_death_tumbleback );
|
|
extendedDeathArray[ extendedDeathArray.size ] = tryAddDeathAnim( %stand_death_chest_stunned );
|
|
}
|
|
}
|
|
//else if ( ( self.damageyaw > 45 ) && ( self.damageyaw <= 135 ) )// Right quadrant
|
|
//{
|
|
//}
|
|
else if ( ( self.damageyaw > -45 ) && ( self.damageyaw <= 45 ) )// Back quadrant
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %exposed_death_falltoknees );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %exposed_death_falltoknees_02 );
|
|
}
|
|
//else// Left quadrant
|
|
//{
|
|
//}
|
|
|
|
foundLocDamageDeath = ( deathArray.size > 0 );
|
|
|
|
if ( !foundLocDamageDeath || RandomInt( 100 ) < 15 )
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %exposed_death_02 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %exposed_death_nerve );
|
|
}
|
|
|
|
if ( RandomInt( 100 ) < 10 && firingDeathAllowed() )
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddFiringDeathAnim( %exposed_death_firing_02 );
|
|
deathArray[ deathArray.size ] = tryAddFiringDeathAnim( %exposed_death_firing );
|
|
deathArray = removeUndefined( deathArray );
|
|
}
|
|
|
|
}
|
|
|
|
assertex( deathArray.size > 0, deathArray.size );
|
|
if ( deathArray.size == 0 )
|
|
deathArray[ deathArray.size ] = %exposed_death;
|
|
|
|
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 ] = tryAddDeathAnim( %exposed_crouch_death_fetal );
|
|
|
|
if ( damageLocationIsAny( "torso_upper", "torso_lower", "left_arm_upper", "right_arm_upper", "neck" ) )
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %exposed_crouch_death_flip );
|
|
|
|
if ( deathArray.size < 2 )
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %exposed_crouch_death_twist );
|
|
if ( deathArray.size < 2 )
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %exposed_crouch_death_flip );
|
|
|
|
assertex( deathArray.size > 0, deathArray.size );
|
|
return deathArray[ randomint( deathArray.size ) ];
|
|
}
|
|
|
|
|
|
getProneDeathAnim()
|
|
{
|
|
if ( isdefined( self.a.proneAiming ) )
|
|
return %prone_death_quickdeath;
|
|
else
|
|
return %dying_crawl_death_v1;
|
|
}
|
|
|
|
getBackDeathAnim()
|
|
{
|
|
deathArray = array( %dying_back_death_v1, %dying_back_death_v2, %dying_back_death_v3, %dying_back_death_v4 );
|
|
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;
|
|
}
|
|
|
|
tryAddFiringDeathAnim( animName )
|
|
{
|
|
assert( animHasNoteTrack( animName, "fire" ) || animHasNoteTrack( animName, "fire_spray" ) );
|
|
return animName;
|
|
}
|
|
|
|
playExplodeDeathAnim()
|
|
{
|
|
if ( isdefined( self.juggernaut ) )
|
|
return false;
|
|
|
|
if ( self.damageLocation != "none" )
|
|
return false;
|
|
|
|
deathArray = [];
|
|
|
|
if ( self.a.movement != "run" )
|
|
{
|
|
if ( ( self.damageyaw > 135 ) || ( self.damageyaw <= -135 ) ) // Front quadrant
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_stand_B_v1 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_stand_B_v2 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_stand_B_v3 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_stand_B_v4 );
|
|
}
|
|
else if ( ( self.damageyaw > 45 ) && ( self.damageyaw <= 135 ) ) // Right quadrant
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_stand_L_v1 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_stand_L_v2 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_stand_L_v3 );
|
|
}
|
|
else if ( ( self.damageyaw > - 45 ) && ( self.damageyaw <= 45 ) ) // Back quadrant
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_stand_F_v1 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_stand_F_v2 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_stand_F_v3 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_stand_F_v4 );
|
|
}
|
|
else
|
|
{ // Left quadrant
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_stand_R_v1 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_stand_R_v2 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ( self.damageyaw > 135 ) || ( self.damageyaw <= -135 ) ) // Front quadrant
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_run_B_v1 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_run_B_v2 );
|
|
}
|
|
else if ( ( self.damageyaw > 45 ) && ( self.damageyaw <= 135 ) ) // Right quadrant
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_run_L_v1 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_run_L_v2 );
|
|
}
|
|
else if ( ( self.damageyaw > - 45 ) && ( self.damageyaw <= 45 ) ) // Back quadrant
|
|
{
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_run_F_v1 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_run_F_v2 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_run_F_v3 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_run_F_v4 );
|
|
}
|
|
else
|
|
{ // Left quadrant
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_run_R_v1 );
|
|
deathArray[ deathArray.size ] = tryAddDeathAnim( %death_explosion_run_R_v2 );
|
|
}
|
|
}
|
|
|
|
deathAnim = deathArray[ randomint( deathArray.size ) ];
|
|
|
|
if ( getdvar( "scr_expDeathMayMoveCheck", "on" ) == "on" )
|
|
{
|
|
localDeltaVector = getMoveDelta( deathAnim, 0, 1 );
|
|
endPoint = self localToWorldCoords( localDeltaVector );
|
|
|
|
if ( !self mayMoveToPoint( endPoint, false ) )
|
|
return false;
|
|
}
|
|
|
|
// this should really be in the notetracks
|
|
self animMode( "nogravity" );
|
|
|
|
playDeathAnim( deathAnim );
|
|
return true;
|
|
}
|
|
|