rott/kf_sources/KFMod/Classes/ZEDGunAltFire.uc
2026-07-14 20:27:09 +07:00

623 lines
17 KiB
Ucode

//=============================================================================
// 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<ZEDBeamEffect> 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<Emitter> 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
}