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

443 lines
13 KiB
Ucode

//=============================================================================
// HuskGunFire
//=============================================================================
// Husk Gun primary fire class
//=============================================================================
// Killing Floor Source
// Copyright (C) 2011 Tripwire Interactive LLC
// - John "Ramm-Jaeger" Gibson
//=============================================================================
class HuskGunFire extends KFShotgunFire;
// 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() class<Projectile> WeakProjectileClass;
var() class<Projectile> StrongProjectileClass;
var() class<Emitter> ChargeEmitterClass;
var() Emitter ChargeEmitter;
static function PreloadAssets(LevelInfo LevelInfo, optional KFShotgunFire 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 ( HuskGunFire(Spawned) != none )
{
HuskGunFire(Spawned).AmbientChargeUpSound = default.AmbientChargeUpSound;
HuskGunFire(Spawned).AmbientFireSound = default.AmbientFireSound;
}
}
static function bool UnloadAssets()
{
super.UnloadAssets();
default.AmbientChargeUpSound = none;
default.AmbientFireSound = none;
return true;
}
simulated function bool AllowFire()
{
return (Weapon.AmmoAmount(ThisModeNum) >= AmmoPerFire);
}
function float MaxRange()
{
return 2500;
}
function DoFireEffect()
{
Super(KFShotgunFire).DoFireEffect();
}
function PlayPreFire()
{
if( KFWeapon(Weapon).bAimingRifle )
{
Weapon.PlayAnim('Charge_Iron', 1.0, 0.1);
}
else
{
Weapon.PlayAnim('Charge', 1.0, 0.1);
}
}
function ModeHoldFire()
{
// Play the chargeup sound
PlayAmbientSound(AmbientChargeUpSound);
InitChargeEffect();
SetTimer(0.15, true);
}
// 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;
}
function Timer()
{
local float ChargeScale;
local HuskGunAttachment WA;
WA = HuskGunAttachment(Weapon.ThirdPersonActor);
if (HoldTime > 0.0 && !bNowWaiting)
{
if( HoldTime < MaxChargeTime )
{
PlayAmbientSound(AmbientChargeUpSound);
ChargeScale = HoldTime/MaxChargeTime;
WA.HuskGunCharge = ChargeScale * 255;
WA.UpdateHuskGunCharge();
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.HuskGunCharge = 255;
WA.UpdateHuskGunCharge();
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.HuskGunCharge = 0;
WA.UpdateHuskGunCharge();
SetTimer(0, false);
}
}
/* Accessor function that returns the type of projectile we want this weapon to fire right now. */
function class<Projectile> GetDesiredProjectileClass()
{
if( HoldTime < (MaxChargeTime * 0.33) )
{
return WeakProjectileClass;
}
else if( HoldTime < (MaxChargeTime * 0.66) )
{
return default.ProjectileClass;
}
else
{
return StrongProjectileClass;
}
}
/* Convenient place to perform changes to a newly spawned projectile */
function PostSpawnProjectile(Projectile P)
{
Super.PostSpawnProjectile(P);
if( HoldTime < MaxChargeTime )
{
HuskGunProjectile(p).ImpactDamage *= HoldTime * 2.5;
HuskGunProjectile(p).Damage *= (1.0 + (HoldTime/MaxChargeTime));// up to double damage
HuskGunProjectile(p).DamageRadius *= (1.0 + (HoldTime/(MaxChargeTime/2.0)));// up 3x the damage radius
}
else
{
HuskGunProjectile(p).ImpactDamage *= MaxChargeTime * 2.5;
HuskGunProjectile(p).Damage *= 2.0;// up to double damage
HuskGunProjectile(p).DamageRadius *= 3.0;// up 3x the damage radius
}
}
// Handle setting new recoil
simulated function HandleRecoil(float Rec)
{
local rotator NewRecoilRotation;
local KFPlayerController KFPC;
local KFPawn KFPwn;
local vector AdjustedVelocity;
local float AdjustedSpeed;
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;
if( Weapon.Owner != none && Weapon.Owner.Physics == PHYS_Falling &&
Weapon.Owner.PhysicsVolume.Gravity.Z > class'PhysicsVolume'.default.Gravity.Z )
{
AdjustedVelocity = Weapon.Owner.Velocity;
// Ignore Z velocity in low grav so we don't get massive recoil
AdjustedVelocity.Z = 0;
AdjustedSpeed = VSize(AdjustedVelocity);
//log("AdjustedSpeed = "$AdjustedSpeed$" scale = "$(AdjustedSpeed* RecoilVelocityScale * 0.5));
// Reduce the falling recoil in low grav
NewRecoilRotation.Pitch += (AdjustedSpeed* 3 * 0.5);
NewRecoilRotation.Yaw += (AdjustedSpeed* 3 * 0.5);
}
else
{
//log("Velocity = "$VSize(Weapon.Owner.Velocity)$" scale = "$(VSize(Weapon.Owner.Velocity)* RecoilVelocityScale));
NewRecoilRotation.Pitch += (VSize(Weapon.Owner.Velocity)* 3);
NewRecoilRotation.Yaw += (VSize(Weapon.Owner.Velocity)* 3);
}
NewRecoilRotation.Pitch += (Instigator.HealthMax / Instigator.Health * 5);
NewRecoilRotation.Yaw += (Instigator.HealthMax / Instigator.Health * 5);
NewRecoilRotation *= Rec;
KFPC.SetRecoil(NewRecoilRotation,RecoilRate * (default.FireRate/FireRate));
}
}
event ModeDoFire()
{
local float Rec;
local float AmmoAmountToUse;
if (!AllowFire())
return;
Spread = Default.Spread;
Rec = 1;
if ( KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo) != none && KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill != none )
{
Spread *= KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill.Static.ModifyRecoilSpread(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo), self, Rec);
}
if( !bFiringDoesntAffectMovement )
{
if (FireRate > 0.25)
{
Instigator.Velocity.x *= 0.1;
Instigator.Velocity.y *= 0.1;
}
else
{
Instigator.Velocity.x *= 0.5;
Instigator.Velocity.y *= 0.5;
}
}
if (!AllowFire())
return;
if (MaxHoldTime > 0.0)
HoldTime = FMin(HoldTime, MaxHoldTime);
// server
if (Weapon.Role == ROLE_Authority)
{
if( HoldTime < MaxChargeTime )
{
AmmoAmountToUse = (1.0 + (HoldTime/(MaxChargeTime/9.0)));// 10 ammo for full charge, at least 1 ammo used
}
else
{
AmmoAmountToUse = 10.0;// 10 ammo for full charge, at least 1 ammo used
}
if( Weapon.AmmoAmount(ThisModeNum) < AmmoAmountToUse )
{
AmmoAmountToUse = Weapon.AmmoAmount(ThisModeNum);
}
Weapon.ConsumeAmmo(ThisModeNum, AmmoAmountToUse);
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();
PlayFiring();
FlashMuzzleFlash();
StartMuzzleSmoke();
}
else // server
{
ServerPlayFiring();
}
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();
}
// client
if (Instigator.IsLocallyControlled())
{
HandleRecoil(Rec);
}
}
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();
}
defaultproperties
{
bFireOnRelease=true
FireAimedAnim=Fire_Iron
KickMomentum=(X=0,y=0,Z=0)
ProjPerFire=1
TransientSoundVolume=2.0
TransientSoundRadius=500.000000
FireSoundRef="KF_HuskGunSnd.HuskGun_Fire"
StereoFireSoundRef="KF_HuskGunSnd.HuskGun_FireST"
AmbientFireSoundRef="KF_HuskGunSnd.ChargedLoop"
AmbientChargeUpSoundRef="KF_HuskGunSnd.ChargeUp"
NoAmmoSoundRef="KF_BaseHusk.Husk_WindDown01"
AmbientFireSoundRadius=500
AmbientFireVolume=255
FireForce="AssaultRifleFire"
FireRate=0.75
AmmoClass=Class'KFMod.HuskGunAmmo'
ProjectileClass=Class'KFMod.HuskGunProjectile'
WeakProjectileClass=Class'KFMod.HuskGunProjectile_Weak'
StrongProjectileClass=Class'KFMod.HuskGunProjectile_Strong'
BotRefireRate=1.800000
aimerror=42.000000
Spread=0.015
SpreadStyle=SS_Random
ProjSpawnOffset=(X=50,Y=10,Z=-20)
FlashEmitterClass=Class'ROEffects.MuzzleFlash1stHusk'
ChargeEmitterClass=Class'ROEffects.ChargeUp1stHusk'
MaxChargeTime=3.0
EffectiveRange=5000.000000
maxVerticalRecoilAngle=1500
maxHorizontalRecoilAngle=250
bWaitForRelease=true
//** View shake **//
ShakeOffsetMag=(X=6.0,Y=2.0,Z=10.0)
ShakeOffsetRate=(X=1000.0,Y=1000.0,Z=1000.0)
ShakeOffsetTime=3.0
ShakeRotMag=(X=50.0,Y=50.0,Z=400.0)
ShakeRotRate=(X=12500.0,Y=12500.0,Z=12500.0)
ShakeRotTime=5.0
bRandomPitchFireSound=true
}