//============================================================================= // ZEDGunAltFire //============================================================================= // ZEDGun secondary fire class //============================================================================= // Killing Floor Source // Copyright (C) 2012 Tripwire Interactive LLC // - John "Ramm-Jaeger" Gibson //============================================================================= class ZEDGunAltFire extends KFFire; // Zed Beam Firing Vars var ZEDBeamEffect Beam; var class BeamEffectClass; var float UpTime; var float ChargeUpTime; var bool bDoHit; var bool bStartFire; var() Vector ProjSpawnOffset; // +x forward, +y right, +z up // sound var sound AmbientChargeUpSound; // The charging up of the weapon var float AmbientFireSoundRadius; // The sound radius for the ambient fire sound var sound AmbientFireSound; // How loud to play the looping ambient fire sound var byte AmbientFireVolume; // The ambient fire sound var string AmbientChargeUpSoundRef; var string AmbientFireSoundRef; var() float MaxChargeTime; // The maximum amount of time for a full charged shot var() float MaxZedSphereChargeTime; // The maximum amount of time to fully charge the zap sphere var() class ChargeEmitterClass; var() Emitter ChargeEmitter; static function PreloadAssets(LevelInfo LevelInfo, optional KFFire Spawned) { super.PreloadAssets(LevelInfo, Spawned); if ( default.AmbientChargeUpSoundRef != "" ) { default.AmbientChargeUpSound = sound(DynamicLoadObject(default.AmbientChargeUpSoundRef, class'sound', true)); } if ( default.AmbientFireSoundRef != "" ) { default.AmbientFireSound = sound(DynamicLoadObject(default.AmbientFireSoundRef, class'sound', true)); } if ( ZEDGunAltFire(Spawned) != none ) { ZEDGunAltFire(Spawned).AmbientChargeUpSound = default.AmbientChargeUpSound; ZEDGunAltFire(Spawned).AmbientFireSound = default.AmbientFireSound; } } static function bool UnloadAssets() { super.UnloadAssets(); default.AmbientChargeUpSound = none; default.AmbientFireSound = none; return true; } function float MaxRange() { return 2500; } function PlayPreFire() { Weapon.PlayAnim('Charge', 1.0, 0.1); } simulated function ModeTick(float dt) { local Vector StartTrace, EndTrace, X, Y, Z; local Vector HitLocation, HitNormal, EndEffect; local Actor Other; local Rotator Aim; local ZEDGun ZEDGun; local ZEDBeamEffect LB; local KFMonster KFM; local float ChargeScale; if ( !bIsFiring ) { return; } ZEDGun = ZEDGun(Weapon); // Handle the beam firing if ( AllowFire() && ((UpTime > 0.0) || (Instigator.Role < ROLE_Authority)) ) { UpTime -= dt; if( ChargeUpTime == 0 ) { // Play the chargeup sound PlayAmbientSound(AmbientChargeUpSound); InitChargeEffect(); SetTimer(0.15, true); PlayPreFire(); } ChargeUpTime += dt; // the to-hit trace starts at the spawn offset to it looks like it is coming from the barrel ZEDGun.GetViewAxes(X, Y, Z); StartTrace = GetFireStart( X, Y, Z); StartTrace = StartTrace + X*ProjSpawnOffset.X; StartTrace = StartTrace + Weapon.Hand * Y*ProjSpawnOffset.Y + Z*ProjSpawnOffset.Z; // Find the beam on the client if ( Instigator.Role < ROLE_Authority ) { if ( Beam == None ) { ForEach Weapon.DynamicActors(class'ZEDBeamEffect', LB ) if ( !LB.bDeleteMe && (LB.Instigator != None) && (LB.Instigator == Instigator) ) { Beam = LB; break; } } } // Consume the ammo on the server if ( Instigator.Role == ROLE_Authority ) { if ( bDoHit ) { Weapon.ConsumeAmmo(0, AmmoPerFire); } } // client if ( bDoHit && Instigator.IsLocallyControlled() ) { HandleRecoil(1.0); } // Set the end point of the beam Aim = AdjustAim(StartTrace, AimError); X = Vector(Aim); EndTrace = StartTrace + TraceRange * X; Other = Weapon.Trace(HitLocation, HitNormal, EndTrace, StartTrace, true); if ( Other != None && Other != Instigator ) EndEffect = HitLocation; else EndEffect = EndTrace; if ( Beam != None ) Beam.EndEffect = EndEffect; // Everything else here needs to happen on the server only if ( Instigator.Role < ROLE_Authority ) { return; } if ( Other != None && Other != Instigator ) { if( KFMonster(Other) != none && KFMonster(Other).Health > 0 ) { KFMonster(Other).SetZapped(dt, Instigator); } else if( ExtendedZCollision(Other)!=None && Other.Base != none && KFMonster(Other.Base) != none && KFMonster(Other.Base).Health > 0 ) { KFMonster(Other.Base).SetZapped(dt, Instigator); } // Make noise every time we "fire" if ( bDoHit ) { Instigator.MakeNoise(1.0); } } if( ChargeUpTime < MaxZedSphereChargeTime ) { ChargeScale = ChargeUpTime/MaxZedSphereChargeTime; } else { ChargeScale = 1.0; } if ( Beam != None ) Beam.SphereCharge = ChargeScale; //Weapon.DrawDebugSphere( EndEffect, 250 * ChargeScale, 12, 0, 255, 0); if ( bDoHit && Other != None && Other != Instigator ) { foreach Weapon.VisibleCollidingActors( class 'KFMonster', KFM, 250 * ChargeScale, EndEffect ) { if( KFM != none && KFM != Other ) { if( KFM.Health > 0 ) { KFM.SetZapped(FireRate * 0.75, Instigator); } } } } // beam effect is created and destroyed when firing starts and stops if ( (Beam == None) && bIsFiring ) { Beam = Weapon.Spawn( BeamEffectClass, Instigator ); } if ( Beam != None ) { Beam.bHitSomething = (Other != None); Beam.EndEffect = EndEffect; } } else StopFiring(); bStartFire = false; bDoHit = false; } // Overriden to share the same ammo as the primary fire simulated function bool AllowFire() { if(KFWeapon(Weapon).bIsReloading) return false; if(KFPawn(Instigator).SecondaryItem!=none) return false; if(KFPawn(Instigator).bThrowingNade) return false; if(KFWeapon(Weapon).MagAmmoRemaining < 1) { if( Level.TimeSeconds - LastClickTime>FireRate ) { LastClickTime = Level.TimeSeconds; } if( AIController(Instigator.Controller)!=None ) KFWeapon(Weapon).ReloadMeNow(); return false; } return ( Weapon.AmmoAmount(0) >= AmmoPerFire); } // Do nothing function ModeHoldFire(){} // Handles toggling the weapon attachment's ambient sound on and off function PlayAmbientSound(Sound aSound) { local WeaponAttachment WA; WA = WeaponAttachment(Weapon.ThirdPersonActor); if ( Weapon == none || (WA == none)) return; if(aSound == None) { WA.SoundVolume = WA.default.SoundVolume; WA.SoundRadius = WA.default.SoundRadius; } else { WA.SoundVolume = AmbientFireVolume; WA.SoundRadius = AmbientFireSoundRadius; } WA.AmbientSound = aSound; } // Handle the Zed gun charge sounds and effects function Timer() { local float ChargeScale; local ZEDGunAttachment WA; WA = ZEDGunAttachment(Weapon.ThirdPersonActor); if (ChargeUpTime > 0.0 && bIsFiring) { if( ChargeUpTime < MaxChargeTime ) { PlayAmbientSound(AmbientChargeUpSound); ChargeScale = ChargeUpTime/MaxChargeTime; WA.ZedGunCharge = ChargeScale * 255; WA.UpdateZedGunCharge(); if( ChargeEmitter != none ) { ChargeEmitter.Emitters[0].SizeScale[1].RelativeSize = Lerp( ChargeScale, 4, 10 ); ChargeEmitter.Emitters[1].StartVelocityRadialRange.Min = Lerp( ChargeScale, 50, 300 ); ChargeEmitter.Emitters[1].StartVelocityRadialRange.Max = Lerp( ChargeScale, 50, 300 ); ChargeEmitter.Emitters[1].SizeScale[0].RelativeSize = Lerp( ChargeScale, 2, 6 ); } } else { PlayAmbientSound(AmbientFireSound); WA.ZedGunCharge = 255; WA.UpdateZedGunCharge(); if( ChargeEmitter != none ) { ChargeEmitter.Emitters[0].SizeScale[1].RelativeSize = 10; ChargeEmitter.Emitters[1].StartVelocityRadialRange.Min = 300; ChargeEmitter.Emitters[1].StartVelocityRadialRange.Max = 300; ChargeEmitter.Emitters[1].SizeScale[0].RelativeSize = 6; } } } else { PlayAmbientSound(none); DestroyChargeEffect(); WA.ZedGunCharge = 0; WA.UpdateZedGunCharge(); PlayFireEnd(); Weapon.StopFire(ThisModeNum); SetTimer(0, false); } } // Handle setting new recoil simulated function HandleRecoil(float Rec) { local rotator NewRecoilRotation; local KFPlayerController KFPC; local KFPawn KFPwn; if( Instigator != none ) { KFPC = KFPlayerController(Instigator.Controller); KFPwn = KFPawn(Instigator); } if( KFPC == none || KFPwn == none ) return; if( !KFPC.bFreeCamera ) { NewRecoilRotation.Pitch = RandRange( maxVerticalRecoilAngle * 0.5, maxVerticalRecoilAngle ); NewRecoilRotation.Yaw = RandRange( maxHorizontalRecoilAngle * 0.5, maxHorizontalRecoilAngle ); if( Rand( 2 ) == 1 ) NewRecoilRotation.Yaw *= -1; NewRecoilRotation.Pitch += (Instigator.HealthMax / Instigator.Health * 5); NewRecoilRotation.Yaw += (Instigator.HealthMax / Instigator.Health * 5); NewRecoilRotation *= Rec; KFPC.SetRecoil(NewRecoilRotation,RecoilRate * (default.FireRate/FireRate)); } } // Handle the beam having stopped firing function StopFiring() { if (Beam != None) { Beam.Destroy(); Beam = None; } bStartFire = true; ChargeUpTime = 0; } event ModeDoFire() { Load = 0; //don't use ammo here - it will be consumed in ModeTick() where it's sync'ed with damage dealing if (!AllowFire()) return; if (!AllowFire()) return; if (MaxHoldTime > 0.0) HoldTime = FMin(HoldTime, MaxHoldTime); // server if (Weapon.Role == ROLE_Authority) { DoFireEffect(); HoldTime = 0; // if bot decides to stop firing, HoldTime must be reset first if ( (Instigator == None) || (Instigator.Controller == None) ) return; if ( AIController(Instigator.Controller) != None ) AIController(Instigator.Controller).WeaponFireAgain(BotRefireRate, true); Instigator.DeactivateSpawnProtection(); } // client if (Instigator.IsLocallyControlled()) { ShakeView(); } Weapon.IncrementFlashCount(ThisModeNum); // set the next firing time. must be careful here so client and server do not get out of sync if (bFireOnRelease) { if (bIsFiring) NextFireTime += MaxHoldTime + FireRate; else NextFireTime = Level.TimeSeconds + FireRate; } else { NextFireTime += FireRate; NextFireTime = FMax(NextFireTime, Level.TimeSeconds); } Load = AmmoPerFire; HoldTime = 0; if (Instigator.PendingWeapon != Weapon && Instigator.PendingWeapon != None) { bIsFiring = false; Weapon.PutDown(); } } // Overridden to play different anims/sounds for the power down of this weapon function PlayFireEnd() { local float RandPitch; if ( Weapon.Mesh != None ) { if ( FireCount > 0 ) { if( KFWeap.bAimingRifle ) { if ( Weapon.HasAnim(FireLoopAimedAnim) ) { Weapon.PlayAnim(FireLoopAimedAnim, FireLoopAnimRate, 0.0); } else if( Weapon.HasAnim(FireAimedAnim) ) { Weapon.PlayAnim(FireAimedAnim, FireAnimRate, TweenTime); } else { Weapon.PlayAnim(FireAnim, FireAnimRate, TweenTime); } } else { if ( Weapon.HasAnim(FireLoopAnim) ) { Weapon.PlayAnim(FireLoopAnim, FireLoopAnimRate, 0.0); } else { Weapon.PlayAnim(FireAnim, FireAnimRate, TweenTime); } } } else { if( KFWeap.bAimingRifle ) { if( Weapon.HasAnim(FireAimedAnim) ) { Weapon.PlayAnim(FireAimedAnim, FireAnimRate, TweenTime); } else { Weapon.PlayAnim(FireAnim, FireAnimRate, TweenTime); } } else { Weapon.PlayAnim(FireAnim, FireAnimRate, TweenTime); } } } if( Weapon.Instigator != none && Weapon.Instigator.IsLocallyControlled() && Weapon.Instigator.IsFirstPerson() && StereoFireSound != none ) { if( bRandomPitchFireSound ) { RandPitch = FRand() * RandomPitchAdjustAmt; if( FRand() < 0.5 ) { RandPitch *= -1.0; } } Weapon.PlayOwnedSound(StereoFireSound,SLOT_Interact,TransientSoundVolume * 0.85,,TransientSoundRadius,(1.0 + RandPitch),false); } else { if( bRandomPitchFireSound ) { RandPitch = FRand() * RandomPitchAdjustAmt; if( FRand() < 0.5 ) { RandPitch *= -1.0; } } Weapon.PlayOwnedSound(FireSound,SLOT_Interact,TransientSoundVolume,,TransientSoundRadius,(1.0 + RandPitch),false); } ClientPlayForceFeedback(FireForce); // jdf FireCount++; } // Don't do any fire effect, its all done in the mode tick and this fire mode doesn't do normal damage function DoFireEffect() { bDoHit = true; UpTime = FireRate+0.1; } // Don't do any fire effect, its all done in the mode tick and this fire mode doesn't do normal damage simulated function DoClientOnlyEffectTrace(Vector Start, Rotator Dir){} simulated function InitChargeEffect() { // don't even spawn on server if ( (Level.NetMode == NM_DedicatedServer) || (AIController(Instigator.Controller) != None) ) return; if ( (ChargeEmitterClass != None) && ((ChargeEmitter == None) || ChargeEmitter.bDeleteMe) ) { ChargeEmitter = Weapon.Spawn(ChargeEmitterClass); if ( ChargeEmitter != None ) Weapon.AttachToBone(ChargeEmitter, 'tip'); } } simulated function DestroyChargeEffect() { if (ChargeEmitter != None) ChargeEmitter.Destroy(); } simulated function DestroyEffects() { super.DestroyEffects(); DestroyChargeEffect(); if ( Level.NetMode != NM_Client ) { if ( Beam != None ) Beam.Destroy(); } } defaultproperties { bFireOnRelease=false FireAimedAnim=ChargeDown FireSoundRef="KF_ZEDGunSnd.KF_WEP_ZED_Secondary_SpinDown_M" StereoFireSoundRef="KF_ZEDGunSnd.KF_WEP_ZED_Secondary_SpinDown_S" NoAmmoSoundRef="KF_ZEDGunSnd.KF_WEP_ZED_Dryfire" DamageType=none FireAnim=ChargeDown TransientSoundVolume=2.0 TransientSoundRadius=500.000000 FireForce="AssaultRifleFire" FireRate=0.12 RecoilRate=0.07 maxVerticalRecoilAngle=100 maxHorizontalRecoilAngle=150 TweenTime=0.025 bRecoilRightOnly=false bWaitForRelease=false TraceRange=2500 AmmoClass=Class'KFMod.ZEDGunAmmo' AmmoPerFire=1 BotRefireRate=0.15 FlashEmitterClass=Class'ROEffects.ZEDGunChargeDown' aimerror=42.000000 Spread=0.015 SpreadStyle=SS_Random ProjSpawnOffset=(X=25,Y=18,Z=-14.5) BeamEffectClass=class'ZEDBeamEffect' ChargeEmitterClass=Class'ROEffects.ChargeUp1stZEDGun' AmbientFireSoundRef="KF_ZEDGunSnd.ZedGunChargeLoop" AmbientChargeUpSoundRef="KF_ZEDGunSnd.ZedGunChargeUp" AmbientFireSoundRadius=500 AmbientFireVolume=255 MaxChargeTime=1.0 MaxZedSphereChargeTime=3.0 //** View shake **// ShakeOffsetMag=(X=3.0,Y=1.0,Z=3.0) ShakeOffsetRate=(X=1000.0,Y=1000.0,Z=1000.0) ShakeOffsetTime=3.0 ShakeRotMag=(X=25.0,Y=25.0,Z=100.0) ShakeRotRate=(X=12500.0,Y=12500.0,Z=12500.0) ShakeRotTime=5.0 bRandomPitchFireSound=false }