//============================================================================= // KFProj_Bullet_ParasiteImplanterAlt //============================================================================= //============================================================================= // Killing Floor 2 // Copyright (C) 2021 Tripwire Interactive LLC //============================================================================= class KFProj_Bullet_ParasiteImplanterAlt extends KFProjectile; var float FuseTime; /** This is the effect indicator that is played for the current user **/ var(Projectile) ParticleSystem ProjIndicatorTemplate; var ParticleSystemComponent ProjIndicatorEffects; var bool IndicatorActive; var transient bool bShouldEnableIndicator; var Class HumanExplosionActorClass; var KFGameExplosion HumanExplosionTemplate; var private AKEvent SeedTimerOutEvent; simulated function TryActivateIndicator() { if(!IndicatorActive && Instigator != None) { IndicatorActive = true; if(WorldInfo.NetMode == NM_Standalone || Instigator.Role == Role_AutonomousProxy || (Instigator.Role == ROLE_Authority && WorldInfo.NetMode == NM_ListenServer && Instigator.IsLocallyControlled() )) { if( ProjIndicatorTemplate != None ) { ProjIndicatorEffects = WorldInfo.MyEmitterPool.SpawnEmitterCustomLifetime(ProjIndicatorTemplate); } if(ProjIndicatorEffects != None) { ProjIndicatorEffects.SetAbsolute(false, false, false); ProjIndicatorEffects.SetLODLevel(WorldInfo.bDropDetail ? 1 : 0); ProjIndicatorEffects.bUpdateComponentInTick = true; AttachComponent(ProjIndicatorEffects); } } } } /** * Set the initial velocity and cook time */ simulated event PostBeginPlay() { Super.PostBeginPlay(); if (Role == ROLE_Authority) { SetTimer(FuseTime, false, 'Timer_Detonate'); } AdjustCanDisintigrate(); } /** * Explode after a certain amount of time */ function Timer_Detonate() { Detonate(); } /** Called when the owning instigator controller has left a game */ simulated function OnInstigatorControllerLeft() { if( WorldInfo.NetMode != NM_Client ) { SetTimer( 1.f + Rand(5) + fRand(), false, nameOf(Timer_Detonate) ); } } /** * Trace down and get the location to spawn the explosion effects and decal */ simulated function GetExplodeEffectLocation(out vector HitLocation, out vector HitRotation, out Actor HitActor) { local vector EffectStartTrace, EffectEndTrace; local TraceHitInfo HitInfo; EffectStartTrace = Location + vect(0,0,1) * 4.f; EffectEndTrace = EffectStartTrace - vect(0,0,1) * 32.f; // Find where to put the decal HitActor = Trace(HitLocation, HitRotation, EffectEndTrace, EffectStartTrace, false,, HitInfo, TRACEFLAG_Bullet); // 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); } } /** Used to check current status of StuckTo actor (to figure out if we should fall) */ simulated event Tick(float DeltaTime) { super.Tick(DeltaTime); StickHelper.Tick(DeltaTime); if (!IsZero(Velocity)) { SetRelativeRotation(rotator(Velocity)); } if (bShouldEnableIndicator) { TryActivateIndicator(); } } simulated function Explode(vector HitLocation, vector HitNormal) { StickHelper.UnPin(); super.Explode(HitLocation, HitNormal); } simulated function SyncOriginalLocation() { local Actor HitActor; local vector HitLocation, HitNormal; local TraceHitInfo HitInfo; if (Role < ROLE_Authority && Instigator != none && Instigator.IsLocallyControlled()) { HitActor = Trace(HitLocation, HitNormal, OriginalLocation, Location,,, HitInfo, TRACEFLAG_Bullet); if (HitActor != none) { StickHelper.TryStick(HitNormal, HitLocation, HitActor); } } } simulated protected function StopSimulating() { super.StopSimulating(); if (ProjIndicatorEffects!=None) { ProjIndicatorEffects.DeactivateSystem(); } } /** Causes charge to explode */ function Detonate() { local KFPawn_Monster KFPM; //@Todo: Some VFX? if (StuckToActor != none) { KFPM = KFPawn_Monster(StuckToActor); if (KFPM != none) { KFPM.ParasiteSeeds.RemoveItem(self); } } StickHelper.UnPin(); ShutDown(); } simulated function OnActorSticked(Actor TargetActor) { local KFPawn_Monster KFPM; KFPM = KFPawn_Monster(TargetActor); if (KFPM != none) { if (Role == ROLE_Authority) { KFPM.AddParasiteSeed(self); PlaySoundBase(SeedTimerOutEvent, false, false); } if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_StandAlone) { bShouldEnableIndicator = true; } } } simulated function ExplodeOnHuman(Vector HitLocation, Vector HitNormal, Actor HitActor) { local vector NudgedHitLocation, ExplosionDirection; if (HumanExplosionTemplate != None) { StopSimulating(); // using a hit location slightly away from the impact point is nice for certain things NudgedHitLocation = HitLocation + (HitNormal * 32.f); ExplosionActor = Spawn(HumanExplosionActorClass, self,, NudgedHitLocation, rotator(HitNormal)); if (ExplosionActor != None) { ExplosionActor.Instigator = Instigator; ExplosionActor.InstigatorController = InstigatorController; PrepareExplosionTemplate(); // If the locations are zero (probably because this exploded in the air) set defaults if( IsZero(HitLocation) ) { HitLocation = Location; } if( IsZero(HitNormal) ) { HitNormal = vect(0,0,1); } // these are needed for the decal tracing later in GameExplosionActor.Explode() HumanExplosionTemplate.HitLocation = HitLocation;// NudgedHitLocation HumanExplosionTemplate.HitNormal = HitNormal; // If desired, attach to mover if we hit one if(bAttachExplosionToHitMover && InterpActor(HitActor) != None) { ExplosionActor.Attachee = HitActor; HumanExplosionTemplate.bAttachExplosionEmitterToAttachee = TRUE; ExplosionActor.SetBase(HitActor); } // directional? if (HumanExplosionTemplate.bDirectionalExplosion) { ExplosionDirection = GetExplosionDirection(HitNormal); //DrawDebugLine(ExplosionActor.Location, ExplosionActor.Location+ExplosionDirection*64, 255, 255, 0, TRUE); } // @todo: make this function responsible for setting explosion instance parameters, and take instance parameters // out of GearExplosion (e.g. Attachee) PrepareExplosionActor(ExplosionActor); ExplosionActor.Explode(HumanExplosionTemplate, ExplosionDirection); // go bewm } // done with it if (!bPendingDelete && !bDeleteMe) { // defer destruction so any replication of explosion stuff can happen if necessary DeferredDestroy(PostExplosionLifetime); } } } simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal) { if (KFPawn_Human(Other) != none && Other != Instigator) { ExplodeOnHuman(HitLocation, HitNormal, Other); } else { super.ProcessTouch(Other, HitLocation, HitNormal); } } defaultproperties { ProjFlightTemplate=ParticleSystem'WEP_ParasiteImplanter_EMIT.FX_Seed_Projectile' ProjIndicatorTemplate=ParticleSystem'WEP_ParasiteImplanter_EMIT.FX_Seed_Projectile_Indicator' bWarnAIWhenFired=true MaxSpeed=15000.0 //10000.0 //4000.0 Speed=15000.0 //10000.0 //4000.0 TerminalVelocity=15000.0 //10000.0 //4000.0 Physics=PHYS_Falling TossZ=0 //150 GravityScale=0 //0.01 //0.7 GlassShatterType=FMGS_ShatterAll bCollideComplex=true bIgnoreFoliageTouch=true bBlockedByInstigator=false bAlwaysReplicateExplosion=true FuseTime=6.0 //4.0 bNetTemporary=false NetPriority=5 NetUpdateFrequency=200 bNoReplicationToInstigator=false bUseClientSideHitDetection=true bUpdateSimulatedPosition=true bSyncToOriginalLocation=true bSyncToThirdPersonMuzzleLocation=true PinBoneIdx=INDEX_None bCanBeDamaged=true bCanDisintegrate=true ExplosionActorClass=class'KFExplosion_ParasiteSeed' HumanExplosionActorClass=class'KFExplosion_ParasiteSeedHuman' // explosion Begin Object Class=KFGameExplosion Name=ExploTemplate0 Damage=400 //250 DamageRadius=450 DamageFalloffExponent=0.5f //1.f DamageDelay=0.f MyDamageType=class'KFDT_Toxic_ParasiteSeedExplosion' // Damage Effects KnockDownStrength=0 KnockDownRadius=0 FractureMeshRadius=0 FracturePartVel=0 // ExplosionEffects=KFImpactEffectInfo'FX_Impacts_ARCH.Explosions.MedicGrenade_Explosion' ExplosionSound=AkEvent'WW_WEP_Medic_GrenadeLauncher.Play_WEP_Medic_GrenadeLauncher_Grenade_Explosion' MomentumTransferScale=0 // Camera Shake CamShake=none CamShakeInnerRadius=0 CamShakeOuterRadius=0 CamShakeFalloff=1.5f bOrientCameraShakeTowardsEpicenter=true End Object ExplosionTemplate=ExploTemplate0 Begin Object Class=KFGameExplosion Name=ExploTemplate1 Damage=1 //250 DamageRadius=100 //450 DamageFalloffExponent=0.5f //1.f DamageDelay=0.f // Damage Effects KnockDownStrength=0 KnockDownRadius=0 FractureMeshRadius=0 FracturePartVel=0 // ExplosionEffects=KFImpactEffectInfo'FX_Impacts_ARCH.Explosions.MedicGrenade_Explosion' ExplosionSound=AkEvent'WW_WEP_Medic_GrenadeLauncher.Play_WEP_Medic_GrenadeLauncher_Grenade_Explosion' MomentumTransferScale=0 // Camera Shake CamShake=none CamShakeInnerRadius=0 CamShakeOuterRadius=0 CamShakeFalloff=1.5f bOrientCameraShakeTowardsEpicenter=true End Object HumanExplosionTemplate=ExploTemplate1 bCanStick=true bCanPin=true Begin Object Class=KFProjectileStickHelper_ParasiteImplanter Name=StickHelper0 End Object StickHelper=StickHelper0 ProjDisintegrateTemplate=ParticleSystem'ZED_Siren_EMIT.FX_Siren_grenade_disable_01' bShouldEnableIndicator=false; ImpactEffects = KFImpactEffectInfo'WEP_ParasiteImplanter_ARCH.Wep_Parasite_Impact_Alt' SeedTimerOutEvent = AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_Seed_Timer_Out'; }