//============================================================================= // KFWeapAttach_HRG_Vampire //============================================================================= // //============================================================================= // Killing Floor 2 // Copyright (C) 2020 Tripwire Interactive LLC //============================================================================= class KFWeapAttach_HRG_Vampire extends KFWeapAttach_SprayBase; var transient ParticleSystemComponent ChargingPSC; var transient ParticleSystemComponent ChargedPSC; var ParticleSystem ChargingEffect; var ParticleSystem ChargedEffect; var ParticleSystem BloodStolenEffect; var bool bIsCharging; var bool bIsFullyCharged; var bool bIsChargeEffectsActive; var bool bIsBloodParticlesEffectActive; var bool bIsDisabledSprayVisualAndMesh; var repnotify float StartFireTime; var int ChargeLevel; var float FXScalingFactorByCharge; var float ChargeRTPC; var KFPawn_Monster oZedCurrentlyBeingSprayed; var KFPawn_Monster oZedPreviouslyBeingSprayed; var KFWeap_HRG_Vampire Weapon; var KFEmit_DirectionalPath BloodStolenParticles[15]; var int NumBloodStolenParticlesForPool; var transient float BloodStolenControlTime; var float RateUpdateDestinationBloodParticles; var transient float UpdateDestinationBloodParticlesTime; var class BloodSplashClass; var bool bHasToStopDoingFireAnim; var bool bHasToStopDoingFireAnimB; /** Blood Particles */ var(BloodParticles) float SpeedBloodParticles; var(BloodParticles) float SpawnRateBloodParticles; var(BloodParticles) float SpeedBloodParticles_3P; var(BloodParticles) float SpawnRateBloodParticles_3P; var(BloodParticles) float HalfAngleSpawnCone; var(BloodParticles) float CurveTurnRateUntilDestinationFinal; var(BloodParticles) float CurveTurnRateUntilDestinationMidPoint; var(BloodParticles) float LimitDistanceFinalPoint; var(BloodParticles) float LimitDistanceMidPoint; simulated function PostBeginPlay() { local int Index; super.PostBeginPlay(); UpdateDestinationBloodParticlesTime = RateUpdateDestinationBloodParticles; for( Index = (NumBloodStolenParticlesForPool - 1) ; Index >= 0 ; Index-- ) { BloodStolenParticles[Index] = Spawn(class'KFEmit_DirectionalPath'); BloodStolenParticles[Index].DeactivateEmitter(); BloodStolenParticles[Index].SetTemplate(BloodStolenEffect, true); } } simulated function StartFire() { StartFireTime = WorldInfo.TimeSeconds; bIsCharging = false; bIsFullyCharged = false; bIsDisabledSprayVisualAndMesh = false; if (ChargingPSC == none) { ChargingPSC = new(self) class'ParticleSystemComponent'; if (WeapMesh != none) { WeapMesh.AttachComponentToSocket(ChargingPSC, 'MuzzleFlash'); } else { AttachComponent(ChargingPSC); } ChargingPSC.SetTemplate(ChargingEffect); } if (ChargedPSC == none) { ChargedPSC = new(self) class'ParticleSystemComponent'; if (WeapMesh != none) { WeapMesh.AttachComponentToSocket(ChargedPSC, 'MuzzleFlash'); } else { AttachComponent(ChargedPSC); } ChargedPSC.SetTemplate(ChargedEffect); } ChargingPSC.SetActive( false ); ChargedPSC.SetActive( false ); } simulated event Tick(float DeltaTime) { local int Index; local vector MuzzleFlashSocketLocation, BloodParticlesMidPointSocketLocation; local KFEmit_DirectionalPath EmitterToRemove; local vector VectorParameterParticle; local vector ChargePercentageVector; local Rotator DestinationRotation, BloodParticlesMidPointSocketRotation; local vector BloodSplashVelocity; if(bIsChargeEffectsActive) { if(bIsCharging) { ChargingPSC.SetActive( true, true ); ChargePercentageVector.X = ChargeRTPC; ChargePercentageVector.Y = ChargeRTPC; ChargePercentageVector.Z = ChargeRTPC; ChargingPSC.SetVectorParameter( name("BlobCharge"), ChargePercentageVector); } if(bIsFullyCharged) { ChargedPSC.SetActive( true, true ); ChargingPSC.SetFloatParameter( name("InflateBlob"), ChargeRTPC); } } if (bIsDisabledSprayVisualAndMesh) { TurnOffFireSpray(); ActiveFlameSpray.CleanupEndFire(); } //Spawning blood particles if conditions met if (bIsBloodParticlesEffectActive) { if(oZedCurrentlyBeingSprayed != none && oZedCurrentlyBeingSprayed.IsAliveAndWell()) { CreateBloodParticle(DeltaTime, oZedCurrentlyBeingSprayed); } } // Updating blood stolen particles FX destination position UpdateDestinationBloodParticlesTime -= DeltaTime; if (UpdateDestinationBloodParticlesTime <= 0) { UpdateDestinationBloodParticlesTime = RateUpdateDestinationBloodParticles; if( WeapMesh != none ) { GetFlameSocketLocAndRot(MuzzleFlashSocketLocation, DestinationRotation); WeapMesh.GetSocketWorldLocationAndRotation('BloodParticlesMidPoint', BloodParticlesMidPointSocketLocation, BloodParticlesMidPointSocketRotation); } for( Index = (NumBloodStolenParticlesForPool - 1) ; Index >= 0 ; Index-- ) { if(!BloodStolenParticles[Index].IsEnabled || BloodStolenParticles[Index] == None) { continue; } if( BloodStolenParticles[Index].bReachDestinationFinal ) { EmitterToRemove = BloodStolenParticles[Index]; //BloodStolenParticles.Remove(Index, 1); EmitterToRemove.DeactivateEmitter(); } else { if (BloodStolenParticles[Index].ParticleSystemComponent != None) { VectorParameterParticle.X = WorldInfo.TimeDilation; VectorParameterParticle.Y = WorldInfo.TimeDilation; VectorParameterParticle.Z = WorldInfo.TimeDilation; BloodStolenParticles[Index].ParticleSystemComponent.SetVectorParameter( name("ZedtimeScale"), VectorParameterParticle ); } BloodStolenParticles[Index].UpdateDestination( MuzzleFlashSocketLocation, BloodParticlesMidPointSocketLocation ); if (FRand() > 0.47) { BloodSplashVelocity.x = 0; BloodSplashVelocity.y = RandRange(-100, 100); BloodSplashVelocity.z = -200; SpawnBloodSplash(BloodSplashClass, BloodStolenParticles[Index].Location, BloodSplashVelocity); } } } } Super.Tick(DeltaTime); } simulated function FirstPersonFireEffects(Weapon W, vector HitLocation) { super.FirstPersonFireEffects(W, HitLocation); } simulated function bool ThirdPersonFireEffects(vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte ) { local EAnimSlotStance AnimType; local bool bResult; bIsChargeEffectsActive = true; bIsBloodParticlesEffectActive = true; //bResult = Super(KFWeaponAttachment).ThirdPersonFireEffects(HitLocation,P,ThirdPersonAnimRateByte); SpawnTracer(GetMuzzleLocation(), HitLocation); // Effects below this point are culled based on visibility and distance if ( !ActorEffectIsRelevant(P, false, MaxFireEffectDistance) ) { return false; } DecodeThirdPersonAnimRate( ThirdPersonAnimRateByte ); // Weapon shoot anims if( !bWeapMeshIsPawnMesh && bHasToStopDoingFireAnim == false ) { PlayWeaponFireAnim(); bHasToStopDoingFireAnim = true; } if( P.IsDoingSpecialMove() && P.SpecialMoves[P.SpecialMove].bAllowFireAnims ) { AnimType = EAS_Additive; } else { AnimType = EAS_FullBody; } // Character shoot anims if ( !P.IsDoingSpecialMove() || AnimType == EAS_Additive ) { if(!bHasToStopDoingFireAnimB) { bHasToStopDoingFireAnimB = true; PlayPawnFireAnim( P, AnimType ); } // interrupt other weapon action anims (e.g. Reload) if( !P.IsDoingSpecialMove() ) { P.StopBodyAnim(P.bIsCrouched ? EAS_CH_UpperBody : EAS_UpperBody, 0.1f); } if ( OnWeaponStateChanged != None ) { OnWeaponStateChanged(true); } } CauseMuzzleFlash(P.FiringMode); // Wider effect distances for fire spray. Called on Instigator to go beyond the weapon mesh cull distance if ( P.FiringMode != 1 && P.ActorEffectIsRelevant(P, false, 15000, 2000) && !bIsDisabledSprayVisualAndMesh ) { TurnOnFireSpray(); bResult = true; } return bResult; } simulated function StopThirdPersonFireEffects(optional bool bForce) { Super.StopThirdPersonFireEffects(bForce); bIsChargeEffectsActive = false; bIsBloodParticlesEffectActive = false; RemoveAllBloodParticles(); bIsDisabledSprayVisualAndMesh = false; if (ChargingPSC != none) { ChargingPSC.SetActive(false); } if (ChargedPSC != none) { ChargedPSC.SetActive(false); } bHasToStopDoingFireAnim = false; bHasToStopDoingFireAnimB = false; } simulated function CauseMuzzleFlash(byte FiringMode) { if (MuzzleFlash == None && MuzzleFlashTemplate != None) { AttachMuzzleFlash(); } Super.CauseMuzzleFlash(FiringMode); } simulated protected function TurnOnPilot() { Super.TurnOnPilot(); if( FlamePool[0] != None && KFSprayActor_HRG_Vampire(FlamePool[0]) != None && KFSprayActor_HRG_Vampire(FlamePool[0]).OwnerWeapon == None) { KFSprayActor_HRG_Vampire(FlamePool[0]).OwnerWeapon = KFWeap_HRG_Vampire(KFPawn_Human(Instigator).MyKFWeapon); } if( FlamePool[1] != None && KFSprayActor_HRG_Vampire(FlamePool[1]) != None && KFSprayActor_HRG_Vampire(FlamePool[1]).OwnerWeapon == None) { KFSprayActor_HRG_Vampire(FlamePool[1]).OwnerWeapon = KFWeap_HRG_Vampire(KFPawn_Human(Instigator).MyKFWeapon); } } simulated function CreateBloodParticle(float DeltaTime, KFPawn_Monster Monster) { Local KFEmit_DirectionalPath Emitter; local vector DestinationLocation, MonsterLocation, BloodParticlesMidPointSocketLocation; local Rotator DestinationRotation, BloodParticlesMidPointSocketRotation; local vector BloodSplashVelocity; local int Index; if( BloodStolenControlTime > 0 ) { BloodStolenControlTime -= DeltaTime; return; } Emitter = none; for( Index = (NumBloodStolenParticlesForPool - 1) ; Index >= 0 ; Index-- ) { if(BloodStolenParticles[Index].IsEnabled == false) { //`log("I gave the emitter"); Emitter = BloodStolenParticles[Index]; break; } } if(Emitter == none) { //`log("NO EMITTER"); return; } if( WeapMesh != none && WeapMesh.SkeletalMesh != none) { GetFlameSocketLocAndRot(DestinationLocation, DestinationRotation); WeapMesh.GetSocketWorldLocationAndRotation('BloodParticlesMidPoint', BloodParticlesMidPointSocketLocation, BloodParticlesMidPointSocketRotation); } BloodStolenControlTime += SpawnRateBloodParticles_3P; MonsterLocation = ActiveFlameSpray.GetLastContactPositionMeshHit(); if (IsZero(MonsterLocation)) { Monster.Mesh.GetBoneLocation('Spine1'); } if(MonsterLocation == vect(0,0,0)) { MonsterLocation = Monster.Location + vect(0,0,20); } if (FRand() > 0.4) { BloodSplashVelocity = BloodParticlesMidPointSocketLocation - MonsterLocation; BloodSplashVelocity = VRandCone(vect(0,0,-1), PI / 5) * 100; SpawnBloodSplash(BloodSplashClass, MonsterLocation, BloodSplashVelocity); } //Emitter = Spawn(class'KFEmit_DirectionalPath',,, MonsterLocation); Emitter.ParticleSpeed = SpeedBloodParticles_3P; Emitter.RateTickCheckHasReached = 0.2; //BloodStolenParticles.AddItem(Emitter); Emitter.SetLocation(MonsterLocation); Emitter.SetDestination(DestinationLocation, BloodParticlesMidPointSocketLocation, 20, 20, 50, 50, 0); Emitter.ActivateEmitter(); //Emitter.isEnabled = true; } simulated function SetChargePercentage(float ChargePercentage) { ChargeRTPC = ChargePercentage; bIsCharging = false; bIsFullyCharged = false; if (ChargeRTPC >= 0.0) { bIsCharging = true; } if (ChargeRTPC >= 1.0) { bIsFullyCharged = true; } } simulated function SetZedCurrentlyBeingSprayed(KFPawn_Monster ZedCurrentlyBeingSprayed) { oZedPreviouslyBeingSprayed = oZedCurrentlyBeingSprayed; oZedCurrentlyBeingSprayed = ZedCurrentlyBeingSprayed; } simulated function DisableSprayVisualAndMesh() { bIsDisabledSprayVisualAndMesh = true; } function SpawnBloodSplash( class SpawnClass, vector SpawnLoc, vector SpawnVel ) { local TraceHitInfo HitInfo; local vector HitLocation, HitRotation; local KFGoreManager GoreManager; // Grab the gore manager GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager); if (GoreManager == none || oZedCurrentlyBeingSprayed == none) { return; } //EffectStartTrace = Location + vect(0,0,1) * 4.f; //EffectEndTrace = EffectStartTrace - vect(0,0,1) * 32.f; // Find where to put the decal Trace(HitLocation, HitRotation, SpawnLoc + SpawnVel * 32.f, SpawnLoc, false,, HitInfo, TRACEFLAG_Bullet); //DrawDebugLine(SpawnLoc,SpawnLoc + SpawnVel * 32.f,0,255,0,TRUE); // If the locations are zero (probably because this exploded in the air) set defaults if( IsZero(HitLocation) ) { HitLocation = Location; } if( IsZero(HitRotation) ) { HitRotation = vect(0,0,1); } //Put the decals GoreManager.LeaveABloodSplatterDecal(oZedCurrentlyBeingSprayed, HitLocation, HitRotation); //GoreManager. LeaveAPersistentBloodSplat(HitLocation, HitNormal, 1.0); if (oZedCurrentlyBeingSprayed != none) { GoreManager.CausePersistentBlood(oZedCurrentlyBeingSprayed, class'KFDamageType', HitLocation, vect(0,0,-1), 0, false, false); } } simulated function RemoveAllBloodParticles() { local int Index; local KFEmit_DirectionalPath EmitterToRemove; for( Index = (NumBloodStolenParticlesForPool - 1) ; Index >= 0 ; Index-- ) { EmitterToRemove = BloodStolenParticles[Index]; //BloodStolenParticles.Remove(Index, 1); EmitterToRemove.DeactivateEmitter(); } } defaultproperties { ChargeRTPC=0 bIsChargeEffectsActive=false bIsBloodParticlesEffectActive=false bIsDisabledSprayVisualAndMesh=false MuzzleFlashTemplate=KFMuzzleFlash'WEP_HRG_Vampire_Arch.Wep_HRG_Vampire_MuzzleFlash_3P' ChargingEffect=ParticleSystem'WEP_HRG_Vampire_EMIT.FX_HRG_Vampire_BlobCharge_3P_01' ChargedEffect=ParticleSystem'WEP_HRG_Vampire_EMIT.FX_HRG_Vampire_FullCharge' BloodStolenEffect=ParticleSystem'WEP_HRG_Vampire_EMIT.FX_HRG_Vampire_BloodStolen_3P' BloodSplashClass=KFProj_BloodSplash RateUpdateDestinationBloodParticles = 0.7 NumBloodStolenParticlesForPool = 15; }