443 lines
13 KiB
Ucode
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
|
|
}
|