//============================================================================= // KFDT_EMP_Blast // FFerrando @ saber3d //============================================================================= // Damage caused by the Arc Generator gun alternate fire //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC //============================================================================= class KFProj_ArcGeneratorSphereBlast extends KFProjectile; var int MaxNumberOfZedsZapped; var int MaxDistanceToBeZapped; var float ZapInterval; var int ZapDamage; var float TimeToZap; var KFPawn_Monster oZedCurrentlyBeingSprayed; var ParticleSystem BeamPSCTemplate; var ParticleSystem oPawnPSCEffect; var string EmitterPoolClassPath; var EmitterPool vBeamEffects; struct BeamZapInfo { var ParticleSystemComponent oBeam; var KFPawn_Monster oAttachedZed; var Actor oSourceActor; var float oControlTime; }; var array CurrentZapBeams; var bool ImpactEffectTriggered; var AkComponent ZapSFXComponent; var() AkEvent ZapSFX; var Controller oOriginalOwnerController; var Pawn oOriginalInstigator; var KFWeapon oOriginalOwnerWeapon; const ALTFIRE_FIREMODE = 1; simulated event PreBeginPlay() { local class PoolClass; super.PreBeginPlay(); bIsAIProjectile = InstigatorController == none || !InstigatorController.bIsPlayer; oOriginalOwnerController = InstigatorController; oOriginalInstigator = Instigator; oOriginalOwnerWeapon = KFWeapon(Weapon(Owner)); PoolClass = class(DynamicLoadObject(EmitterPoolClassPath, class'Class')); if (PoolClass != None) { vBeamEffects = Spawn(PoolClass, self,, vect(0,0,0), rot(0,0,0)); } if(oOriginalOwnerWeapon != None) { PenetrationPower = oOriginalOwnerWeapon.GetInitialPenetrationPower(ALTFIRE_FIREMODE); } } function Init(vector Direction) { if( LifeSpan == default.LifeSpan && WorldInfo.TimeDilation < 1.f ) { LifeSpan /= WorldInfo.TimeDilation; } super.Init( Direction ); } simulated function bool ZapFunction(Actor _TouchActor) { local vector BeamEndPoint; local KFPawn_Monster oMonsterPawn; local int iZapped; local ParticleSystemComponent BeamPSC; foreach WorldInfo.AllPawns( class'KFPawn_Monster', oMonsterPawn ) { if( oMonsterPawn.IsAliveAndWell() && oMonsterPawn != _TouchActor) { //`Warn("PAWN CHECK IN: "$oMonsterPawn.Location$""); //`Warn(VSizeSQ(oMonsterPawn.Location - _TouchActor.Location)); if( VSizeSQ(oMonsterPawn.Location - _TouchActor.Location) < Square(MaxDistanceToBeZapped) ) { if(FastTrace(_TouchActor.Location, oMonsterPawn.Location, vect(0,0,0)) == false) { continue; } if(WorldInfo.NetMode != NM_DedicatedServer) { BeamPSC = vBeamEffects.SpawnEmitter(BeamPSCTemplate, _TouchActor.Location, _TouchActor.Rotation); BeamEndPoint = oMonsterPawn.Mesh.GetBoneLocation('Spine1'); if(BeamEndPoint == vect(0,0,0)) BeamEndPoint = oMonsterPawn.Location; BeamPSC.SetBeamSourcePoint(0, _TouchActor.Location, 0); BeamPSC.SetBeamTargetPoint(0, BeamEndPoint, 0); BeamPSC.SetAbsolute(false, false, false); BeamPSC.bUpdateComponentInTick = true; BeamPSC.SetActive(true); StoreBeam(BeamPSC, oMonsterPawn); ZapSFXComponent.PlayEvent(ZapSFX, true); } if(WorldInfo.NetMode == NM_DedicatedServer || WorldInfo.NetMode == NM_StandAlone || WorldInfo.NetMode == NM_ListenServer) { ChainedZapDamageFunction(oMonsterPawn, _TouchActor); } ++iZapped; } } if(iZapped >= MaxNumberOfZedsZapped) break; } if(iZapped > 0) return true; else return false; } simulated function StoreBeam(ParticleSystemComponent Beam, KFPawn_Monster Monster) { local BeamZapInfo BeamInfo; BeamInfo.oBeam = Beam; BeamInfo.oAttachedZed = Monster; BeamInfo.oSourceActor = self; BeamInfo.oControlTime = ZapInterval; CurrentZapBeams.AddItem(BeamInfo); } function ChainedZapDamageFunction(Actor _TouchActor, Actor _OriginActor) { //local float DistToHitActor; local vector Momentum; local TraceHitInfo HitInfo; local Pawn TouchPawn; local int TotalDamage; if (_OriginActor != none) { Momentum = _TouchActor.Location - _OriginActor.Location; } //DistToHitActor = VSize(Momentum); //Momentum *= (MomentumScale / DistToHitActor); if (ZapDamage > 0) { TouchPawn = Pawn(_TouchActor); // Let script know that we hit something if (TouchPawn != none) { ProcessDirectImpact(); } //`Warn("["$WorldInfo.TimeSeconds$"] Damaging "$_TouchActor.Name$" for "$ZapDamage$", Dist: "$VSize(_TouchActor.Location - _OriginActor.Location)); TotalDamage = ZapDamage * UpgradeDamageMod; _TouchActor.TakeDamage(TotalDamage, oOriginalOwnerController, _TouchActor.Location, Momentum, class'KFDT_EMP_ArcGenerator_AltFiremodeZapDamage', HitInfo, self); } } /** Notification that a direct impact has occurred. */ event ProcessDirectImpact() { local KFPlayerController KFPC; KFPC = KFPlayerController(oOriginalOwnerController); if( KFPC != none ) { KFPC.AddShotsHit(1); } } simulated event HitWall( vector HitNormal, actor Wall, PrimitiveComponent WallComp ) { if( !bHasExploded ) { Explode(Location - (HitNormal * CylinderComponent.CollisionRadius), HitNormal); //DrawDebugSphere(Location, CylinderComponent.CollisionRadius, 10, 255, 255, 0, true ); //DrawDebugSphere(Location, 2, 10, 0, 0, 255, true ); //DrawDebugSphere(Location - (HitNormal * CylinderComponent.CollisionRadius), 2, 10, 255, 0, 0, true ); } } /** Call ProcessBulletTouch */ simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal) { Local KFPawn_Monster Monster; //Super.ProcessTouch(Other, HitLocation, HitNormal); local KFPawn KFP; local bool bPassThrough, bNoPenetrationDmgReduction; local KFPerk CurrentPerk; local InterpCurveFloat PenetrationCurve; local KFWeapon KFW; ProcessEffect(HitLocation, HitNormal, Other); if(role != role_authority) { return; } if (Other != oOriginalOwnerWeapon) { if(IgnoreTouchActor == Other) { return; } if (!Other.bStatic && DamageRadius == 0.0) { // check/ignore repeat touch events if( CheckRepeatingTouch(Other) ) { return; } KFW = oOriginalOwnerWeapon; // Keep going if we need to keep penetrating if (KFW == none || KFW.GetInitialPenetrationPower(ALTFIRE_FIREMODE) > 0.0f) { if (PenetrationPower > 0 || PassThroughDamage(Other)) { if (KFW != none) { CurrentPerk = KFW.GetPerk(); if (CurrentPerk != none) { bNoPenetrationDmgReduction = CurrentPerk.IgnoresPenetrationDmgReduction(); } PenetrationCurve = KFW.PenetrationDamageReductionCurve[ALTFIRE_FIREMODE]; if (!bNoPenetrationDmgReduction) { Damage *= EvalInterpCurveFloat(PenetrationCurve, PenetrationPower / KFW.GetInitialPenetrationPower(ALTFIRE_FIREMODE)); } } ProcessBulletTouch(Other, HitLocation, HitNormal); // Reduce penetration power for every KFPawn penetrated KFP = KFPawn(Other); if (KFP != none) { PenetrationPower -= KFP.PenetrationResistance; bPassThrough = TRUE; } } } else { ProcessBulletTouch(Other, HitLocation, HitNormal); } } // handle water pass through damage/hitfx else if ( DamageRadius == 0.f && !Other.bBlockActors && Other.IsA('KFWaterMeshActor') ) { if ( WorldInfo.NetMode != NM_DedicatedServer ) { `ImpactEffectManager.PlayImpactEffects(HitLocation, oOriginalInstigator,, ImpactEffects); } bPassThrough = TRUE; } if ( !bPassThrough ) { Super.ProcessTouch(Other, HitLocation, HitNormal); } } Monster = KFPawn_Monster(Other); // Needed to spawn particles cause of the special behaviour of the projectile if( Monster != None && Monster.IsAliveAndWell() && ImpactEffects != None ) { WorldInfo.MyEmitterPool.SpawnEmitter(oPawnPSCEffect, HitLocation, rotator(HitNormal), Other); } } /** Handle bullet collision and damage */ simulated function ProcessBulletTouch(Actor Other, Vector HitLocation, Vector HitNormal) { local Pawn Victim; local Pawn CurrentInstigator; local array HitZoneImpactList; local vector StartTrace, EndTrace, Direction; local TraceHitInfo HitInfo; local KFWeapon KFW; // Do the impact effects ProcessEffect(HitLocation, HitNormal, Other); Victim = Pawn( Other ); if ( Victim == none ) { if ( bDamageDestructiblesOnTouch && Other.bCanBeDamaged ) { HitInfo.HitComponent = LastTouchComponent; HitInfo.Item = INDEX_None; // force TraceComponent on fractured meshes Other.TakeDamage(Damage, oOriginalOwnerController, Location, MomentumTransfer * Normal(Velocity), MyDamageType, HitInfo, self); } // Reduce the penetration power to zero if we hit something other than a pawn or foliage actor if( InteractiveFoliageActor(Other) == None ) { PenetrationPower = 0; return; } } else { if (bSpawnShrapnel) { //spawn straight forward through the zed SpawnShrapnel(Other, HitLocation, HitNormal, rotator(Velocity), ShrapnelSpreadWidthZed, ShrapnelSpreadHeightZed); } StartTrace = HitLocation; Direction = Normal(Velocity); EndTrace = StartTrace + Direction * (Victim.CylinderComponent.CollisionRadius * 6.0); TraceProjHitZones(Victim, EndTrace, StartTrace, HitZoneImpactList); // Right now we just send the first impact. TODO: Figure out what the // most "important" or high damage impact is and send that one! Or, // if we need the info on the server send the whole thing - Ramm if ( HitZoneImpactList.length > 0 ) { HitZoneImpactList[0].RayDir = Direction; if( bReplicateClientHitsAsFragments ) { if( oOriginalInstigator != none ) { KFW = oOriginalOwnerWeapon; if( KFW != none ) { KFW.HandleGrenadeProjectileImpact(HitZoneImpactList[0], class); } } } // Owner is none on a remote client, or the weapon on the server/local player else if( oOriginalOwnerWeapon != none ) { KFW = oOriginalOwnerWeapon; if( KFW != none ) { CurrentInstigator = KFW.Instigator; KFW.Instigator = oOriginalInstigator; KFW.HandleProjectileImpactSpecial(ALTFIRE_FIREMODE, HitZoneImpactList[0], oOriginalInstigator, PenetrationPower); KFW.Instigator = CurrentInstigator; } } } } } simulated event Tick( float DeltaTime ) { Local int i; local vector BeamEndPoint; /*//cylinders debug local vector A, B; A = Location + (vect(0.0f,0.0f,20.0f) * 1); B = Location + (vect(0.0f,0.0f,-20.0f) * 1); DrawDebugCylinder( A, B, 40, 10, 255, 255, 0, true); // SLOW! Use for debugging only! */ if(CurrentZapBeams.length > 0) { for(i=0 ; i 0 && CurrentZapBeams[i].oAttachedZed.IsAliveAndWell()) { BeamEndPoint = CurrentZapBeams[i].oAttachedZed.Mesh.GetBoneLocation('Spine1'); if(BeamEndPoint == vect(0,0,0)) BeamEndPoint = CurrentZapBeams[i].oAttachedZed.Location; CurrentZapBeams[i].oBeam.SetBeamSourcePoint(0, CurrentZapBeams[i].oSourceActor.Location, 0); CurrentZapBeams[i].oBeam.SetBeamTargetPoint(0, BeamEndPoint, 0); } else { CurrentZapBeams[i].oBeam.DeactivateSystem(); CurrentZapBeams.RemoveItem(CurrentZapBeams[i]); i--; } } } TimeToZap += DeltaTime; //`Warn(TimeToZap); //`Warn(TimeToZap > ZapInterval); if(TimeToZap > ZapInterval) { if(ZapFunction(self)) { TimeToZap = 0; } } } simulated protected function DeferredDestroy(float DelaySec) { Super.DeferredDestroy(DelaySec); FinalEffectHandling(); } simulated function Destroyed() { FinalEffectHandling(); Super.Destroyed(); } simulated function FinalEffectHandling() { Local int i; if( ImpactEffects != None) { ImpactEffectTriggered=True; WorldInfo.MyEmitterPool.SpawnEmitter(ImpactEffects.DefaultImpactEffect.ParticleTemplate, Location, Rotation); } if(CurrentZapBeams.length > 0) { for(i=0 ; i