//============================================================================= // KFExplosion_HRG_BallisticBouncer //============================================================================= // Killing Floor 2 // Copyright (C) 2022 Tripwire Interactive LLC //============================================================================= class KFExplosion_HRG_BallisticBouncer extends KFExplosionActorLingering; var() class HealingDamageType; var() float HealingAmount; var AkEvent SmokeLoopStartEvent; var AkEvent SmokeLoopStopEvent; var KFPerk CachedInstigatorPerk; var float fChargePercentage; var float fMinAmmoutHealing; var float fMaxAmmoutHealing; var float fAltMinAmmoutHealing; var float fAltMaxAmmoutHealing; var bool bHealsInstigator; var bool bHealsDifferentAmmoutToInstigator; /* Only used if bOnlyDamagePawns is active to create fractures in walls */ var bool bAllowFractureDamage; simulated function SpawnExplosionParticleSystem(ParticleSystem Template) { local ParticleSystemComponent PSC; local vector vec; // If the template is none, grab the default if( !ExplosionTemplate.bAllowPerMaterialFX && Template == none ) { Template = KFGameExplosion(ExplosionTemplate).ExplosionEffects.DefaultImpactEffect.ParticleTemplate; } // Use custom pool PSC = WorldInfo.MyEmitterPool.SpawnEmitter(Template, Location, rotator(ExplosionTemplate.HitNormal), None); //fChargePercentage vec.X = fChargePercentage; vec.Y = fChargePercentage; vec.Z = fChargePercentage; PSC.SetVectorParameter( name("BlobCharge"), vec); PSC.SetFloatParameter( name("MineFxControlParam"), fChargePercentage); } /* * @param Direction For bDirectionalExplosion=true explosions, this is the forward direction of the blast. * Overridden to add the ability to spawn fragments from the explosion **/ simulated function Explode(GameExplosion NewExplosionTemplate, optional vector Direction) { local KFPawn KFP; super.Explode(NewExplosionTemplate, Direction); if( Instigator != none ) { KFP = KFPawn(Instigator); if( KFP != none ) { CachedInstigatorPerk = KFP.GetPerk(); } } if (Role == Role_Authority) { //DelayedExplosionDamage(); SetTimer(Interval, false, nameof(DelayedExplosionDamage), self); } } /** * Deal damage or heal players */ protected simulated function AffectsPawn(Pawn Victim, float DamageScale) { local KFPawn_Human HumanVictim; local KFPawn_Monster MonsterVictim; local KFProj_MedicGrenade OwnerProjectile; local bool bCanRepairArmor; local Box BBox; local vector BBoxCenter; local Actor HitActor; local bool bDamageBlocked; if( Victim != none && Victim.IsAliveAndWell() ) { MonsterVictim = KFPawn_Monster(Victim); if( MonsterVictim != none ) { if( bWasFadedOut|| bDeleteMe || bPendingDelete ) { return; } Victim.GetComponentsBoundingBox(BBox); BBoxCenter = (BBox.Min + BBox.Max) * 0.5f; HitActor = TraceExplosive(BBoxCenter, Location + vect(0, 0, 20)); bDamageBlocked = (HitActor != None && HitActor != Victim); if(bDamageBlocked && HitActor.IsA('KFDoorActor')) { bDamageBlocked = false; } if( !bDamageBlocked ) { Victim.TakeRadiusDamage(InstigatorController, ExplosionTemplate.Damage * DamageScale, ExplosionTemplate.DamageRadius, ExplosionTemplate.MyDamageType, ExplosionTemplate.MomentumTransferScale, Location, bDoFullDamage, (Owner != None) ? Owner : self, ExplosionTemplate.DamageFalloffExponent); } } else { Victim.GetComponentsBoundingBox(BBox); BBoxCenter = (BBox.Min + BBox.Max) * 0.5f; HitActor = TraceExplosive(BBoxCenter, Location + vect(0, 0, 20)); bDamageBlocked = (HitActor != None && HitActor != Victim); if(bDamageBlocked && HitActor.IsA('KFDoorActor')) { bDamageBlocked = false; } if(!bDamageBlocked) { if(!bHealsInstigator && Victim == Instigator) { return; } HumanVictim = KFPawn_Human(Victim); if( HumanVictim != none && HumanVictim.GetExposureTo(Location) > 0 ) { OwnerProjectile = KFProj_MedicGrenade(Owner); if( OwnerProjectile != none ) { bCanRepairArmor = OwnerProjectile.HealedPawns.Find( HumanVictim ) == INDEX_NONE; } if(bHealsDifferentAmmoutToInstigator && bHealsInstigator && Victim == Instigator) { HumanVictim.HealDamage( Lerp(fAltMinAmmoutHealing, fAltMaxAmmoutHealing, fChargePercentage), InstigatorController, HealingDamageType, bCanRepairArmor); } else { HumanVictim.HealDamage( Lerp(fMinAmmoutHealing, fMaxAmmoutHealing, fChargePercentage), InstigatorController, HealingDamageType, bCanRepairArmor); } if( bCanRepairArmor ) { OwnerProjectile.HealedPawns.AddItem( HumanVictim ); } } } } } } protected simulated function bool DoExplosionDamage(bool bCauseDamage, bool bCauseEffects) { if( bWasFadedOut || bDeleteMe || bPendingDelete ) { return false; } if( bOnlyDamagePawns && bAllowFractureDamage ) { ExplodeFractures(); } if( bOnlyDamagePawns ) { return ExplodePawns(bCauseDamage); } return super(KFExplosionActor).DoExplosionDamage(bCauseDamage, bCauseEffects); } /** Check fractures only if */ protected simulated function ExplodeFractures() { local Actor Victim; local bool bCauseFractureEffects; local float CheckRadius, VictimDist; local Box BBox; local FracturedStaticMeshActor FracActor; local byte WantPhysChunksAndParticles; local TraceHitInfo HitInfo; if( bWasFadedOut || bDeleteMe || bPendingDelete ) { return; } bCauseFractureEffects = WorldInfo.NetMode != NM_DedicatedServer && ExplosionTemplate.bCausesFracture; if( !bCauseFractureEffects ) { return; } // determine radius to check CheckRadius = GetEffectCheckRadius(true, bCauseFractureEffects, WorldInfo.NetMode != NM_Client); if ( CheckRadius > 0.0 ) { foreach CollidingActors(class'Actor', Victim, CheckRadius, Location, ExplosionTemplate.bUseOverlapCheck,,HitInfo ) { if ( Victim != Self && (!Victim.bWorldGeometry || Victim.bCanBeDamaged) && (NavigationPoint(Victim) == None) && Victim != ExplosionTemplate.ActorToIgnoreForDamage && (!ExplosionTemplate.bIgnoreInstigator || Victim != Instigator) && !ClassIsChildOf(Victim.Class, ExplosionTemplate.ActorClassToIgnoreForDamage) && !IsBehindExplosion(Victim) ) { // return if this is a pawn if(GamePawn(Victim) != none) { return; } // check if visible, unless physics object // note: using bbox center instead of location, because that's what visiblecollidingactors does Victim.GetComponentsBoundingBox(BBox); // adjust distance if using overlap check VictimDist = ExplosionTemplate.bUseOverlapCheck ? BoxDistanceToPoint(Location, BBox) : VSize(Location - Victim.Location); FracActor = FracturedStaticMeshActor(Victim); if ( (FracActor != None) && (VictimDist < ExplosionTemplate.FractureMeshRadius) && (FracActor.Physics == PHYS_None) && FracActor.IsFracturedByDamageType(ExplosionTemplate.MyDamageType) && FracActor.FractureEffectIsRelevant( false, Instigator, WantPhysChunksAndParticles) ) { // Let kismet know that we were hit by an explosion FracActor.NotifyHitByExplosion(InstigatorController, ExplosionTemplate.Damage, ExplosionTemplate.MyDamageType); FracActor.BreakOffPartsInRadius(Location, ExplosionTemplate.FractureMeshRadius, ExplosionTemplate.FracturePartVel, WantPhysChunksAndParticles == 1 ? true : false); } } } } } DefaultProperties { HealingDamageType=class'KFDT_Healing' HealingAmount=0; fMinAmmoutHealing=5; //4; fMaxAmmoutHealing=50; //40 Interval=0 MaxTime=0.0 FadeOutTime=0.0 bExplodeMoreThanOnce=false bDoFullDamage=false bOnlyDamagePawns=true bAllowFractureDamage=true bSkipLineCheckForPawns=true LoopStartEvent=none LoopStopEvent=none //EXPERIMENTAL FEATURES FOR DESIGN bHealsInstigator = false; bHealsDifferentAmmoutToInstigator = false; fAltMinAmmoutHealing=1; fAltMaxAmmoutHealing=5; }