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

312 lines
9.4 KiB
Ucode

class ScrnFNFALFire extends FNFALFire;
var int BurstSize;
var transient int BurstShotCount; //how many bullets were fired in the current burst?
var transient float FireBurstEndTime; //this is just to be sure we don't stuck inside FireBurst state, if shit happens
// fixes double shot bug -- PooSH
state FireLoop
{
function BeginState()
{
super.BeginState();
NextFireTime = Level.TimeSeconds - 0.000001; //fire now!
}
}
state WaitingForFireButtonRelease
{
function PlayFiring() {}
function ServerPlayFiring() {}
function PlayFireEnd() {}
function ModeDoFire() {}
}
state FireBurst
{
function BeginState()
{
NumShotsInBurst = 0;
BurstShotCount = 0;
NextFireTime = Level.TimeSeconds - 0.0001; //fire now!
FireBurstEndTime = Level.TimeSeconds + ( FireRate * BurstSize ) + 0.1; // if shit happens - get us out of this state when this time hits
}
function EndState()
{
PlayFireEnd();
}
function StopFiring()
{
GotoState('');
}
function ModeTick(float dt)
{
Super.ModeTick(dt);
if ( !bIsFiring || !AllowFire() ) // stopped firing, magazine empty
GotoState('');
else if ( Level.TimeSeconds > FireBurstEndTime )
{
GotoState('');
log("ScrnM4203BulletFire stuck inside FireBurst state after making "$BurstShotCount$" shots! Getting us out of it.", 'ScrnBalance');
}
}
// Calculate modifications to spread
simulated function float GetSpread()
{
local float NewSpread;
local float AccuracyMod;
AccuracyMod = 1.0;
// Spread bonus for firing aiming
if( KFWeap.bAimingRifle )
{
AccuracyMod *= 0.5;
}
// Small spread bonus for firing crouched
if( Instigator != none && Instigator.bIsCrouched )
{
AccuracyMod *= 0.85;
}
NumShotsInBurst++;
// Small spread bonus for firing in semi auto mode
// make spread bonus for first 2 shots -- PooSH
if( NumShotsInBurst < 2 || (bAccuracyBonusForSemiAuto && bWaitForRelease) )
{
AccuracyMod *= 0.85;
}
if ( Level.TimeSeconds - LastFireTime > 0.5 ) {
NewSpread = Default.Spread;
NumShotsInBurst=0;
}
else if (BurstSize <= 2) {
//2-nd shot doesn't get spread penalty
NewSpread = Default.Spread;
}
else {
// Decrease accuracy up to MaxSpread by the number of recent shots up to a max of six
NewSpread = FMin(Default.Spread + (NumShotsInBurst * (MaxSpread/6.0)),MaxSpread);
}
NewSpread *= AccuracyMod;
return NewSpread;
}
//copy-pasted from KFFire
function ModeDoFire()
{
local float Rec;
local KFPlayerReplicationInfo KFPRI;
if (!AllowFire())
return;
if( Instigator==None || Instigator.Controller==none )
return;
KFPRI = KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo);
Spread = GetSpread();
Rec = GetFireSpeed();
FireRate = default.FireRate/Rec;
FireAnimRate = default.FireAnimRate*Rec;
ReloadAnimRate = default.ReloadAnimRate*Rec;
Rec = 1;
if ( KFPRI != none && KFPRI.ClientVeteranSkill != none )
{
Spread *= KFPRI.ClientVeteranSkill.Static.ModifyRecoilSpread(KFPRI, self, Rec);
}
LastFireTime = Level.TimeSeconds;
if (Weapon.Owner != none && AllowFire() && !bFiringDoesntAffectMovement)
{
if (FireRate > 0.25)
{
Weapon.Owner.Velocity.x *= 0.1;
Weapon.Owner.Velocity.y *= 0.1;
}
else
{
Weapon.Owner.Velocity.x *= 0.5;
Weapon.Owner.Velocity.y *= 0.5;
}
}
Super(WeaponFire).ModeDoFire();
// client
if (Instigator.IsLocallyControlled())
{
if( bDoClientRagdollShotFX && Weapon.Level.NetMode == NM_Client )
{
DoClientOnlyFireEffect();
}
//reduce recoil for first 2 bullets
if (NumShotsInBurst <= 1) {
maxVerticalRecoilAngle = default.maxVerticalRecoilAngle * 0.1;
maxHorizontalRecoilAngle = default.maxHorizontalRecoilAngle * 0.1;
}
HandleRecoil(Rec);
//restore defaults
maxVerticalRecoilAngle = default.maxVerticalRecoilAngle;
maxHorizontalRecoilAngle = default.maxHorizontalRecoilAngle;
}
if ( ++BurstShotCount >= BurstSize ) {
GotoState('WaitingForFireButtonRelease');
return;
}
}
}
//add 1 penetration
function DoTrace(Vector Start, Rotator Dir)
{
local Vector X,Y,Z, End, HitLocation, HitNormal, ArcEnd;
local Actor Other;
local byte HitCount,HCounter;
local float HitDamage;
local array<int> HitPoints;
local KFPawn HitPawn;
local array<Actor> IgnoreActors;
local Actor DamageActor;
local int i;
MaxRange();
Weapon.GetViewAxes(X, Y, Z);
if ( Weapon.WeaponCentered() )
{
ArcEnd = (Instigator.Location + Weapon.EffectOffset.X * X + 1.5 * Weapon.EffectOffset.Z * Z);
}
else
{
ArcEnd = (Instigator.Location + Instigator.CalcDrawOffset(Weapon) + Weapon.EffectOffset.X * X +
Weapon.Hand * Weapon.EffectOffset.Y * Y + Weapon.EffectOffset.Z * Z);
}
X = Vector(Dir);
End = Start + TraceRange * X;
HitDamage = DamageMax;
While( (HitCount++)<2 ) // 1 penetration
{
DamageActor = none;
Other = Instigator.HitPointTrace(HitLocation, HitNormal, End, HitPoints, Start,, 1);
if( Other==None )
{
Break;
}
else if( Other==Instigator || Other.Base == Instigator )
{
IgnoreActors[IgnoreActors.Length] = Other;
Other.SetCollision(false);
Start = HitLocation;
Continue;
}
if( ExtendedZCollision(Other)!=None && Other.Owner!=None )
{
IgnoreActors[IgnoreActors.Length] = Other;
IgnoreActors[IgnoreActors.Length] = Other.Owner;
Other.SetCollision(false);
Other.Owner.SetCollision(false);
DamageActor = Pawn(Other.Owner);
}
if ( !Other.bWorldGeometry && Other!=Level )
{
HitPawn = KFPawn(Other);
if ( HitPawn != none )
{
// Hit detection debugging
/*log("PreLaunchTrace hit "$HitPawn.PlayerReplicationInfo.PlayerName);
HitPawn.HitStart = Start;
HitPawn.HitEnd = End;*/
if(!HitPawn.bDeleteMe)
HitPawn.ProcessLocationalDamage(int(HitDamage), Instigator, HitLocation, Momentum*X,DamageType,HitPoints);
// Hit detection debugging
/*if( Level.NetMode == NM_Standalone)
HitPawn.DrawBoneLocation();*/
IgnoreActors[IgnoreActors.Length] = Other;
IgnoreActors[IgnoreActors.Length] = HitPawn.AuxCollisionCylinder;
Other.SetCollision(false);
HitPawn.AuxCollisionCylinder.SetCollision(false);
DamageActor = Other;
}
else
{
if( KFMonster(Other)!=None )
{
IgnoreActors[IgnoreActors.Length] = Other;
Other.SetCollision(false);
DamageActor = Other;
}
else if( DamageActor == none )
{
DamageActor = Other;
}
Other.TakeDamage(int(HitDamage), Instigator, HitLocation, Momentum*X, DamageType);
}
if( (HCounter++)>=3 || Pawn(DamageActor)==None )
{
Break;
}
//Big zeds (Blot, Husk, SC, FP) significantly reduce further penetration damage
if (KFMonster(Other) != none && KFMonster(Other).default.Health >= 500)
HitDamage*=0.25;
else
HitDamage*=0.75;
Start = HitLocation;
}
else if ( HitScanBlockingVolume(Other)==None )
{
if( KFWeaponAttachment(Weapon.ThirdPersonActor)!=None )
KFWeaponAttachment(Weapon.ThirdPersonActor).UpdateHit(Other,HitLocation,HitNormal);
Break;
}
}
// Turn the collision back on for any actors we turned it off
if ( IgnoreActors.Length > 0 )
{
for (i=0; i<IgnoreActors.Length; i++)
{
if ( IgnoreActors[i] != none )
IgnoreActors[i].SetCollision(true);
}
}
}
defaultproperties
{
BurstSize=2
maxVerticalRecoilAngle=500
maxHorizontalRecoilAngle=250
DamageType=Class'ScrnBalanceSrv.ScrnDamTypeFNFALAssaultRifle'
FireAnimRate=0.571333
FireLoopAnimRate=0.571333
FireRate=0.150000
AmmoClass=Class'ScrnBalanceSrv.ScrnFNFALAmmo'
Spread=0.007500
}