/** * Version of KActor that can be dynamically spawned and destroyed during gameplay * * Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. **/ class KActorFromStatic extends KActor native(Physics) notplaceable transient; /** Actor from whome this static mesh came */ var Actor MyStaticMeshActor; /** Max speed from an impulse */ var float MaxImpulseSpeed; cpptext { virtual void NotifyBump(AActor *Other, UPrimitiveComponent* OtherComp, const FVector &HitNormal); virtual UBOOL IgnoreBlockingBy( const AActor *Other ) const; #if __TW_PHYSICS_ virtual UBOOL ShouldTrace(UPrimitiveComponent* Primitive,AActor *SourceActor, DWORD TraceFlags); #endif } /** Disable precomputed lighting when become dynamic */ final native function DisablePrecomputedLighting(); auto state Initializing { /** Delay disabling lightmaps by one tick to avoid lighting pop */ event Tick(float DeltaTime) { DisablePreComputedLighting(); GotoState(''); } } /** * A little while after it goes to sleep, make the static mesh static again */ event OnSleepRBPhysics() { `if(`__TW_PERFORMANCE_) // Skip timer (unnecessary with our lighting) and go straight to sleep MakeStatic(); Destroy(); return; `endif BecomeStatic(); } /** * Cancel becoming static */ event OnWakeRBPhysics() { ClearTimer('BecomeStatic'); } /** If still asleep, make static */ function BecomeStatic() { if ( !WorldInfo.bDropDetail && (WorldInfo.TimeSeconds - LastRenderTime < 0.15) ) { // wait till not rendered to avoid having lighting pop be visible SetTimer(1.0, false, 'BecomeStatic'); return; } // if woke up again, don't make static if ( StaticMeshComponent.RigidBodyIsAwake() ) { return; } MakeStatic(); Destroy(); } /** * Move the StaticMeshComponent back to its original actor owner (MyStaticMeshActor), and fix its position again. */ static native function MakeStatic(); /** * @PARAM MovableMesh: Move this StaticMeshComponent to a KActorFromStatic * @RETURNS a KActorSpawnable actor with MovableMesh as its mesh (removing MovableMesh from its current owner) */ static native function KActorFromStatic MakeDynamic(StaticMeshComponent MovableMesh); /** * Figure out velocity change here so we can bound it. */ event ApplyImpulse( Vector ImpulseDir, float ImpulseMag, Vector HitLocation, optional TraceHitInfo HitInfo, optional class DamageType ) { local float BodyMass; BodyMass = StaticMeshComponent.BodyInstance.GetBodyMass(); if ( (BodyMass > 0.0) && ((DamageType == None) || !DamageType.default.bRadialDamageVelChange) ) { if ( BodyMass < 1.0 ) { BodyMass = Sqrt(BodyMass); } ImpulseMag = FMin(ImpulseMag/BodyMass, MaxImpulseSpeed); } CollisionComponent.AddImpulse( Normal(ImpulseDir) * ImpulseMag, HitLocation,, TRUE ); `if(`__TW_PERFORMANCE_) // reset the default lifespan everytime a new impulse is applied LifeSpan = default.LifeSpan; `endif } /** * Bumped or touched by pawn, so take impulse from it. */ function ReceiveImpulse(Pawn Other, Vector HitLocation, Vector HitNormal) { local vector HitDir; local float ImpulseMag; HitDir = Location - HitLocation; HitDir.Z = FMax(HitDir.Z, 0.0); HitDir = Normal(HitDir); ImpulseMag = FMax( 0.5*Other.GroundSpeed, ((Other.Velocity - Velocity) dot HitDir)); `if(`__TW_PHYSICS_) // apply a factor of pawn mass to get momentum ImpulseMag *= (Other.Mass / 75.f) * 5.0; `endif ApplyImpulse(HitDir, ImpulseMag, Location); } /** * Bump event is only called for KActorFromStatic if Other is a Pawn */ event Bump( Actor Other, PrimitiveComponent OtherComp, Vector HitNormal ) { ReceiveImpulse(Pawn(Other), Other.Location, HitNormal); } /** * Pawns will Touch rather than Bump */ event Touch( Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal ) { if ( Pawn(Other) != None ) { ReceiveImpulse(Pawn(Other), HitLocation, HitNormal); } } /** * 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 ) { 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) ) { ApplyImpulse(Location-HurtOrigin, DamageType.default.RadialDamageImpulse, Location,, DamageType); } } `if(`__TW_PHYSICS_) simulated function Reset(); `endif `if(`__TW_PERFORMANCE_) simulated event Destroyed() { Super.Destroyed(); if ( bRigidBodyWasAwake ) { `log(self@"destroyed for not going to sleep. Lost static actor"@MyStaticMeshActor); } } `endif defaultproperties { RemoteRole=ROLE_None bNoDelete=false bCallRigidBodyWakeEvents=true MaxImpulseSpeed=900.0 bCanStepUpOn=false CollisionComponent=None StaticMeshComponent=None Components.Remove(StaticMeshComponent0) LightEnvironment=None Components.Remove(MyLightEnvironment) `if(`__TW_PERFORMANCE_) // If this KActor is having trouble going to sleep delete it (static will be lost!) LifeSpan=15.f `endif }