//============================================================================= // KFProj_Mine_Reconstructor //============================================================================= // Projectile class for Mine Reconstructor //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC //============================================================================= class KFProj_Mine_Reconstructor extends KFProjectile; /** Steepest a slope can be for the mine to stick */ const MINMINEFLOORZ = 0.8f; /** Ground FX template */ var ParticleSystem GroundFXTemplate; /** Burst FX template */ var ParticleSystem BurstFXTemplate; /** Cached team number, because players can switch teams */ var byte TeamNum; /** How much damage the mine can take before exploding */ var int Health; /** Dampen amount for every bounce */ var float DampenFactor; /** Dampen amount for parallel angle to velocity */ var float DampenFactorParallel; /** Vector to offset the ground FX particle system by when landing */ var vector LandedFXOffset; /** Armed mine collision settings */ var float ExplodeTriggerRadius, ExplodeTriggerHeight; /** Sound mine makes when it hits something and bounces off */ var AkEvent BounceAkEvent; /** Sound mine makes when it hits something and comes to rest */ var AkEvent ImpactAkEvent; /** Sound mine makes when it hits something and bounces off */ var AkEvent BounceAkEventHeavy; /** Sound mine makes when it hits something and comes to rest */ var AkEvent ImpactAkEventHeavy; /** Decal settings */ var MaterialInterface ImpactDecalMaterial; var float ImpactDecalWidth; var float ImpactDecalHeight; var float ImpactDecalThickness; /** How long the mine can exist before exploding */ var float FuseDuration; /** Tells clients to trigger an explosion */ var repnotify bool bClientExplode; /** Whether we've triggered a fade out */ var bool bFadingOut; /** How long it should take to fade out this mine */ var float FadeOutTime; /** Maximum number of times this mine can bounce */ var const int MaxBounces; var int NumBounces; var float MaxExplodeTriggerRadiusPerPercentage; var float MinExplodeTriggerRadiusPerPercentage; var float MaxExplodeTriggerHeightPerPercentage; var float MinExplodeTriggerHeightPerPercentage; var float MaxDamageRadiusPerPercentage; var float MinDamageRadiusPerPercentage; var float MaxCollisionRadius; var float MinCollisionRadius; var float MaxCollisionHeight; var float MinCollisionHeight; var float MaxDamagePerPercentage; var float MinDamagePerPercentage; var repnotify float InheritedScale; var repnotify float fChargePercentage; var float fCachedCylinderWidth, fCachedCylinderHeight; //EXPERIMENTAL FEATURES var bool bCantBeTouchedByInstigator; var bool bManuallyDetonated; var bool bCantDetonateOnFullHP; var bool bSticked; var repnotify bool bArmed; struct ReplicatedStickInfo { var vector vRepStuckLocation, vRepStuckNormal; }; var repnotify ReplicatedStickInfo StickInfo; replication { if( bNetDirty && Role == Role_Authority) bClientExplode, InheritedScale, fChargePercentage, StickInfo; if(bNetInitial && Role == Role_Authority) bArmed; } simulated event ReplicatedEvent( name VarName ) { if( VarName == nameOf(bClientExplode) ) { TriggerExplosion( Location, vect(0,0,1), none ); } else { super.ReplicatedEvent( VarName ); } if( VarName == nameOf(fChargePercentage)) { fCachedCylinderWidth =Lerp(MinCollisionRadius, MaxCollisionRadius, fChargePercentage); fCachedCylinderHeight = Lerp(MinCollisionHeight, MaxCollisionHeight, fChargePercentage); CylinderComponent(CollisionComponent).SetCylinderSize(fCachedCylinderWidth, fCachedCylinderHeight); ExplodeTriggerRadius = Lerp(MinExplodeTriggerRadiusPerPercentage, MaxExplodeTriggerRadiusPerPercentage, fChargePercentage); ExplodeTriggerHeight = Lerp(MinExplodeTriggerHeightPerPercentage, MaxExplodeTriggerHeightPerPercentage, fChargePercentage); ExplosionTemplate.DamageRadius = Lerp(MinDamageRadiusPerPercentage, MaxDamageRadiusPerPercentage, fChargePercentage); ExplosionTemplate.Damage = Lerp(MinDamagePerPercentage, MaxDamagePerPercentage, fChargePercentage); } if(VarName == nameOf(StickInfo)) { StickInClient(StickInfo.vRepStuckLocation, StickInfo.vRepStuckNormal); } if( VarName == nameOf(bArmed) ) { if(bArmed) { SetPhysics( PHYS_Falling ); } } } simulated function SetInheritedScale(float Scale, float ChargePercentage) { InheritedScale=Scale; fChargePercentage=ChargePercentage; fChargePercentage = FMax(0.1, fChargePercentage); //ProjEffects.SetScale( InheritedScale ); CylinderComponent(CollisionComponent).SetCylinderSize(Lerp(MinCollisionRadius, MaxCollisionRadius, fChargePercentage), Lerp(MinCollisionHeight, MaxCollisionHeight, ChargePercentage)); ExplodeTriggerRadius = Lerp(MinExplodeTriggerRadiusPerPercentage, MaxExplodeTriggerRadiusPerPercentage, fChargePercentage); ExplodeTriggerHeight = Lerp(MinExplodeTriggerHeightPerPercentage, MaxExplodeTriggerHeightPerPercentage, fChargePercentage); ExplosionTemplate.DamageRadius = Lerp(MinDamageRadiusPerPercentage, MaxDamageRadiusPerPercentage, fChargePercentage); ExplosionTemplate.Damage = Lerp(MinDamagePerPercentage, MaxDamagePerPercentage, fChargePercentage); ExplodeTriggerRadius = FMax(0.1, ExplodeTriggerRadius); ExplodeTriggerHeight = FMax(0.1, ExplodeTriggerHeight); ExplosionTemplate.DamageRadius = FMax(0.1, ExplosionTemplate.DamageRadius); ExplosionTemplate.Damage = FMax(0.1, ExplosionTemplate.Damage); bNetDirty=true; } /** Adds our puke mine to the pool */ simulated event PostBeginPlay() { // Cache team num TeamNum = GetTeamNum(); super.PostBeginPlay(); if( WorldInfo.NetMode != NM_Client ) { if( InstigatorController != none || IsAIProjectile() ) { //class'KFGameplayPoolManager'.static.GetPoolManager().AddProjectileToPool( self, PPT_MineReconstructor ); } else { //Destroy(); //return; } } if( Role == ROLE_Authority ) { SetTimer( FuseDuration, false, nameOf(Timer_Explode) ); } } simulated function SpawnFlightEffects() { local vector vec; vec.X = fChargePercentage; vec.Y = fChargePercentage; vec.Z = fChargePercentage; super.SpawnFlightEffects(); //ProjEffects.SetScale( InheritedScale ); if(ProjEffects != None) { ProjEffects.SetVectorParameter( name("BlobCharge"), vec); ProjEffects.SetFloatParameter( name("MineFxControlParam"), fChargePercentage); } } /** Overridden to do nothing */ simulated function Shutdown() {} simulated protected function StopSimulating() {} simulated function Explode( vector HitLocation, vector HitNormal ) { super.Explode(HitLocation, HitNormal); } /** Give a little bounce */ simulated event HitWall( vector HitNormal, Actor Wall, PrimitiveComponent WallComp ) { // Don't collide with other puke mines if( Wall.class == class ) { return; } // Stick to static walls and destructible environment objects. if( CanStick(Wall, HitNormal) && (Wall.bStatic == true || (Wall.bCanBeDamaged && KFPawn(Wall) == none)) ) { Stick( Location, HitNormal ); } else { Bounce( HitNormal, Wall ); } } simulated protected function bool CanStick( Actor HitActor, out vector HitNormal ) { // Basic slope check if( HitNormal.Z < MINMINEFLOORZ ) { return false; } return true; } simulated event Bump( Actor Other, PrimitiveComponent OtherComp, Vector HitNormal ) { if( Other.bWorldGeometry || Other.bStatic ) { HitWall( HitNormal, Other, OtherComp ); } } function RestoreCylinder() { CylinderComponent(CollisionComponent).SetCylinderSize(fCachedCylinderWidth, fCachedCylinderHeight); } /** Adjusts movement/physics of projectile. * Returns true if projectile actually bounced / was allowed to bounce */ simulated function bool Bounce( vector HitNormal, Actor BouncedOff ) { local vector VNorm; // Avoid crazy bouncing if(CheckRepeatingTouch(BouncedOff)) { CylinderComponent(CollisionComponent).SetCylinderSize(1,1); SetTimer(0.06f, false, nameof(RestoreCylinder)); return false; } /*if(HitNormal.Z > 0.9f || HitNormal.Z < -0.9f) { HitNormal = -Velocity; if(HitNormal.Z > 0) { HitNormal.Z = -HitNormal.Z; HitNormal=Normal(HitNormal); } else { HitNormal=Normal(HitNormal); } }*/ // Reflect off BouncedOff w/damping VNorm = (Velocity dot HitNormal) * HitNormal; Velocity = -VNorm * DampenFactor + (Velocity - VNorm) * DampenFactorParallel; Speed = VSize(Velocity); if( Speed < 60 ) { Speed = 200; //Stick( Location, HitNormal ); //return false; } // Play a sound PlayImpactSound(); return true; } simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal) { if (Other != Instigator && Other.GetTeamNum() != GetTeamNum() && !IsInState('Armed')) { ProcessBulletTouch(Other, HitLocation, HitNormal); } if (bCanStick || bCanPin) { StickHelper.TryStick(HitNormal, HitLocation, Other); return; } if (ExplosionTemplate != None && Role == ROLE_Authority) { TriggerExplosion(HitLocation, HitNormal, Other); } Shutdown(); // cleanup/destroy projectile } /** Called once the mine has finished moving */ simulated function Stick( vector StuckLocation, vector StuckNormal ) { local vector HitLocation, HitNormal; local rotator StuckRotation; local KFProj_Mine_Reconstructor PukeMine; local rotator RandRot; if( Role != ROLE_Authority ) { return; } //we added a scapegoat that fixes the problem of hitwall not calling always in client, so we need to check it in case it comes //to not call it twice if(bSticked == true) { return; } RotationRate = rot(0,0,0); // Modify the collision so it can be detonated by the player CylinderComponent.SetTraceBlocking( true, true ); SetCollisionSize( ExplodeTriggerRadius, ExplodeTriggerHeight ); CylinderComponent.SetActorCollision( true, false ); bCollideComplex = false; bBounce = false; SetPhysics( PHYS_None ); // Optimize for network NetUpdateFrequency = 10; bOnlyDirtyReplication = true; bForceNetUpdate = true; // Set rotation Trace( HitLocation, HitNormal, Location - vect(0,0,50), Location + vect(0,0,5), false,,, TRACEFLAG_Bullet ); if( !IsZero(HitLocation) ) { StuckRotation = rotator( HitNormal ); } else { StuckRotation = rotator( StuckNormal ); } // Destroy any overlapping mines if( Role == ROLE_Authority ) { foreach TouchingActors( class'KFProj_Mine_Reconstructor', PukeMine ) { PukeMine.TriggerExplosion( PukeMine.Location, vect(0,0,1), none ); } } SetRotation( StuckRotation ); // Apply some random yaw RandRot.Yaw = Rand( 65535 ); SetRelativeRotation( RandRot ); // Swap to ground FX SwapToGroundFX( StuckRotation ); // Play a sound PlayImpactSound( true ); // Spawn an impact decal SpawnImpactDecal( StuckLocation, StuckNormal ); // Go to armed state GotoState( 'Armed' ); StickInfo.vRepStuckNormal=StuckNormal; StickInfo.vRepStuckLocation=StuckLocation; bNetDirty = true; bSticked = true; bArmed = true; } reliable client function StickInClient(vector StuckLocation, vector StuckNormal) { local vector HitLocation, HitNormal; local rotator StuckRotation; local rotator RandRot; //we added a scapegoat that fixes the problem of hitwall not calling always in client, so we need to check it in case it comes //to not call it twice if(bSticked == true) { return; } RotationRate = rot(0,0,0); SetLocation(StuckLocation); // Modify the collision so it can be detonated by the player CylinderComponent.SetTraceBlocking( true, true ); SetCollisionSize( ExplodeTriggerRadius, ExplodeTriggerHeight ); CylinderComponent.SetActorCollision( true, false ); bCollideComplex = false; bBounce = false; // Optimize for network NetUpdateFrequency = 0.25f; bOnlyDirtyReplication = true; bForceNetUpdate = true; // Set rotation Trace( HitLocation, HitNormal, Location - vect(0,0,50), Location + vect(0,0,5), false,,, TRACEFLAG_Bullet ); if( !IsZero(HitLocation) ) { StuckRotation = rotator( HitNormal ); } else { StuckRotation = rotator( StuckNormal ); } SetRotation( StuckRotation ); // Apply some random yaw RandRot.Yaw = Rand( 65535 ); SetRelativeRotation( RandRot ); // Swap to ground FX SwapToGroundFX( StuckRotation ); // Play a sound PlayImpactSound( true ); // Spawn an impact decal SpawnImpactDecal( StuckLocation, StuckNormal ); // Go to armed state GotoState( 'Armed' ); bSticked = true; bArmed = true; } /** Switch to ground fx */ simulated function SwapToGroundFX( rotator GroundRotation ) { local vector vec; // Replace the flying effects with ground FX if(/* WorldInfo.NetMode != NM_DedicatedServer && */!IsInState('Armed') ) { vec.X = fChargePercentage; vec.Y = fChargePercentage; vec.Z = fChargePercentage; StopFlightEffects(); ProjFlightTemplate = GroundFXTemplate; ProjFlightTemplateZedTime = GroundFXTemplate; bAutoStartAmbientSound = true; SpawnFlightEffects(); if(ProjEffects != None) { ProjEffects.SetAbsolute( true, true, false ); ProjEffects.SetTranslation( Location + LandedFXOffset ); ProjEffects.SetRotation( GroundRotation ); //ProjEffects.SetScale( InheritedScale ); ProjEffects.SetVectorParameter( name("BlobCharge"), vec); ProjEffects.SetFloatParameter( name("MineFxControlParam"), fChargePercentage); } } } /** Plays a sound on impact */ simulated function PlayImpactSound( optional bool bIsAtRest ) { if( WorldInfo.NetMode != NM_DedicatedServer ) { if( bIsAtRest ) { if(fChargePercentage < 0.75f) PostAkEvent( ImpactAkEvent, true, true, false ); else PostAkEvent( ImpactAkEventHeavy, true, true, false ); } else { if(fChargePercentage < 0.75f) PostAkEvent( BounceAkEvent, true, true, false ); else PostAkEvent( BounceAkEventHeavy, true, true, false ); } } } /** Spawns a decal at the impact location */ simulated function SpawnImpactDecal( vector HitLocation, vector HitNormal ) { if( WorldInfo.MyDecalManager != none && !WorldInfo.bDropDetail && !IsInState('Armed')) { WorldInfo.MyDecalManager.SpawnDecal( ImpactDecalMaterial, HitLocation, rotator(-HitNormal), ImpactDecalWidth, ImpactDecalHeight, ImpactDecalThickness, true ); } } /** Validates a touch */ simulated function bool ValidTouch( Pawn Other ) { // Make sure only enemies detonate if(bCantBeTouchedByInstigator == false) { if( Other.IsAliveAndWell() == false ) { return false; } if(bCantDetonateOnFullHP) { if(Other.GetTeamNum() == TeamNum && Other.Health >= Other.HealthMax ) { return false; } } // Make sure not touching through wall return FastTrace( Other.Location, Location,, true ); } else { if(bCantDetonateOnFullHP) { if(Other.GetTeamNum() == TeamNum && Other.Health >= Other.HealthMax ) { return false; } } if( Other.IsAliveAndWell() == false || Other == Instigator ) { return false; } // Make sure not touching through wall return FastTrace( Other.Location, Location,, true ); } return FastTrace( Other.Location, Location,, true ); } /** When touched by an actor */ simulated event Touch( Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal ) { local Pawn P; // If touched by ballistic bouncer, explode if (KFProj_HRG_BallisticBouncer(Other) != none || KFProj_BloatPukeMine(Other) != none) { if (KFProj_BloatPukeMine(Other) != none) { // Force Touch, as by itself doesn't detect the Mine.. KFProj_BloatPukeMine(Other).Touch(self, none, HitLocation, HitNormal); } if (`TimeSince(CreationTime) >= 0.1f) { if ( WorldInfo.NetMode != NM_DedicatedServer ) { // Use ImpactEffectManager to handle material based impacts `ImpactEffectManager.PlayImpactEffects(HitLocation, Instigator,, ImpactEffects); } if (ExplosionTemplate != None && Role == ROLE_Authority) { TriggerExplosion(HitLocation, HitNormal, Other); } Shutdown(); // cleanup/destroy projectile return; } } // If touched by an enemy pawn, explode P = Pawn( Other ); if( P != None ) { if( ((P != Instigator) || (`TimeSince(CreationTime) >= 0.1f && P == Instigator)) && ValidTouch(P) ) { //super.Touch( Other, OtherComp, HitLocation, HitNormal ); ImpactedActor = Other; ProcessTouch(Other, HitLocation, HitNormal); ImpactedActor = none; } } else if( bBounce ) { super.Touch( Other, OtherComp, HitLocation, HitNormal ); } } /** Blows up mine immediately */ function Detonate() { if( Role == ROLE_Authority ) { TriggerExplosion( Location, vect(0,0,1), none ); } } /** Blows up mine immediately */ function ManuallyDetonate() { bManuallyDetonated = true; if( Role == ROLE_Authority ) { TriggerExplosion( Location, vect(0,0,1), none ); } } /** Blows up on a timer */ function Timer_Explode() { Detonate(); } /** Explode this mine */ simulated function TriggerExplosion(Vector HitLocation, Vector HitNormal, Actor HitActor) { // If we're fading out, don't explode if( bFadingOut ) { return; } super.TriggerExplosion( Location, HitNormal , HitActor ); SetHidden( true ); /** Tell clients to explode */ if( Role == ROLE_Authority ) { bClientExplode = true; bForceNetUpdate = true; } } /** Tells clients to start fading out */ simulated event TornOff() { FadeOut(); } /** Fades this mine out and destroys it */ simulated function FadeOut() { // If we're in midair, just destroy if( Physics == PHYS_Falling ) { Destroy(); return; } // Already fading, early out if( bFadingOut || bDeleteMe || (WorldInfo.NetMode == NM_DedicatedServer && bTearOff) ) { return; } // Remove ownership SetOwner( none ); // Turn off collision SetCollision( false, false ); // Dedicated server doesn't need to fade if( WorldInfo.NetMode != NM_DedicatedServer ) { // Set initial fade values bFadingOut = false; FadeOutTime = WorldInfo.TimeSeconds + default.FadeOutTime; } else { // Delay destruction slightly SetTimer( 0.2f, false, nameOf(Timer_Destroy) ); } // Tell clients to tear off and fade out on their own if( WorldInfo.NetMode != NM_Client ) { bTearOff = true; bNetDirty = true; bForceNetUpdate = true; } } simulated function Timer_Destroy() { Destroy(); } /** Spawns a small burst effect when the mine has finished shrinking */ simulated function SpawnBurstEffect() { if( WorldInfo.NetMode == NM_DedicatedServer || WorldInfo.MyEmitterPool == none || ProjEffects == none ) { return; } WorldInfo.MyEmitterPool.SpawnEmitter( BurstFXTemplate, ProjEffects.GetPosition(), rotator(vect(0,0,1)) ); } /** Removes our puke mine from the pool */ simulated event Destroyed() { if( WorldInfo.NetMode != NM_Client ) { if( InstigatorController != none ) { class'KFGameplayPoolManager'.static.GetPoolManager().RemoveProjectileFromPool( self, PPT_PukeMine ); } } super.Destroyed(); } /** * State where this mine is waiting to detonate */ simulated state Armed { /** Make sure no pawn already touching */ simulated function CheckTouching() { local Pawn P; foreach TouchingActors( class'Pawn', P ) { Touch( P, None, Location, Normal(Location - P.Location) ); } } /** Adjust collision */ simulated function BeginState( Name PreviousStateName ) { if(Role != Role_Authority) { SetPhysics( PHYS_Falling ); CheckTouching(); } } } simulated protected function PrepareExplosionTemplate() { local Weapon OwnerWeapon; local KFWeap_Mine_Reconstructor OwnerMinerec; local Pawn OwnerPawn; //local vector vec; super(KFProjectile).PrepareExplosionTemplate(); //vec.X = 0; //vec.Y = 0; //vec.Z = 0; OwnerWeapon = Weapon(Owner); if (OwnerWeapon != none) { OwnerPawn = Pawn(OwnerWeapon.Owner); if (OwnerPawn != none) { //vec.X = fChargePercentage; //vec.Y = fChargePercentage; //vec.Z = fChargePercentage; ExplosionTemplate.DamageRadius *= KFPawn(OwnerPawn).GetPerk().GetAoERadiusModifier(); if(fChargePercentage > 0.51f) ExplosionTemplate.ExplosionSound=AkEvent'WW_WEP_MineReconstructor.Play_WEP_Mine_Reconstructor_Mine_Explosion_Heavy'; //ExplosionTemplate.ExplosionEffects.DefaultImpactEffect.ParticleTemplate.SetVectorParameter( name("BlobCharge"), vec); //SetFloatParameter( name("MineFxControlParam"), fChargePercentage); OwnerMinerec = KFWeap_Mine_Reconstructor(OwnerWeapon); if(OwnerMinerec != none && !bManuallyDetonated) OwnerMinerec.RemoveProjectileFromPool(self); } } } simulated protected function PrepareExplosionActor(GameExplosionActor GEA) { KFExplosion_MineReconstructor(GEA).fChargePercentage = fChargePercentage; super.PrepareExplosionActor(GEA); } /** Controller left, explode after a short time */ /*simulated function OnInstigatorControllerLeft() { if( WorldInfo.NetMode != NM_Client ) { SetTimer( 1.f + Rand(5) + fRand(), false, nameOf(Timer_Explode) ); } }*/ defaultproperties { Speed=2000 MaxSpeed=2000 TerminalVelocity=2000 TossZ=150 GravityScale=0.5 MomentumTransfer=50000.0 LifeSpan=300 FuseDuration=300 PostExplosionLifetime=1 Physics=PHYS_Falling bBounce=true ProjFlightTemplate= ParticleSystem'WEP_Mine_Reconstructor_EMIT.FX_Bloat_Mine_projectile_01' GroundFXTemplate= ParticleSystem'WEP_Mine_Reconstructor_EMIT.FX_Bloat_Mine_01' BurstFXTemplate= ParticleSystem'WEP_Mine_Reconstructor_EMIT.FX_Bloat_Mine_Hit_01' ExplosionActorClass=class'KFExplosion_MineReconstructor' bSuppressSounds=false bAmbientSoundZedTimeOnly=false bAutoStartAmbientSound=false bStopAmbientSoundOnExplode=true AmbientSoundPlayEvent=AkEvent'WW_WEP_MineReconstructor.Play_WEP_Mine_Reconstructor_Mine_Loop' AmbientSoundStopEvent=AkEvent'WW_WEP_MineReconstructor.Stop_WEP_Mine_Reconstructor_Mine' ImpactAkEvent=AkEvent'WW_WEP_MineReconstructor.Play_WEP_Mine_Reconstructor_Mine' BounceAkEvent=AkEvent'WW_WEP_MineReconstructor.Play_WEP_Mine_Reconstructor_Mine' ImpactAkEventHeavy=AkEvent'WW_WEP_MineReconstructor.Play_WEP_Mine_Reconstructor_Mine_Heavy' BounceAkEventHeavy=AkEvent'WW_WEP_MineReconstructor.Play_WEP_Mine_Reconstructor_Mine_Heavy' Begin Object Class=AkComponent name=AmbientAkSoundComponent bStopWhenOwnerDestroyed=true bForceOcclusionUpdateInterval=true OcclusionUpdateInterval=0.25f End Object AmbientComponent=AmbientAkSoundComponent Components.Add(AmbientAkSoundComponent) //ImpactDecalMaterial=DecalMaterial'FX_Mat_Lib.FX_Puke_Mine_Splatter_DM' ImpactDecalWidth=178.f ImpactDecalHeight=178.f ImpactDecalThickness=28.f Begin Object Name=CollisionCylinder CollisionRadius=10.f CollisionHeight=10.f CollideActors=true PhysMaterialOverride=PhysicalMaterial'WEP_Mine_Reconstructor_EMIT.BloatPukeMine_PM' End Object bCollideActors=true bProjTarget=true bCanBeDamaged=false bCollideComplex=true bNoEncroachCheck=true bPushedByEncroachers=false DampenFactor=0.125f DampenFactorParallel=0.175f LandedFXOffset=(X=0,Y=0,Z=2) // Since we're still using an extent cylinder, we need a line at 0 ExtraLineCollisionOffsets.Add(()) // Collision size we should use when waiting to be triggered ExplodeTriggerRadius=60.f ExplodeTriggerHeight=22.f // Fade out properties FadeOutTime=500000f // Explosion Begin Object Class=KFGameExplosion Name=ExploTemplate0 Damage=150 //30 DamageRadius=200 //450 DamageFalloffExponent=0.5f //0.f DamageDelay=0.f MyDamageType=class'KFDT_Toxic_MineReconstructorExplosion' //bIgnoreInstigator is set to true in PrepareExplosionTemplate //Impulse applied to Zeds MomentumTransferScale=45000 // Damage Effects KnockDownStrength=0 KnockDownRadius=0 FractureMeshRadius=200.0 FracturePartVel=500.0 ExplosionEffects=KFImpactEffectInfo'WEP_Mine_Reconstructor_Arch.Mine_Reconstructor_Mine_Explosion' ExplosionSound=AkEvent'WW_WEP_MineReconstructor.Play_WEP_Mine_Reconstructor_Mine_Explosion' //MomentumTransferScale=0 // Dynamic Light ExploLight=none // Camera Shake CamShake=CameraShake'WEP_Mine_Reconstructor_Arch.Camera_Shake' CamShakeInnerRadius=100 CamShakeOuterRadius=450 CamShakeFalloff=1.5f bOrientCameraShakeTowardsEpicenter=true End Object ExplosionTemplate=ExploTemplate0 GlassShatterType=FMGS_ShatterAll InheritedScale=1 MaxExplodeTriggerRadiusPerPercentage=60 MinExplodeTriggerRadiusPerPercentage=30 MaxExplodeTriggerHeightPerPercentage=22 MinExplodeTriggerHeightPerPercentage=11 MaxDamageRadiusPerPercentage=340 MinDamageRadiusPerPercentage=160 MaxDamagePerPercentage=400 //350 //300 MinDamagePerPercentage=40 //35 //30 MaxCollisionRadius=20 MinCollisionRadius=10 MaxCollisionHeight=20 MinCollisionHeight=10 bBlockedByInstigator=true bNetTemporary=false bSyncToOriginalLocation=true bSyncToThirdPersonMuzzleLocation=true bUseClientSideHitDetection=true bUpdateSimulatedPosition=false bReplicateLocationOnExplosion=true bSticked=false TouchTimeThreshhold=0.05 //EXPERIMENTAL FEATURES FOR DESING bManuallyDetonated=false bCantBeTouchedByInstigator=true bCantDetonateOnFullHP=true }