//============================================================================= // KFSprayActor //============================================================================= // Based on GOW2-3 FlameSpray tech //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC //============================================================================= class KFSprayActor extends Actor placeable native(Effect) config(Game); /********************************************************************************************* * Spray SkeltalMesh ********************************************************************************************* */ /** Editable SkeletalMesh set by the archetype */ var(SprayMesh) const SkeletalMesh SkelMesh; /** Anim set to use for the spray anims */ var(SprayMesh) AnimSet SprayAnimSet; /** Anim set to use for the spray anims */ var(SprayMesh) AnimTree SprayAnimTreeTemplate; /** The skeletal mesh that represents the core of the spray effect */ var transient KFSkeletalMeshComponent SkeletalSprayMesh; /** Whether to spawn dynamic lights for the spray actor */ var globalconfig bool bAllowSprayLights; /** Socket to attach the spray actor to */ var(SprayMesh) name SpraySocketName; /********************************************************************************************* * Gravity Scaling ********************************************************************************************* */ /** Scales the gravity to apply to this object. X, Y define ramp-in range. */ var() const vector2D GravityScaleRange; /** How long it takes the gravity to fully ramp in. */ var() const float GravityScaleInTime; /********************************************************************************************* * Per-bone info for the spray's bone chain ********************************************************************************************* */ struct native SprayBoneInfo { var() name BoneName; var int BoneIndex; // transient, filled in at startup var() float MaterialParam; var() float BoneScale; // per-bone effects info var() float EffectScale; /** General Particle System Template. If a first person template exists, it will be used for first person, otherwise this standard template will be used */ var() ParticleSystem ParticleSystemTemplate; /** First Person Particle System Template. If this is set it will override the standard ParticleSystemTemplate in first person */ var() ParticleSystem ParticleSystemTemplate1P; // First person and third person var transient KFParticleSystemComponent BonePSC0; // First person only var transient KFParticleSystemComponent BonePSC1; /** This denotes the location of this bone along the seed stream. Filled in by the skel control when it aligns the bones to the seeds. */ var transient float SeedChainLoc; var transient vector LastLoc; // most recently known location var() float ParticleActivationDelay; }; /** Defines the main bone chain. Should be sorted front to back. */ var(SprayBoneChain) array BoneChain; /** True to spawn the per-bone FX, false to skip it. */ var(SprayBoneChain) bool bDoPerBoneSprayFX; /** Easy parameter for scaling all per-bone fx. */ var(SprayBoneChain) float PerBoneSprayFXGlobalScale; /** True to spawn the first person effects, false to spawn third person effects. */ var bool bDoFirstPersonFX; /********************************************************************************************* * Splash and Impact Effects ********************************************************************************************* */ // Configurable partticle systems /** Effect to play when the flame is deflecting off a surface */ var(SpraySplash) ParticleSystem SplashGlancingEffect; /** Effect to play when the flame hitting a surface directly */ var(SpraySplash) ParticleSystem SplashDirectEffect; /** Effect to play when the flame hitting a pawn */ var(SpraySplash) ParticleSystem SplashPawnEffect; /** Impact info (sfx, vfx, etc.), so that each flame type can have different effects if desired */ var (SpraySplash) KFImpactEffectInfo ImpactEffects; // Internal particle system components var transient KFParticleSystemComponent CurrentSplashEffect; var transient KFParticleSystemComponent SplashGlancingPSC; var transient KFParticleSystemComponent SplashDirectPSC; var transient KFParticleSystemComponent SplashPawnPSC; var transient KFParticleSystemComponent SplashMaterialBasedPSC; /** Looping sound to play when the spray hits a pawn. */ var(SpraySplash) AkEvent SplashPawnAKEvent; /** Looping sound to play when the spray hits direct. */ var(SpraySplash) AkEvent SplashDirectAKEvent; /** Looping sound to play when the spray hits a glancing shot. */ var(SpraySplash) AkEvent SplashGlancingAKEvent; /** Event to play to stop the splash pawn looping sound. */ var(SpraySplash) AkEvent SplashPawnStopAKEvent; /** Event to play to stop the splash direct looping sound. */ var(SpraySplash) AkEvent SplashDirectStopAKEvent; /** Event to play to stop the splash glancing looping sound. */ var(SpraySplash) AkEvent SplashGlancingStopAKEvent; /** Currently playing splash AKEvent sound */ var protected AkEvent CurrentSplashAKEvent; /** Audio Component for playing splash AKEvents */ var protected AkComponent CurrentSplashAKC; var(SpraySplash) float SplashGlancingDotLimit; var(SpraySplash) int LastBoneChainIndexThatCanSpawnSplashEffects; /** The maximum distance in Unreal Units that "scorched" decals will be drawm from this spray actor "burning" or hitting things */ var(SpraySplash) float MaxDecalEffectDistance; /** The maximum distance in Unreal Units that "scorched" decals will be drawm from this spray actor "burning" or hitting things */ var(SpraySplash) float ImpactProjectileInterval; /** Class to spawn at the ImpactProjectileInterval when the spray is impacting something. Used for things like fire on the ground/walls */ var(SpraySplash) class ImpactProjectileClass; /********************************************************************************************* * Splash and Impact Effect Optimization ********************************************************************************************* */ const MAX_SPRAY_FX_INFOS = 20; /** Information about when and where we have spawned impact projectiles */ struct native SprayFxInfo { var vector Location; var float TimeStamp; }; /** used as circular array of infos */ var transient SprayFxInfo SprayFxInfos[MAX_SPRAY_FX_INFOS]; /** used as index into circular array */ var transient int CurrSprayFxLocIdx; /********************************************************************************************* * Splash orientation smoothing ********************************************************************************************* */ var(SpraySplash) float SplashRotInterpSpeed; var(SpraySplash) float SplashLocInterpSpeed; var transient rotator LastSplashRot; var transient vector LastSplashLoc; /********************************************************************************************* * Optional spray lights ********************************************************************************************* */ struct native SprayLight { var() PointLightComponent Light; var() int BoneChainIndex; var() float FlickerIntensity; var() float FlickerInterpSpeed; var() float LastLightBrightness; }; var(SprayLights) array SprayLights; /********************************************************************************************* * Materials * Materials (index 1 & 2) in the mesh have a parameter named "Heat" which will ramp from * MaterialHetRange.X to MaterialHeatRange.Y over MaterialHeatRampTime seconds. ********************************************************************************************* */ var MaterialInstanceConstant MIC_SprayMat0; var MaterialInstanceConstant MIC_SprayMat1; var MaterialInstanceConstant MIC_SprayMat2; var(SprayMaterials) vector2d MaterialHeatRange; var(SprayMaterials) float MaterialHeatRampTime; var(SprayMaterials) float MatFadePow; var(SprayMaterials) float MaterialFadeOutTime; var transient float MaterialCurrentFadeVal; /** This emitter is spawned when firing starts, and is attached to the front bone. */ var(SprayFX) ParticleSystem SprayStartEffect; /** Internal particle system component for the Spray Start Effect. */ var transient KFParticleSystemComponent SprayStartPSC; /** Effect to play when the stopping firing. Functionality to play this must be implemented in the actor that this flame is attached to, since the sprayactor itself gets detached when firing stops. */ var(SprayFX) ParticleSystem SprayEndEffect; /********************************************************************************************* * Damage Handling ********************************************************************************************* */ /** How far the spray spawns should rotate */ var(SprayDamage) int AimAdjustYaw; /** How often to apply spray or splash damage */ var(SprayDamage) float DamageInterval; /** Damage amounts done at DamageInterval. X = damage closest to nozzle, Y = damage at far end of spray */ var(SprayDamage) vector2d SprayDamage; /** Range over which to scale the damage (see SprayDamageSec vars) */ var(SprayDamage) vector2d SprayDamageScaleDistRange; /** Damage type to deliver */ var(SprayDamage) class MyDamageType; /** Radius within which to do splash damage */ var(SprayDamage) float SplashDamageRadius; /** Damage amount to do from splash damage */ var(SprayDamage) float SplashDamage; /** How much to reduce damage as you get farther away from the origin of where splash damage is occuring */ var(SprayDamage) float SplashDamageFalloffExponent; /** What percentage of splash damage should get applied to the instigator */ var(SprayDamage) float SplashDamageInstigatorDamageScale; /** The flame spray can touch/collide/hit teammates */ var(SprayDamage) bool bCollideWithTeammates; /** Scales the momentum for damage taken */ var(SprayDamage) float MomentumScale; /** How much damage will be modified based on current weapon upgrade */ var float DamageModifier; struct native DamagedActorInfo { /** The Actor that was hit */ var Actor HitActor; /** How much incremental damage that has accumulated that is less than a full integer of damage */ var float RemainingDamage; /** When this actor was hit */ var float HitTime; }; /** Stores a list of recently damaged actors. */ var array RecentlyDamagedActors; /** This spray actor is for visuals only, so don't do any damage */ var(SprayDamage) bool bVisualOnly; /********************************************************************************************* * Collision handling ********************************************************************************************* */ struct native SprayMeshContact { /** ChainIndex of the bone that made contact. */ var int BoneChainIndex; var Actor Actor; var vector ContactPosition; var vector ContactNormal; var PhysicalMaterial PhysicalMaterial; var name BoneName; }; /** This is the highest (i.e. in the bone hierarchy) contact of the Spray mesh. */ var private transient SprayMeshContact HighestSprayMeshContactThisTick; var private transient Actor CurrentSprayedActor; var private transient bool bSprayMeshCollidedThisTick; var private transient bool bSprayMeshCollidedLastTick; /** How long the Spray mesh has been touching something */ var transient float bSprayMeshCollisionDuration; /** Enables per-poly collision, instead of simplified. */ var(SprayCollision) bool bDoCollideComplex; /** Will always trace with Extent traces for the flame mesh collision checks. Slower, but will hit the collision cylinder of pawns making them easier to hit */ var(SprayCollision) bool bUseExtentTracing; var(SprayCollision) int MaxSprayMeshesCollided; /********************************************************************************************* * Animation nodes & Skeltal controls ********************************************************************************************* */ var transient AnimNodeBlendList AnimBlendNode; var transient AnimNodeSequence StartSpraySeqNode; var transient AnimNodeSequence EndSpraySeqNode; /** Ref to bone scaling skelcontrol */ var transient KFSkelControl_SprayScaling ScalingSkelControl; /********************************************************************************************* * Spray Seeds * Seeds are spewed like particles from the spray's origin, and the spray skeletal mesh is * fitted to this seed chain. ********************************************************************************************* */ struct native SpraySeed { var vector Location; var vector Velocity; var float Age; }; var transient array Seeds; /** How fast the seeds are traveling at spawn. Higher speeds == stiffer feeling spray. */ var(SpraySeed) float SeedSprayVel; /** Seed deceleration as it travels. Higher deceleration == softer spray at the far end. */ var(SpraySeed) float SeedDecel; /** Seeds expire after this time. Keep as short as is reasonable for memory/efficiency reasons. */ var(SpraySeed) float SeedMaxAge; /** Don't expire any seeds if seed chain length is below this (set this to be longer than the Spray mesh). */ var(SpraySeed) float SeedMinChainLength; /** Fixed-timestep simulation frequency for updating seed chain*/ var(SpraySeed) float SeedSimFreq; /** Accumlator for simulation time, holds leftovers between updates. */ var transient float SeedSimTimeRemaining; var(SpraySeed) float SeedWarmupTime; /** Downward velocity if you want the spray to taper downward over a distance. */ var(SpraySeed) float VelocityScaleZ; /********************************************************************************************* * Instigator Handling ********************************************************************************************* */ /** KFPawn who is firing us, or None if appropriate. Use this instead of Instigator. */ var transient KFPawn OwningKFPawn; /** Actor that is firing when fired from dummy fire */ var transient Actor DummyFireParent; /** Controller of pawn who fired, in case pawn dies */ var transient Controller InstigatorController; /********************************************************************************************* * Miscellaneous ********************************************************************************************* */ var transient bool bWaitingToDestroy; var transient bool bDetached; var transient float CurrentAge; var transient bool bSkeletonHasBeenUpdated; /** How fast the spray is rotating */ var transient float RotationSpeed; /** Rotation last tick. Used to modulate pitch on looping audio. */ var private transient Rotator LastRotation; var transient bool bSplashActive; /********************************************************************************************* * Debugging ********************************************************************************************* */ /** Toggles bone rendering. For debugging. */ var(SprayDebug) bool bDebugShowBones; /** Toggles collision rendering. For debugging. */ var(SprayDebug) bool bDebugShowCollision; /** Toggles rendering of the splash damage radius. For debugging. */ var(SprayDebug) bool bDebugShowSplashRadius; /** True to make player use third person particle systems. */ var(SprayDebug) bool bDebugForceNonPlayerParticles; /** Toggles rendering of Spray seeds. For debugging. */ var(SprayDebug) bool bDebugShowSeeds; /** Log out direct damage information*/ var(SprayDebug) bool bDebugDirectDamage; /** Log out splash damage information*/ var(SprayDebug) bool bDebugSplashDamage; /** Log out light information*/ var(SprayDebug) bool bDebugSprayLights; cpptext { public: virtual FLOAT GetGravityZ(); virtual void TickSpecial( FLOAT DeltaSeconds ); /** Returns the number of hits, if line misses this returns 0, will stop at MaxSprayMeshesCollided */ INT SprayMeshLineCheck(const FVector& End, const FVector& Start, DWORD TraceFlags, const FVector& Extent, TArray& arrActorsHit, INT BoneChainIndex=INDEX_NONE, INT CurrentNumHit=0); }; /********************************************************************************************* * Native functions ********************************************************************************************* */ native final function UpdateSeeds( float DeltaTime ); /** Update Per Bone Fire FX information. */ native simulated function UpdatePerBoneSprayFX( float DeltaTime ); native simulated private final function UpdateSplashes( float DeltaTime ); simulated native private function DestroyIfAllEmittersFinished(); native simulated protected function SetBoneSpawnParticlesActive( ParticleSystemComponent PSC, bool bActive ); /** For debugging. Renders boxes at the bone positions. */ simulated native final function DebugRenderBones(); /** * Calculate actual damage. * Server only. */ native function private float GetDamage(float HitDist, Actor HitActor); event PreBeginPlay() { if ( SkeletalSprayMesh != None ) { if ( SprayAnimSet != None ) { SkeletalSprayMesh.AnimSets[0] = SprayAnimSet; } if ( SkelMesh != none ) { // set the skeletal mesh from our archetype to the WeaponAttachment SkeletalSprayMesh.SkeletalMesh = SkelMesh; } if( SprayAnimTreeTemplate != none ) { // set the skeletal mesh from our archetype to the WeaponAttachment SkeletalSprayMesh.SetAnimTreeTemplate(SprayAnimTreeTemplate); } //WeapAnimNode = AnimNodeSequence(WeapMesh.Animations); } super.PreBeginPlay(); } simulated function PostBeginPlay() { local bool bDebugDelayFX; super.PostBeginPlay(); if( Instigator == none ) { bDebugDelayFX = true; } // find owner pawn, handles boomer special case. OwningKFPawn = FindOwningKFPawn(); if( OwningKFPawn != None) { InstigatorController = OwningKFPawn.Controller; } if( !bDebugDelayFX ) { SetupFX(); } } /** Notification that a direct impact has occurred. */ event ProcessDirectImpact() { local KFPlayerController KFPC; KFPC = KFPlayerController(InstigatorController); if( KFPC != none ) { KFPC.AddShotsHit(1); } } /** * Adjust the FOV when used in first person */ simulated function SetFOV( float NewFOV ) { local int Idx; // make sure fire particles got destroyed for (Idx = 0; Idx < BoneChain.length; ++Idx) { if( BoneChain[Idx].BonePSC0 != None) { BoneChain[Idx].BonePSC0.SetFOV(NewFOV); } if( BoneChain[Idx].BonePSC1 != None) { BoneChain[Idx].BonePSC1.SetFOV(NewFOV); } } SprayStartPSC.SetFOV(NewFOV); if( CurrentSplashEffect != none ) { CurrentSplashEffect.SetFOV(NewFOV); } SplashGlancingPSC.SetFOV(NewFOV); SplashDirectPSC.SetFOV(NewFOV); SplashPawnPSC.SetFOV(NewFOV); SplashMaterialBasedPSC.SetFOV(NewFOV); SprayStartPSC.SetFOV(NewFOV); SkeletalSprayMesh.SetFOV(NewFOV); } simulated function SetupFX() { local int ChainIdx, Idx; CacheAnimNodes(); SetupPerBoneFireFX(); // we want to set splash pos/rot in world space if( SplashDirectPSC != None) { SplashDirectPSC.SetTemplate(SplashDirectEffect); SplashDirectPSC.SetAbsolute( true, true, true ); } if( SplashGlancingPSC != None) { SplashGlancingPSC.SetTemplate(SplashGlancingEffect); SplashGlancingPSC.SetAbsolute( true, true, true ); } if( SplashPawnPSC != None) { SplashPawnPSC.SetTemplate(SplashPawnEffect); SplashPawnPSC.SetAbsolute( true, true, true ); } if( SplashMaterialBasedPSC != None) { SplashMaterialBasedPSC.SetAbsolute( true, true, true ); } // set up material parameters MIC_SprayMat0 = SkeletalSprayMesh.CreateAndSetMaterialInstanceConstant(0); MIC_SprayMat1 = SkeletalSprayMesh.CreateAndSetMaterialInstanceConstant(1); MIC_SprayMat2 = SkeletalSprayMesh.CreateAndSetMaterialInstanceConstant(2); // look up and cache bone indices for (ChainIdx=0; ChainIdx