//============================================================================= // KFSkeletalMeshComponent //============================================================================= // SMC with FOV settings //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC // Based on UDKSkeletalMeshComponent Copyright 1998-2012 Epic Games, Inc. // - Andrew "Strago" Ladenberger //============================================================================= class KFSkeletalMeshComponent extends SkeletalMeshComponent native; cpptext { /** Creates a FKFSkeletalMeshSceneProxy (defined in UTWeapon.cpp) */ virtual FPrimitiveSceneProxy* CreateSceneProxy() override; /** Default tick. */ virtual void Tick(FLOAT DeltaTime) override; /** The KFSkeletalMesh ticks during async as well as post async, performing different work. */ void TickPostAsync(FLOAT DeltaTime); /** Overridden ProcessRootMotion so that we can defer root motion until post asycn work. */ virtual void ProcessRootMotion( FLOAT DeltaTime, FBoneAtom& ExtractedRootMotionDelta, INT& bHasRootMotion ) override; #if __TW_HEADSCALE_ virtual float GetHeadBoneScale() override { return HeadBoneScale; } #endif } /** This variable is set during async tick so that we have the cached value when we tick physics calls in the post tick. */ var BoneAtom CachedExtractedRootMotionDelta; /** This variable is set during async tick so that we have the cached value when we tick physics calls in the post tick. */ var int bCachedHasRootMotion; /** This variable is set during async tick so that we have the cached value when we tick physics calls in the post tick. */ var bool bNeedsProcessRootMotion; /** This changes the FOV used for rendering the skeletal mesh component. A value of 0 means to use the default. */ var() const float FOV; /** whether textures are currently forced loaded */ var bool bForceLoadTextures; /** When this component is ticking in async time, we need to know if there is pending work that needs to be ticked in post async * because we actually switch during ragdoll to post async and don't want to perform deferred tick work. */ var bool bPendingDeferredWork; /** when to clear forced streaming */ var float ClearStreamingTime; /** * If > 0, ensure the DeltaTime does not go above this value. * Useful when issuing tick notifies (e.g. Hitbox collision) is critical */ var() float MinTickTimeStep; /** Scale to use when doing line traces against the head bone */ var() float HeadBoneScale; /** * Force streamed textures to be loaded. Used to get MIPS streamed in before weapon comes up * @PARAM bForcePreload if true causes streamed textures to be force loaded, if false, clears force loading */ simulated event PreloadTextures(bool bForcePreload, float ClearTime) { local int idx; bForceLoadTextures = bForcePreload; ClearStreamingTime = ClearTime; for (Idx = 0; Idx < Materials.Length; Idx++) { if (Materials[Idx] != None) { Materials[Idx].SetForceMipLevelsToBeResident(true, bForcePreload, -1.0f); } } } /** changes the value of FOV */ native final function SetFOV(float NewFOV); /** * Called by AnimNotify_PlayParticleEffect * Looks for a socket name first then bone name * * @param AnimNotifyData The AnimNotify_PlayParticleEffect which will have all of the various params on it */ event bool PlayParticleEffect( const AnimNotify_PlayParticleEffect AnimNotifyData ) { local KFParticleSystemComponent PSC; // Assign 1st person particles to match depth and FOV // abridged version (only attached, non-extreme content) from Super() if ( DepthPriorityGroup == SDPG_Foreground && AnimNotifyData.bAttach && !AnimNotifyData.bIsExtremeContent ) { PSC = new(self) class'KFParticleSystemComponent'; // move this to the object pool once it can support attached to bone/socket and relative translation/rotation PSC.SetTemplate( AnimNotifyData.PSTemplate ); PSC.SetDepthPriorityGroup(SDPG_Foreground); PSC.SetFOV(FOV); if (AnimNotifyData.bUsePostUpdateWorkTickGroup) { PSC.SetTickGroup(TG_PostUpdateWork); } if( AnimNotifyData.SocketName != '' ) { //`log( "attaching AnimNotifyData.SocketName" ); AttachComponentToSocket( PSC, AnimNotifyData.SocketName ); } else if( AnimNotifyData.BoneName != '' ) { //`log( "attaching AnimNotifyData.BoneName" ); AttachComponent( PSC, AnimNotifyData.BoneName ); } PSC.ActivateSystem(true); PSC.OnSystemFinished = SkelMeshCompOnParticleSystemFinished; // We use this to keep track of particle systems spawned by animations if( PSC != none && Owner != none ) { Owner.OnAnimNotifyParticleSystemSpawned( AnimNotifyData, PSC ); } return true; } return Super.PlayParticleEffect(AnimNotifyData); } /** Returns the duration (in seconds) for a named AnimSequence. Returns 0.f if no animation. */ final function float GetAnimInterruptTime(Name AnimSeqName) { return GetAnimNotifyTime(AnimSeqName, class'KFAnimNotify_Interrupt'); } /** Returns the duration (in seconds) for a named AnimSequence. Returns 0.f if no animation. */ final function float GetReloadAmmoTime(Name AnimSeqName) { return GetAnimNotifyTime(AnimSeqName, class'KFAnimNotify_ReloadAmmo'); } /** Returns the duration (in seconds) for a named AnimSequence. Returns 0.f if no animation. */ final function float GetAnimNotifyTime(Name AnimSeqName, class NotifyClass) { local int i; local AnimSequence AnimSeq; AnimSeq = FindAnimSequence(AnimSeqName); if( AnimSeq == None || AnimSeq.RateScale <= 0.f ) { return 0.f; } for(i=0; i