1
0
KF2-Dev-Scripts/Engine/Classes/SkeletalMeshActor.uc
2020-12-13 18:01:13 +03:00

736 lines
23 KiB
Ucode

/**
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
*/
class SkeletalMeshActor extends Actor
native(Anim)
ClassGroup(SkeletalMeshes)
placeable;
var() bool bDamageAppliesImpulse;
/** Whether or not this actor should respond to anim notifies - CURRENTLY ONLY AFFECTS PlayParticleEffect NOTIFIES**/
var() bool bShouldDoAnimNotifies;
var() SkeletalMeshComponent SkeletalMeshComponent;
var() const editconst LightEnvironmentComponent LightEnvironment;
var AudioComponent FacialAudioComp;
/** Used to replicate mesh to clients */
var repnotify transient SkeletalMesh ReplicatedMesh;
/** used to replicate the material in index 0 */
var repnotify MaterialInterface ReplicatedMaterial0, ReplicatedMaterial1;
struct CheckpointRecord
{
var bool bReplicated;
var bool bHidden;
var bool bSavedPosition;
var vector Location;
var rotator Rotation;
};
/** @hack: force saving positional data in checkpoint - some uses in Matinee require this */
var() bool bForceSaveInCheckpoint;
/** Struct that stores info to update one skel control with a location target */
struct native SkelMeshActorControlTarget
{
/** Name of SkelControl to update */
var() name ControlName;
/** Actor to use for location of skel control target. */
var() actor TargetActor;
};
/** Set of skel controls to update targets of, based on Actor location */
var() array<SkelMeshActorControlTarget> ControlTargets;
/** mirror of bCollideActors used for backwards compatibility with change of default bCollideActors to FALSE
* we save the value in previous package versions into this property instead and copy back
* thus preserving the value of the property in old content regardless of whether they modified it
*/
var deprecated bool bCollideActors_OldValue;
/** List of Matinee InterpGroup controlling this actor. */
var transient Array<InterpGroup> InterpGroupList;
/** This is only editor only, when exiting Matinee, it should preserve previous position **/
var transient private name SavedAnimSeqName;
var transient private float SavedCurrentTime;
/** If TRUE, attachments are set to use this actors mesh as a shadow parent and share this actors light environment */
var() bool bShouldShadowParentAllAttachedActors;
cpptext
{
// UObject interface
#if WITH_EDITOR
virtual void CheckForErrors();
#endif
//@compatibility
virtual void PostLoad();
// AActor interface
virtual void TickSpecial(FLOAT DeltaSeconds);
virtual void ForceUpdateComponents(UBOOL bCollisionUpdate,UBOOL bTransformOnly);
virtual void PreviewBeginAnimControl(class UInterpGroup* InInterpGroup);
virtual void PreviewSetAnimPosition(FName SlotName, INT ChannelIndex, FName InAnimSeqName, FLOAT InPosition, UBOOL bLooping, UBOOL bFireNotifies, UBOOL bEnableRootMotion, FLOAT DeltaTime);
virtual void PreviewSetAnimWeights(TArray<FAnimSlotInfo>& SlotInfos);
virtual void PreviewFinishAnimControl(class UInterpGroup* InInterpGroup);
virtual void PreviewUpdateFaceFX(UBOOL bForceAnim, const FString& GroupName, const FString& SeqName, FLOAT InPosition);
// WWISEMODIF_START, alessard, nov-28-2008, WwiseAudioIntegration
virtual void PreviewActorPlayFaceFX(const FString& GroupName, const FString& SeqName, UAkBaseSoundObject* InSoundCue);
// WWISEMODIF_END
virtual void PreviewActorStopFaceFX();
virtual UAudioComponent* PreviewGetFaceFXAudioComponent();
virtual class UFaceFXAsset* PreviewGetActorFaceFXAsset();
/** Called each from while the Matinee action is running, to set the animation weights for the actor. */
virtual void SetAnimWeights( const TArray<struct FAnimSlotInfo>& SlotInfos );
/** Build AnimSet list, called by UpdateAnimSetList() */
void BuildAnimSetList();
/** Add a given list of anim sets on the top of the list (so they override the other ones */
void AddAnimSets(const TArray<class UAnimSet*>& CustomAnimSets);
/** Restore Mesh's AnimSets to defaults, as defined in the default properties */
void RestoreAnimSetsToDefault();
/** Save Mesh's defaults to AnimSets, back up*/
void SaveDefaultsToAnimSets();
protected:
#if USE_GAMEPLAY_PROFILER
/**
* This function actually does the work for the GetProfilerAssetObject and is virtual.
* It should only be called from GetProfilerAssetObject as GetProfilerAssetObject is safe to call on NULL object pointers
*/
virtual UObject* GetProfilerAssetObjectInternal() const;
#endif
/**
* This function actually does the work for the GetDetailInfo and is virtual.
* It should only be called from GetDetailedInfo as GetDetailedInfo is safe to call on NULL object pointers
*/
virtual FString GetDetailedInfoInternal() const;
}
replication
{
if (Role == ROLE_Authority)
ReplicatedMesh, ReplicatedMaterial0, ReplicatedMaterial1;
}
simulated event PostBeginPlay()
{
// grab the current mesh for replication
if (Role == ROLE_Authority && SkeletalMeshComponent != None)
{
ReplicatedMesh = SkeletalMeshComponent.SkeletalMesh;
}
if (bShouldShadowParentAllAttachedActors)
{
SetShadowParentOnAllAttachedComponents(SkeletalMeshComponent, LightEnvironment);
}
// Unfix bodies flagged as 'full anim weight'
if( SkeletalMeshComponent != None &&
//SkeletalMeshComponent.bEnableFullAnimWeightBodies &&
SkeletalMeshComponent.PhysicsAssetInstance != None )
{
SkeletalMeshComponent.PhysicsAssetInstance.SetFullAnimWeightBonesFixed(FALSE, SkeletalMeshComponent);
}
if( bHidden )
{
SkeletalMeshComponent.SetClothFrozen(TRUE);
}
`if(`__TW_PERFORMANCE_)
if (WorldInfo.NetMode == NM_DedicatedServer)
{
SetTickIsDisabled(true);
}
`endif
}
simulated event Destroyed()
{
Super.Destroyed();
// Empty list of Matinee that control us
InterpGroupList.Length = 0;
// Free up animsets.
UpdateAnimSetList();
}
/** Update list of AnimSets for this Pawn */
native simulated function UpdateAnimSetList();
simulated event ReplicatedEvent( name VarName )
{
if (VarName == 'ReplicatedMesh')
{
SkeletalMeshComponent.SetSkeletalMesh(ReplicatedMesh);
}
else if (VarName == 'ReplicatedMaterial0')
{
SkeletalMeshComponent.SetMaterial(0, ReplicatedMaterial0);
}
else if (VarName == 'ReplicatedMaterial1')
{
SkeletalMeshComponent.SetMaterial(1, ReplicatedMaterial1);
}
else
{
Super.ReplicatedEvent(VarName);
}
}
/** Handling Toggle event from Kismet. */
simulated function OnToggle(SeqAct_Toggle action)
{
local AnimNodeSequence SeqNode;
SeqNode = AnimNodeSequence(SkeletalMeshComponent.Animations);
if( SeqNode != None )
{
// Turn ON
if (action.InputLinks[0].bHasImpulse)
{
// If animation is not playing - start playing it now.
if(!SeqNode.bPlaying)
{
// This starts the animation playing from the beginning. Do we always want that?
SeqNode.PlayAnim(SeqNode.bLooping, SeqNode.Rate, 0.0);
}
}
// Turn OFF
else if (action.InputLinks[1].bHasImpulse)
{
// If animation is playing, stop it now.
if(SeqNode.bPlaying)
{
SeqNode.StopAnim();
}
}
// Toggle
else if (action.InputLinks[2].bHasImpulse)
{
// Toggle current animation state.
if(SeqNode.bPlaying)
{
SeqNode.StopAnim();
}
else
{
SeqNode.PlayAnim(SeqNode.bLooping, SeqNode.Rate, 0.0);
}
}
}
}
function OnSetMaterial(SeqAct_SetMaterial Action)
{
SkeletalMeshComponent.SetMaterial( Action.MaterialIndex, Action.NewMaterial );
if (Action.MaterialIndex == 0)
{
ReplicatedMaterial0 = Action.NewMaterial;
ForceNetRelevant();
}
if (Action.MaterialIndex == 1)
{
ReplicatedMaterial1 = Action.NewMaterial;
ForceNetRelevant();
}
}
simulated event BeginAnimControl(InterpGroup InInterpGroup)
{
MAT_BeginAnimControl(InInterpGroup);
}
/** Start AnimControl. Add required AnimSets. */
native function MAT_BeginAnimControl(InterpGroup InInterpGroup);
simulated event SetAnimPosition(name SlotName, int ChannelIndex, name InAnimSeqName, float InPosition, bool bFireNotifies, bool bLooping, bool bEnableRootMotion)
{
local AnimNodeSequence SeqNode;
// Ensure anims are updated correctly in cinematics even when the mesh isn't rendered.
SkeletalMeshComponent.LastRenderTime = WorldInfo.TimeSeconds;
SeqNode = AnimNodeSequence(SkeletalMeshComponent.Animations);
if( SeqNode != None )
{
if( SeqNode.AnimSeqName != InAnimSeqName || SeqNode.AnimSeq == None )
{
SeqNode.SetAnim(InAnimSeqName);
}
SeqNode.bLooping = bLooping;
SeqNode.SetPosition(InPosition, bFireNotifies);
}
}
/** Called when we are done with the AnimControl track. */
simulated event FinishAnimControl(InterpGroup InInterpGroup)
{
MAT_FinishAnimControl(InInterpGroup);
}
/** End AnimControl. Release required AnimSets */
native function MAT_FinishAnimControl(InterpGroup InInterpGroup);
/** Handler for Matinee wanting to play FaceFX animations in the game. */
// WWISEMODIF_START, alessard, nov-28-2008, WwiseAudioIntegration
simulated event bool PlayActorFaceFXAnim(FaceFXAnimSet AnimSet, String GroupName, String SeqName, SoundCue SoundCueToPlay, AkEvent AkEventToPlay)
{
return SkeletalMeshComponent.PlayFaceFXAnim(AnimSet, SeqName, GroupName, SoundCueToPlay, AkEventToPlay);
}
// WWISEMODIF_END
/** Handler for Matinee wanting to stop FaceFX animations in the game. */
simulated event StopActorFaceFXAnim()
{
SkeletalMeshComponent.StopFaceFXAnim();
}
/** Used to let FaceFX know what component to play dialogue audio on. */
simulated event AudioComponent GetFaceFXAudioComponent()
{
return FacialAudioComp;
}
/** Function for handling the SeqAct_PlayFaceFXAnim Kismet action working on this Actor. */
simulated function OnPlayFaceFXAnim(SeqAct_PlayFaceFXAnim inAction)
{
local PlayerController PC;
// WWISEMODIF_START, alessard, nov-28-2008, WwiseAudioIntegration
SkeletalMeshComponent.PlayFaceFXAnim(inAction.FaceFXAnimSetRef, inAction.FaceFXAnimName, inAction.FaceFXGroupName, inAction.SoundCueToPlay, inAction.AkEventToPlay);
// WWISEMODIF_END
// tell non-local players to play as well
foreach WorldInfo.AllControllers(class'PlayerController', PC)
{
if (NetConnection(PC.Player) != None)
{
// WWISEMODIF_START, alessard, nov-28-2008, WwiseAudioIntegration
PC.ClientPlayActorFaceFXAnim(self, inAction.FaceFXAnimSetRef, inAction.FaceFXGroupName, inAction.FaceFXAnimName, inAction.SoundCueToPlay, inAction.AkEventToPlay);
// WWISEMODIF_END
}
}
}
/** Used by Matinee in-game to mount FaceFXAnimSets before playing animations. */
simulated event FaceFXAsset GetActorFaceFXAsset()
{
if(SkeletalMeshComponent.SkeletalMesh != None && !SkeletalMeshComponent.bDisableFaceFX)
{
return SkeletalMeshComponent.SkeletalMesh.FaceFXAsset;
}
else
{
return None;
}
}
/**
* Returns TRUE if this actor is playing a FaceFX anim.
*/
simulated function bool IsActorPlayingFaceFXAnim()
{
return (SkeletalMeshComponent != None && SkeletalMeshComponent.IsPlayingFaceFXAnim());
}
event OnSetMesh(SeqAct_SetMesh Action)
{
if (Action.MeshType == MeshType_SkeletalMesh)
{
if (Action.NewSkeletalMesh != None && Action.NewSkeletalMesh != SkeletalMeshComponent.SkeletalMesh)
{
SkeletalMeshComponent.SetSkeletalMesh(Action.NewSkeletalMesh);
ReplicatedMesh = Action.NewSkeletalMesh;
}
}
}
/** Handle action that forces bodies to sync to their animated location */
simulated event OnUpdatePhysBonesFromAnim(SeqAct_UpdatePhysBonesFromAnim Action)
{
if (action.InputLinks[0].bHasImpulse)
{
SkeletalMeshComponent.ForceSkelUpdate();
SkeletalMeshComponent.UpdateRBBonesFromSpaceBases(TRUE, TRUE);
SkeletalMeshComponent.ForceApexClothingTeleportAndReset();
}
else if(action.InputLinks[1].bHasImpulse)
{
// Fix bodies flagged as 'full anim weight'
if( SkeletalMeshComponent.PhysicsAssetInstance != None )
{
SkeletalMeshComponent.PhysicsAssetInstance.SetAllBodiesFixed(TRUE);
}
}
else if(action.InputLinks[2].bHasImpulse)
{
// Unfix bodies flagged as 'full anim weight'
if( SkeletalMeshComponent.PhysicsAssetInstance != None )
{
SkeletalMeshComponent.PhysicsAssetInstance.SetFullAnimWeightBonesFixed(FALSE, SkeletalMeshComponent);
}
}
}
/** Handle action to set skel control target from kismet. */
simulated event OnSetSkelControlTarget(SeqAct_SetSkelControlTarget Action)
{
local int i;
// Check we have the info we need
if(Action.SkelControlName == '' || Action.TargetActors.length == 0)
{
return;
}
// First see if we have an entry for this control
for(i=0; i<ControlTargets.length; i++)
{
// See if name matches
if(ControlTargets[i].ControlName == Action.SkelControlName)
{
// It does - just update target actor
ControlTargets[i].TargetActor = Actor(Action.TargetActors[Rand(Action.TargetActors.length)]);
return;
}
}
// Did not find an existing entry - make a new one
ControlTargets.length = ControlTargets.length + 1;
ControlTargets[ControlTargets.length-1].ControlName = Action.SkelControlName;
ControlTargets[ControlTargets.length-1].TargetActor = Actor(Action.TargetActors[Rand(Action.TargetActors.length)]);
}
/** Performs actual attachment. Can be subclassed for class specific behaviors. */
function DoKismetAttachment(Actor Attachment, SeqAct_AttachToActor Action)
{
local bool bOldCollideActors, bOldBlockActors, bValidBone, bValidSocket;
// If a bone/socket has been specified, see if it is valid
if( SkeletalMeshComponent != None && Action.BoneName != '' )
{
// See if the bone name refers to an existing socket on the skeletal mesh.
bValidSocket = (SkeletalMeshComponent.GetSocketByName(Action.BoneName) != None);
bValidBone = (SkeletalMeshComponent.MatchRefBone(Action.BoneName) != INDEX_NONE);
// Issue a warning if we were expecting to attach to a bone/socket, but it could not be found.
if( !bValidBone && !bValidSocket )
{
`log(WorldInfo.TimeSeconds @ class @ GetFuncName() @ "bone or socket" @ Action.BoneName @ "not found on actor" @ Self @ "with mesh" @ SkeletalMeshComponent);
}
}
// Special case for handling relative location/rotation w/ bone or socket
if( bValidBone || bValidSocket )
{
// disable collision, so we can successfully move the attachment
bOldCollideActors = Attachment.bCollideActors;
bOldBlockActors = Attachment.bBlockActors;
Attachment.SetCollision(FALSE, FALSE);
Attachment.SetHardAttach(Action.bHardAttach);
// Sockets by default move the actor to the socket location.
// This is not the case for bones!
// So if we use relative offsets, then first move attachment to bone's location.
if( bValidBone && !bValidSocket )
{
if( Action.bUseRelativeOffset )
{
Attachment.SetLocation(SkeletalMeshComponent.GetBoneLocation(Action.BoneName));
}
if( Action.bUseRelativeRotation )
{
Attachment.SetRotation(QuatToRotator(SkeletalMeshComponent.GetBoneQuaternion(Action.BoneName)));
}
}
// Attach attachment to base.
Attachment.SetBase(Self,, SkeletalMeshComponent, Action.BoneName);
if( Action.bUseRelativeRotation )
{
Attachment.SetRelativeRotation(Attachment.RelativeRotation + Action.RelativeRotation);
}
// if we're using the offset, place attachment relatively to the target
if( Action.bUseRelativeOffset )
{
Attachment.SetRelativeLocation(Attachment.RelativeLocation + Action.RelativeOffset);
}
// restore previous collision
Attachment.SetCollision(bOldCollideActors, bOldBlockActors);
}
else
{
// otherwise base on location
Super.DoKismetAttachment(Attachment, Action);
}
}
/**
* Default behaviour when shot is to apply an impulse and kick the KActor.
*/
`if(`__TW_)
// Clientside placed actors, RemoteRole==None && bNoDelete, need the simulated keyword
simulated event TakeDamage(int Damage, Controller EventInstigator, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
`else
event TakeDamage(int Damage, Controller EventInstigator, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
`endif
{
local vector ApplyImpulse;
// call Actor's version to handle any SeqEvent_TakeDamage for scripting
Super.TakeDamage(Damage, EventInstigator, HitLocation, Momentum, DamageType, HitInfo, DamageCauser);
if ( bDamageAppliesImpulse && damageType.default.KDamageImpulse > 0 )
{
if ( VSize(momentum) < 0.001 )
{
`Log("Zero momentum to SkeletalMeshActor.TakeDamage");
return;
}
ApplyImpulse = Normal(momentum) * damageType.default.KDamageImpulse;
if ( HitInfo.HitComponent != None )
{
HitInfo.HitComponent.AddImpulse(ApplyImpulse, HitLocation, HitInfo.BoneName);
}
}
}
// checkpointing
function bool ShouldSaveForCheckpoint()
{
return (RemoteRole != ROLE_None || bForceSaveInCheckpoint || IsInPersistentLevel(true));
}
function CreateCheckpointRecord(out CheckpointRecord Record)
{
Record.bReplicated = (RemoteRole != ROLE_None);
Record.bHidden = bHidden;
if (bForceSaveInCheckpoint || IsInPersistentLevel(true))
{
Record.bSavedPosition = true;
Record.Location = Location;
Record.Rotation = Rotation;
}
}
function ApplyCheckpointRecord(const out CheckpointRecord Record)
{
SetHidden(Record.bHidden);
if (Record.bSavedPosition)
{
SetLocation(Record.Location);
SetRotation(Record.Rotation);
}
if (Record.bReplicated)
{
ForceNetRelevant();
if (RemoteRole != ROLE_None)
{
SetForcedInitialReplicatedProperty(Property'Engine.Actor.bHidden', (bHidden == default.bHidden));
}
}
}
/**
* 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
*
* @return bool true if the particle effect was played, false if not;
*/
event bool PlayParticleEffect( const AnimNotify_PlayParticleEffect AnimNotifyData )
{
local vector Loc;
local rotator Rot;
local ParticleSystemComponent PSC;
local bool bPlayNonExtreme;
if (WorldInfo.NetMode == NM_DedicatedServer)
{
`Log("(SkeletalMeshActor): PlayParticleEffect on dedicated server!");
return true;
}
// should I play non extreme content?
bPlayNonExtreme = ( AnimNotifyData.bIsExtremeContent == TRUE ) && ( WorldInfo.GRI.ShouldShowGore() == FALSE ) ;
// if we should not respond to anim notifies OR if this is extreme content and we can't show extreme content then return
if( ( bShouldDoAnimNotifies == FALSE )
// if playing non extreme but no data is set, just return
|| ( bPlayNonExtreme && AnimNotifyData.PSNonExtremeContentTemplate==None )
)
{
// Return TRUE to prevent the SkelMeshComponent from playing it as well!
return true;
}
// now go ahead and spawn the particle system based on whether we need to attach it or not
if( AnimNotifyData.bAttach == TRUE )
{
PSC = new(self) class'ParticleSystemComponent'; // move this to the object pool once it can support attached to bone/socket and relative translation/rotation
if ( bPlayNonExtreme )
{
PSC.SetTemplate( AnimNotifyData.PSNonExtremeContentTemplate );
}
else
{
PSC.SetTemplate( AnimNotifyData.PSTemplate );
}
if( AnimNotifyData.SocketName != '' )
{
//`log( "attaching AnimNotifyData.SocketName" );
SkeletalMeshComponent.AttachComponentToSocket( PSC, AnimNotifyData.SocketName );
}
else if( AnimNotifyData.BoneName != '' )
{
//`log( "attaching AnimNotifyData.BoneName" );
SkeletalMeshComponent.AttachComponent( PSC, AnimNotifyData.BoneName );
}
PSC.ActivateSystem();
PSC.OnSystemFinished = SkelMeshActorOnParticleSystemFinished;
}
else
{
// find the location
if( AnimNotifyData.SocketName != '' )
{
SkeletalMeshComponent.GetSocketWorldLocationAndRotation( AnimNotifyData.SocketName, Loc, Rot );
}
else if( AnimNotifyData.BoneName != '' )
{
Loc = SkeletalMeshComponent.GetBoneLocation( AnimNotifyData.BoneName );
Rot = QuatToRotator(SkeletalMeshComponent.GetBoneQuaternion(AnimNotifyData.BoneName));
}
else
{
Loc = Location;
Rot = rot(0,0,1);
}
PSC = WorldInfo.MyEmitterPool.SpawnEmitter( AnimNotifyData.PSTemplate, Loc, Rot);
}
if( PSC != None && AnimNotifyData.BoneSocketModuleActorName != '' )
{
// NVCHANGE_BEGIN: JCAO - Apply the lightingChannel for the particle from the pawn
PSC.SetLightingChannels(SkeletalMeshComponent.LightingChannels);
// NVCHANGE_END: JCAO - Apply the lightingChannel for the particle from the pawn
PSC.SetActorParameter(AnimNotifyData.BoneSocketModuleActorName, self);
}
return true;
}
/** We so we detach the Component once we are done playing it **/
simulated function SkelMeshActorOnParticleSystemFinished( ParticleSystemComponent PSC )
{
SkeletalMeshComponent.DetachComponent( PSC );
}
/**
* Called by AnimNotify_ForceField
* Looks for a socket name first then bone name
*
* @param AnimNotifyData The AnimNotify_ForceField which will have all of the various params on it
*
* @return bool true if the forcefield was created, false if not;
*/
event bool CreateForceField( const AnimNotify_Forcefield AnimNotifyData)
{
local NxForceFieldComponent NewForceFieldComponent;
NewForceFieldComponent =
new(SkeletalMeshComponent) AnimNotifyData.ForceFieldComponent.Class(AnimNotifyData.ForceFieldComponent);
NewForceFieldComponent.DoInitRBPhys();
if( AnimNotifyData.SocketName != '' )
{
SkeletalMeshComponent.AttachComponentToSocket( NewForceFieldComponent, AnimNotifyData.SocketName );
}
else if( AnimNotifyData.BoneName != '' )
{
SkeletalMeshComponent.AttachComponent( NewForceFieldComponent, AnimNotifyData.BoneName );
}
return true;
}
defaultproperties
{
Begin Object Class=AnimNodeSequence Name=AnimNodeSeq0
End Object
Begin Object Class=DynamicLightEnvironmentComponent Name=MyLightEnvironment
`if(`__TW_LIGHTING_MODIFICATIONS_)
bEnabled=False
`else
bEnabled=True
`endif
TickGroup=TG_DuringAsyncWork
// Using a skylight for secondary lighting by default to be cheap
// Characters and other important skeletal meshes should set bSynthesizeSHLight=true
End Object
`if(`__TW_LIGHTING_MODIFICATIONS_)
// Don't add to components list
`else
Components.Add(MyLightEnvironment)
`endif
LightEnvironment=MyLightEnvironment
Begin Object Class=SkeletalMeshComponent Name=SkeletalMeshComponent0
Animations=AnimNodeSeq0
bUpdateSkelWhenNotRendered=FALSE
CollideActors=TRUE //@warning: leave at TRUE until backwards compatibility code is removed (bCollideActors_OldValue, etc)
BlockActors=FALSE
BlockZeroExtent=TRUE
BlockNonZeroExtent=FALSE
BlockRigidBody=FALSE
LightEnvironment=MyLightEnvironment
RBChannel=RBCC_GameplayPhysics
RBCollideWithChannels=(Default=TRUE,BlockingVolume=TRUE,GameplayPhysics=TRUE,EffectPhysics=TRUE)
`if(`__TW_LIGHTING_MODIFICATIONS_) // Custom lighting channel implementation
LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE)
`endif
End Object
CollisionComponent=SkeletalMeshComponent0
SkeletalMeshComponent=SkeletalMeshComponent0
Components.Add(SkeletalMeshComponent0)
Begin Object Class=AudioComponent Name=FaceAudioComponent
End Object
FacialAudioComp=FaceAudioComponent
Components.Add(FaceAudioComponent)
Physics=PHYS_None
bEdShouldSnap=TRUE
bStatic=FALSE
bCollideActors=false
bBlockActors=FALSE
bWorldGeometry=FALSE
bCollideWorld=FALSE
bNoEncroachCheck=TRUE
bProjTarget=TRUE
bUpdateSimulatedPosition=FALSE
RemoteRole=ROLE_None
bNoDelete=TRUE
bShouldDoAnimNotifies=true
bCollideActors_OldValue=true
}