/** * Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. */ class KActor extends DynamicSMActor native(Physics) nativereplication placeable ClassGroup(Physics) showcategories(Navigation); cpptext { // AActor interface virtual void physRigidBody(FLOAT DeltaTime); virtual INT* GetOptimizedRepList(BYTE* InDefault, FPropertyRetirement* Retire, INT* Ptr, UPackageMap* Map, UActorChannel* Channel); virtual void OnRigidBodyCollision(const FRigidBodyCollisionInfo& MyInfo, const FRigidBodyCollisionInfo& OtherInfo, const FCollisionImpactData& RigidCollisionData); UBOOL ShouldTrace(UPrimitiveComponent* Primitive, AActor *SourceActor, DWORD TraceFlags); /** * Function that gets called from within Map_Check to allow this actor to check itself * for any potential errors and register them with map check dialog. */ #if WITH_EDITOR virtual void CheckForErrors(); #endif virtual void TickSpecial(FLOAT DeltaSeconds); } var() bool bDamageAppliesImpulse; var() repnotify bool bWakeOnLevelStart; // Impact effects var ParticleSystemComponent ImpactEffectComponent; var AudioComponent ImpactSoundComponent; var AudioComponent ImpactSoundComponent2; // @TODO: This could be turned into a dynamic array; but for the moment just 2 will do. var float LastImpactTime; var PhysEffectInfo ImpactEffectInfo; // Slide effects var ParticleSystemComponent SlideEffectComponent; var AudioComponent SlideSoundComponent; var bool bCurrentSlide; var bool bSlideActive; var float LastSlideTime; var PhysEffectInfo SlideEffectInfo; /** Enable 'Stay upright' torque, that tries to keep Z axis of KActor pointing along world Z */ var(StayUprightSpring) bool bEnableStayUprightSpring; /** Torque applied to try and keep KActor horizontal. */ var(StayUprightSpring) float StayUprightTorqueFactor; /** Max torque that can be applied to try and keep KActor horizontal */ var(StayUprightSpring) float StayUprightMaxTorque; /** If TRUE limit the maximum speed this object can move. */ var() bool bLimitMaxPhysicsVelocity; /** If bLimitMaxPhysicsVelocity is TRUE, this is how fast the object can move. */ var() float MaxPhysicsVelocity; var native const RigidBodyState RBState; var native const float AngErrorAccumulator; /** replicated version of DrawScale3D */ var repnotify vector ReplicatedDrawScale3D; var transient vector InitialLocation; var transient rotator InitialRotation; /** whether we need to replicate RBState - used to avoid it for bNoDelete KActors that haven't moved or been awakened yet * as in that case the client should already have the same data */ var transient bool bNeedsRBStateReplication; /** * Set TRUE to disable collisions with Pawn rigid bodies on clients. Set this to true if using optimizations that * could cause the server to miss or ignore contacts that the client might dtect with this KActor, which could cause * vibration, rubberbanding, and general visual badness. */ var bool bDisableClientSidePawnInteractions; replication { if (bNeedsRBStateReplication && Role == ROLE_Authority) RBState; if (bNetInitial && Role == ROLE_Authority) bWakeOnLevelStart, ReplicatedDrawScale3D; } /** Util for getting the PhysicalMaterial applied to this KActor's StaticMesh. */ native final function PhysicalMaterial GetKActorPhysMaterial(); /** Forces the resolve the RBState regardless of whether the actor is sleeping */ native final function ResolveRBState(); simulated event PostBeginPlay() { Super.PostBeginPlay(); if (bWakeOnLevelStart && (StaticMeshComponent != None) ) { StaticMeshComponent.WakeRigidBody(); } else { bNeedsRBStateReplication = !bNoDelete; } ReplicatedDrawScale3D = DrawScale3D * 1000.0f; // avoids effects of vector rounding // Initialise impact/slide components (if we are being notified of physics events, and have sounds/effects set up // in PhysicalMaterial applied to our static mesh. if((StaticMeshComponent != None) && StaticMeshComponent.bNotifyRigidBodyCollision) { SetPhysicalCollisionProperties(); } InitialLocation = Location; InitialRotation = Rotation; if ( bDisableClientSidePawnInteractions && (Role != ROLE_Authority) && (StaticMeshComponent != None) ) { // on clients, turn off collision with pawn RBs, Let replication handle collision response // to avoid server/client disagreement. StaticMeshComponent.SetRBCollidesWithChannel(RBCC_Pawn, FALSE); } } /** called when the actor falls out of the world 'safely' (below KillZ and such) */ simulated event FellOutOfWorld(class dmgType) { ShutDown(); Super.FellOutOfWorld(dmgType); } simulated event Destroyed() { // Let the components play out normally if( ImpactEffectInfo.Sound != None ) { if( ImpactSoundComponent != none ) { ImpactSoundComponent.bAutoDestroy = TRUE; } if( ImpactSoundComponent2 != none ) { ImpactSoundComponent2.bAutoDestroy = TRUE; } } if( SlideEffectInfo.Sound != None ) { SlideSoundComponent.bAutoDestroy = TRUE; } Super.Destroyed(); } simulated function SetPhysicalCollisionProperties() { local PhysicalMaterial PhysMat; PhysMat = GetKActorPhysMaterial(); // cache effect info ImpactEffectInfo = PhysMat.FindPhysEffectInfo(EPMET_Impact); SlideEffectInfo = PhysMat.FindPhysEffectInfo(EPMET_Slide); if(ImpactEffectInfo.Effect != None) { ImpactEffectComponent = new(self) class'ParticleSystemComponent'; //AttachComponent(ImpactEffectComponent); ImpactEffectComponent.bAutoActivate = FALSE; ImpactEffectComponent.SetTemplate(ImpactEffectInfo.Effect); } `if (`__TW_) // do nothing `else if(ImpactEffectInfo.Sound != None) { ImpactSoundComponent = new(self) class'AudioComponent'; //AttachComponent(ImpactSoundComponent); ImpactSoundComponent.SoundCue = ImpactEffectInfo.Sound; ImpactSoundComponent2 = new(self) class'AudioComponent'; //AttachComponent(ImpactSoundComponent2); ImpactSoundComponent2.SoundCue = ImpactEffectInfo.Sound; } `endif // __TW_ if(SlideEffectInfo.Effect != None) { SlideEffectComponent = new(self) class'ParticleSystemComponent'; //AttachComponent(SlideEffectComponent); SlideEffectComponent.bAutoActivate = FALSE; SlideEffectComponent.SetTemplate(SlideEffectInfo.Effect); } `if (`__TW_) // do nothing `else if(SlideEffectInfo.Sound != None) { SlideSoundComponent = new(self) class'AudioComponent'; //AttachComponent(SlideSoundComponent); SlideSoundComponent.SoundCue = SlideEffectInfo.Sound; } `endif // __TW_ } /** Makes sure these properties get set up on spawned meshes. Factories do not set the mesh before PostBeginPlay is called. */ simulated event SpawnedByKismet() { if(StaticMeshComponent.bNotifyRigidBodyCollision) { SetPhysicalCollisionProperties(); } InitialLocation = Location; InitialRotation = Rotation; } simulated event ReplicatedEvent(name VarName) { local vector NewDrawScale3D; if (VarName == 'bWakeOnLevelStart') { if (bWakeOnLevelStart) { StaticMeshComponent.WakeRigidBody(); } } else if (VarName == nameof(ReplicatedDrawScale3D)) { NewDrawScale3D = ReplicatedDrawScale3D / 1000.0; // needs to match multiply in PostBeginPlay() SetDrawScale3D(NewDrawScale3D); } else { Super.ReplicatedEvent(VarName); } } event ApplyImpulse( Vector ImpulseDir, float ImpulseMag, Vector HitLocation, optional TraceHitInfo HitInfo, optional class DamageType ) { local vector AppliedImpulse; AppliedImpulse = Normal(ImpulseDir) * ImpulseMag; if( HitInfo.HitComponent != None ) { HitInfo.HitComponent.AddImpulse( AppliedImpulse, HitLocation, HitInfo.BoneName ); } else { // if no HitComponent is passed, default to our CollisionComponent CollisionComponent.AddImpulse( AppliedImpulse, HitLocation ); } } /** * Default behaviour when shot is to apply an impulse and kick the KActor. */ event TakeDamage(int Damage, Controller EventInstigator, vector HitLocation, vector Momentum, class DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser) { // 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 KActor.TakeDamage"); return; } ApplyImpulse(Momentum, DamageType.default.KDamageImpulse, HitLocation, HitInfo, DamageType); } } /** * Respond to radial damage as well. */ simulated function TakeRadiusDamage ( Controller InstigatedBy, float BaseDamage, float DamageRadius, class DamageType, float Momentum, vector HurtOrigin, bool bFullDamage, Actor DamageCauser, optional float DamageFalloffExponent=1.f, optional bool bAdjustRadiusDamage=true ) { local int Idx; local SeqEvent_TakeDamage DmgEvt; // search for any damage events for (Idx = 0; Idx < GeneratedEvents.Length; Idx++) { DmgEvt = SeqEvent_TakeDamage(GeneratedEvents[Idx]); if (DmgEvt != None) { // notify the event of the damage received DmgEvt.HandleDamage(self, InstigatedBy, DamageType, BaseDamage); } } if ( bDamageAppliesImpulse && damageType.default.RadialDamageImpulse > 0 && (Role == ROLE_Authority) ) { CollisionComponent.AddRadialImpulse(HurtOrigin, DamageRadius, damageType.default.RadialDamageImpulse, RIF_Linear, damageType.default.bRadialDamageVelChange); } } /** If this KActor receives a Toggle ON event from Kismet, wake the physics up. */ simulated function OnToggle(SeqAct_Toggle action) { if(action.InputLinks[0].bHasImpulse) { StaticMeshComponent.WakeRigidBody(); } } /** * Called upon receiving a SeqAct_Teleport action. Grabs * the first destination available and attempts to teleport * this actor. * * @param inAction - teleport action that was activated */ simulated function OnTeleport(SeqAct_Teleport inAction) { local array objVars; local int idx; local Actor destActor; // find the first supplied actor inAction.GetObjectVars(objVars,"Destination"); for (idx = 0; idx < objVars.Length && destActor == None; idx++) { destActor = Actor(objVars[idx]); } // and set to that actor's location if (destActor != None) { StaticMeshComponent.SetRBPosition(destActor.Location); StaticMeshComponent.SetRBRotation(destActor.Rotation); PlayTeleportEffect(false, true); } } simulated function Reset() { `if (`__TW_) if ( bDeleteMe ) { return; } `endif StaticMeshComponent.SetRBLinearVelocity( Vect(0,0,0) ); StaticMeshComponent.SetRBAngularVelocity( Vect(0,0,0) ); StaticMeshComponent.SetRBPosition( InitialLocation ); StaticMeshComponent.SetRBRotation( InitialRotation ); if (!bWakeOnLevelStart) { StaticMeshComponent.PutRigidBodyToSleep(); } else { StaticMeshComponent.WakeRigidBody(); } // Resolve the RBState and get all of the needed flags set ResolveRBState(); // Force replication bForceNetUpdate = TRUE; super.Reset(); } defaultproperties { TickGroup=TG_PostAsyncWork SupportedEvents.Add(class'SeqEvent_RigidBodyCollision') Begin Object Name=StaticMeshComponent0 WireframeColor=(R=0,G=255,B=128,A=255) BlockRigidBody=true RBChannel=RBCC_GameplayPhysics RBCollideWithChannels=(Default=TRUE,BlockingVolume=TRUE,GameplayPhysics=TRUE,EffectPhysics=TRUE) bBlockFootPlacement=false End Object bDamageAppliesImpulse=true bNetInitialRotation=true Physics=PHYS_RigidBody bStatic=false bCollideWorld=false bProjTarget=true bBlockActors=true bWorldGeometry=false bNoDelete=true bAlwaysRelevant=true bSkipActorPropertyReplication=false bUpdateSimulatedPosition=true bReplicateMovement=true RemoteRole=ROLE_SimulatedProxy bCollideActors=true bNoEncroachCheck=true bBlocksTeleport=true bBlocksNavigation=true bPawnCanBaseOn=false bSafeBaseIfAsleep=TRUE bNeedsRBStateReplication=true StayUprightTorqueFactor=1000.0 StayUprightMaxTorque=1500.0 MaxPhysicsVelocity=350.0 ReplicatedDrawScale3D=(X=1000.0,Y=1000.0,Z=1000.0) // set so that default scale of (1,1,1) doesn't replicate anything bDisableClientSidePawnInteractions=TRUE }