1
0
KF2-Dev-Scripts/KFGame/Classes/KFSM_DeathAnim.uc

278 lines
9.4 KiB
Ucode
Raw Normal View History

2020-12-13 15:01:13 +00:00
//=============================================================================
// KFSM_DeathAnim
//=============================================================================
// Plays a partial ragdoll anim after TearOff & PlayDying (Client-side only)
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
// - Andrew "Strago" Ladenberger
//=============================================================================
class KFSM_DeathAnim extends KFSpecialMove;
/** Chaining death anims */
var float NextDeathAnim_ActorTime;
var byte NumChainedDeathAnims;
var byte MaxChainedDeathAnims;
/** motors flag to make sure it always get disabled */
var bool bHasDeathMotorsActive;
static function byte PackSMFlags(KFPawn P, vector HitDir)
{
// @todo
return 0;
}
/** Restrictions for doing Death Animation. */
protected function bool InternalCanDoSpecialMove()
{
if( PawnOwner.Physics == PHYS_RigidBody && KFPOwner != None
&& KFPOwner.PawnAnimInfo.bCanPlayDeathAnimations )
{
return TRUE;
}
return FALSE;
}
/**
* Play a death animation followed by rag doll physics.
* Return TRUE if successful, FALSE if couldn't find an animation and wants to go into ragdoll.
*/
function bool PlayDeathAnimation(class<DamageType> DamageType, vector HitDirection, optional name HitBoneName)
{
local name DeathAnim;
local float Duration;
DeathAnim = KFPOwner.PawnAnimInfo.ChooseDeathAnimation(KFPOwner, DamageType, HitDirection, HitBoneName);
if( DeathAnim != '' )
{
// Play one shot death animation
Duration = PlaySpecialMoveAnim(DeathAnim, EAS_FullBody, 0.2, -1.f);
}
// If animation failed to play, end special move now
if ( Duration <= 0.f )
{
// Do rag doll death instead.
KFPOwner.EndSpecialMove();
return FALSE;
}
// TRUE = blend to physics/motors. FALSE = plain animation, no physics used. Used for debugging.
if( TRUE )
{
StartDeathAnimMotors();
}
else
{
KFPOwner.EndSpecialMove();
}
`log("PlayDeathAnimation success DamageType:"$DamageType@"BoneName:"$HitBoneName, KFPOwner.bLogCustomAnim);
return TRUE;
}
/** Calculate direction and pick death animation from anim group list */
function bool GetDeathAnimation(class<DamageType> DamageType, vector HitDirection, optional name HitBoneName)
{
// CalcOctagonRegion()
// Check HitBoneName
// Check High impact
}
/**
* Notification called when body stance animation finished playing.
* @param SeqNode - Node that finished playing. You can get to the SkeletalMeshComponent by looking at SeqNode->SkelComponent
* @param PlayedTime - Time played on this animation. (play rate independant).
* @param ExcessTime - how much time overlapped beyond end of animation. (play rate independant).
*/
function AnimEndNotify(AnimNodeSequence SeqNode, float PlayedTime, float ExcessTime)
{
StopDeathAnimMotors();
KFPOwner.EndSpecialMove();
}
/** Event called when Special Move is finished. */
function SpecialMoveEnded( Name PrevMove, Name NextMove )
{
// If move was ended by something else (aka AbortSpecialMove), cleanup motors
if ( bHasDeathMotorsActive )
{
StopDeathAnimMotors();
}
KFPOwner.StopAllAnimations();
`log("DeathAnimation finished ChainCount:"$NumChainedDeathAnims, KFPOwner.bLogCustomAnim);
}
/*********************************************************************************************
* Internal
*********************************************************************************************/
function StartDeathAnimMotors()
{
// Turn on bone springs
//SetSpringForBone(PawnOwner.PelvisBoneName, TRUE);
//SetSpringForBone(PawnOwner.RightHandBoneName, TRUE);
//SetSpringForBone(PawnOwner.LeftHandBoneName, TRUE);
//SetSpringForBone(PawnOwner.LeftFootBoneName, TRUE);
//SetSpringForBone(PawnOwner.RightFootBoneName, TRUE);
// Disable animation tree SkelControls
PawnOwner.Mesh.bIgnoreControllers = 1;
// Set physics to be updated from Kinematic data
PawnOwner.Mesh.bUpdateJointsFromAnimation = TRUE;
PawnOwner.Mesh.bUpdateKinematicBonesFromAnimation = TRUE;
// Turn on motors
if ( PawnOwner.Mesh.PhysicsAssetInstance != None )
{
//PawnOwner.Mesh.PhysicsAssetInstance.SetAllMotorsAngularDriveParams(5000.f, 250.f, 0.f, PawnOwner.Mesh, TRUE);
PawnOwner.Mesh.PhysicsAssetInstance.SetAllMotorsAngularDriveParams(KFPOwner.PawnAnimInfo.DeathPhysMotorStrength.X, KFPOwner.PawnAnimInfo.DeathPhysMotorStrength.Y, 0.f, PawnOwner.Mesh, TRUE);
PawnOwner.Mesh.PhysicsAssetInstance.SetAllMotorsAngularPositionDrive(TRUE, TRUE, PawnOwner.Mesh, TRUE);
}
// If Pawn had broken constraints, then we need to make sure child bodies and joints are not turned back into kinematic state
if ( KFPOwner.bHasBrokenConstraints )
{
PawnOwner.Mesh.UpdateMeshForBrokenConstraints();
}
bHasDeathMotorsActive = true;
}
function StopDeathAnimMotors()
{
// Turn off bone springs
//SetSpringForBone(PawnOwner.PelvisBoneName, FALSE);
//SetSpringForBone(PawnOwner.RightHandBoneName, FALSE);
//SetSpringForBone(PawnOwner.LeftHandBoneName, FALSE);
//SetSpringForBone(PawnOwner.LeftFootBoneName, FALSE);
//SetSpringForBone(PawnOwner.RightFootBoneName, FALSE);
// Kinematic data can now be ignored (we're not using velocity motors)
PawnOwner.Mesh.bUpdateJointsFromAnimation = FALSE;
PawnOwner.Mesh.bUpdateKinematicBonesFromAnimation = FALSE;
if (PawnOwner.Mesh.PhysicsAssetInstance != None)
{
// Turn off position motors
PawnOwner.Mesh.PhysicsAssetInstance.SetAllMotorsAngularPositionDrive(FALSE, FALSE, PawnOwner.Mesh, TRUE);
// Turn on velocity motors, to add some joint friction
//PawnOwner.Mesh.PhysicsAssetInstance.SetAllMotorsAngularDriveParams(0.f, 0.f, 0.02f, PawnOwner.Mesh, TRUE);
//PawnOwner.Mesh.PhysicsAssetInstance.SetAllMotorsAngularVelocityDrive(TRUE, TRUE, PawnOwner.Mesh, TRUE);
}
bHasDeathMotorsActive = false;
}
/**
* Creates a spring between physics representation of bone and animated position.
* To have physics match animation.
*/
final function SetSpringForBone(Name InBoneName, bool bEnable)
{
local int BoneIndex;
local matrix BoneMatrix;
local RB_BodyInstance BoneBody;
// Filter through sockets and bones. Make sure we have a valid bone name to use.
InBoneName = PawnOwner.Mesh.GetSocketBoneName(InBoneName);
if( InBoneName == '' )
{
`Warn(PawnOwner.WorldInfo.TimeSeconds @ PawnOwner @ class @ GetFuncName() @ "Has non existing Bone or Socket named:" @ InBoneName @ "Unable to create HipBodyInstance!");
return;
}
BoneIndex = PawnOwner.Mesh.MatchRefBone(InBoneName);
if( BoneIndex != INDEX_NONE )
{
BoneMatrix = PawnOwner.Mesh.GetBoneMatrix(BoneIndex);
BoneBody = PawnOwner.Mesh.FindBodyInstanceNamed(InBoneName);
if( BoneBody != None )
{
BoneBody.EnableBoneSpring(bEnable, bEnable, BoneMatrix);
if( bEnable )
{
BoneBody.OverextensionThreshold = 50.f;
BoneBody.SetBoneSpringParams(10000.f, 500.f, 10000.f, 500.f);
BoneBody.bDisableOnOverExtension = TRUE;
}
}
else
{
`Warn(PawnOwner.WorldInfo.TimeSeconds @ PawnOwner @ class @ GetFuncName() @ "BodyInstance not found for BoneName:" @ InBoneName);
ScriptTrace();
}
}
else
{
`Warn(PawnOwner.WorldInfo.TimeSeconds @ PawnOwner @ class @ GetFuncName() @ "Has non existing Index for BoneName:" @ InBoneName @ "Unable to create BodyInstance!");
}
}
/** Notification forwarded from RB_BodyInstance, when a spring is over extended and disabled. */
function OnRigidBodySpringOverextension(RB_BodyInstance BodyInstance)
{
local Name PelvisBoneName;
local RB_BodyInstance PelvisBodyInstance;
// Make sure we have correct bone name if this is a socket.
PelvisBoneName = PawnOwner.Mesh.GetSocketBoneName(KFPOwner.PelvisBoneName);
PelvisBodyInstance = PawnOwner.Mesh.FindBodyInstanceNamed(PelvisBoneName);
// If Pelvis spring was broken, we can't keep the other limbs (hands & legs) under spring influence, it would look too bad.
// So we just turn them off and force the Pawn into rag doll.
if( PelvisBodyInstance == BodyInstance )
{
StopDeathAnimMotors();
}
}
/*********************************************************************************************
* Death Anim Chanining
*********************************************************************************************/
/** Handle death animation chaining */
function NotifyOwnerTakeHit(class<KFDamageType> DamageType, vector HitLoc, vector HitDir, Controller InstigatedBy)
{
local TraceHitInfo HitInfo;
local name DeathAnim;
// Since the pawn is torn off, the chaining effect will only play for locally controlled players
if ( NumChainedDeathAnims < MaxChainedDeathAnims && PawnOwner.ActorTimeSince(NextDeathAnim_ActorTime) > 0 )
{
// don't double process the initial impact (possible on the server)
if ( NumChainedDeathAnims == 0 && PawnOwner.WorldInfo.TimeSeconds == KFPOwner.TimeOfDeath )
{
return;
}
KFPOwner.CheckHitInfo(HitInfo, KFPOwner.Mesh, HitDir, HitLoc);
DeathAnim = KFPOwner.PawnAnimInfo.ChooseDeathAnimation(KFPOwner, DamageType, HitDir, HitInfo.BoneName);
if( DeathAnim != '' )
{
PlaySpecialMoveAnim(DeathAnim, EAS_FullBody, 0.2, -1.f);
}
NumChainedDeathAnims++;
NextDeathAnim_ActorTime = PawnOwner.GetActorTimeSeconds() + RandRange(0.15f, 0.33f);
}
}
defaultproperties
{
MaxChainedDeathAnims=5
bDisablesWeaponFiring=true
}