This patch dramatically reduces recoil on all guns and lets XMV850 accumulate way more damage that previously possible.
760 lines
31 KiB
Ucode
760 lines
31 KiB
Ucode
class NiceFire extends KFFire
|
|
abstract;
|
|
var bool bDoSpam;
|
|
var name FireIncompleteAnim;
|
|
var name FireIncompleteAimedAnim;
|
|
var name FireIncompleteLoopAnim;
|
|
var name FireIncompleteLoopAimedAnim;
|
|
var float zedTimeFireSpeedUp;
|
|
// Projectile-related variables
|
|
var() bool bProjectileFire;
|
|
var() bool bCanFireIncomplete;
|
|
var() int ProjPerFire;
|
|
var() Vector ProjSpawnOffset;
|
|
var() float EffectiveRange;
|
|
var() vector KickMomentum;
|
|
var() float LowGravKickMomentumScale; // How much to scale up kick momentum in low grav so guys fly around hilariously :)
|
|
var() float ProjectileSpeed; // How fast projectile should move
|
|
var() bool bDisabled;
|
|
var() bool bSemiMustBurst;
|
|
var() int MaxBurstLength;
|
|
var() int burstShotsMade;
|
|
var bool bResetRecoil; // Set this flag to 'true' to disable recoil for the next shot; flag will be automatically reset to 'false' after that
|
|
var float aimingSpreadReductionCoefficient;
|
|
var bool zoomOutOnShot;
|
|
var bool bShouldBounce;
|
|
var bool bCausePain;
|
|
var bool bBetterBurst;
|
|
var class<NiceBullet> bulletClass;
|
|
var class<NiceWeaponDamageType> explosionDamageType;
|
|
var int explosionDamage;
|
|
var float explosionRadius;
|
|
var float explosionExponent;
|
|
var float explosionMomentum;
|
|
var float fuseTime;
|
|
var bool explodeOnFuse;
|
|
var bool explodeOnPawnHit;
|
|
var bool explodeOnWallHit;
|
|
var bool projAffectedByScream;
|
|
var bool bShouldStick;
|
|
var int resetTicks;
|
|
var float niceNextFireTime;
|
|
var float minimalSpreadScale;
|
|
var float activeSpreadScale;
|
|
var float spreadGainedPerShot;
|
|
var float spreadLostPerSecond;
|
|
struct FireModeContext{
|
|
var bool bHipfire;
|
|
var NiceWeapon sourceWeapon;
|
|
var NiceHumanPawn Instigator;
|
|
var bool bIsBursting;
|
|
var int burstLength;
|
|
var float continiousBonus;
|
|
var float lockonTime;
|
|
var NiceMonster lockonZed;
|
|
};
|
|
var FireModeContext currentContext;
|
|
// Secondary fire effect, - allows to spawn additional projectiles along with main hitscan/projectile
|
|
struct ShotType{
|
|
var bool bShouldBounce; // Bullets should bounce off the walls
|
|
var int damage;
|
|
var int projPerFire;
|
|
var float spread;
|
|
var float projSpeed;
|
|
var float momentum;
|
|
var ESpreadStyle spreadStyle;
|
|
var class<NiceWeaponDamageType> shotDamageType;
|
|
var class<NiceWeaponDamageType> explosionDamageType;
|
|
var class<NiceBullet> bulletClass;
|
|
var int explosionDamage;
|
|
var float explosionRadius;
|
|
var float explosionExponent;
|
|
var float explosionMomentum;
|
|
var float fuseTime;
|
|
var bool bCausePain;
|
|
var bool explodeOnFuse;
|
|
var bool explodeOnPawnHit;
|
|
var bool explodeOnWallHit;
|
|
var bool projAffectedByScream;
|
|
var bool bShouldStick;
|
|
};
|
|
// All the available shot types; shot type at index zero is auto-filled from the usual kf's parameters
|
|
var array<ShotType> fireShots;
|
|
// Index of the shot we're currently generating
|
|
var int currentShot;
|
|
// Variables for managing damage boosts for fast firing
|
|
var float contBonus;
|
|
var bool contBonusReset; // does bonus reset after reaching the top value? 'false' means it'll keep being maxed out until player releases fire button
|
|
var int maxBonusContLenght;
|
|
var int currentContLenght;
|
|
var float period;
|
|
static function PreloadAssets(LevelInfo LevelInfo, optional KFFire Spawned){
|
|
local int i;
|
|
if(default.FireSound == none && default.FireSoundRef != "")
|
|
default.FireSound = sound(DynamicLoadObject(default.FireSoundRef, class'Sound', true));
|
|
if(default.StereoFireSound == none){
|
|
if(default.StereoFireSoundRef != "")
|
|
default.StereoFireSound = sound(DynamicLoadObject(default.StereoFireSoundRef, class'Sound', true));
|
|
else
|
|
default.StereoFireSound = default.FireSound;
|
|
}
|
|
if(default.NoAmmoSound == none && default.NoAmmoSoundRef != "")
|
|
default.NoAmmoSound = sound(DynamicLoadObject(default.NoAmmoSoundRef, class'Sound', true));
|
|
if(Spawned != none){
|
|
Spawned.FireSound = default.FireSound;
|
|
Spawned.StereoFireSound = default.StereoFireSound;
|
|
Spawned.NoAmmoSound = default.NoAmmoSound;
|
|
}
|
|
if(default.bulletClass != none)
|
|
default.bulletClass.static.PreloadAssets();
|
|
for(i = 0;i < default.fireShots.Length;i ++)
|
|
if(default.fireShots[i].bulletClass != none)
|
|
default.fireShots[i].bulletClass.static.PreloadAssets();
|
|
}
|
|
static function bool UnloadAssets(){
|
|
default.FireSound = none;
|
|
default.StereoFireSound = none;
|
|
default.NoAmmoSound = none;
|
|
if(default.bulletClass != none)
|
|
default.bulletClass.static.UnloadAssets();
|
|
return true;
|
|
}
|
|
simulated function PostBeginPlay(){
|
|
local ShotType defaultShotType;
|
|
currentContext.continiousBonus = 1.0;
|
|
currentContext.burstLength = 1;
|
|
// Build index-zero shot type
|
|
defaultShotType.damage = DamageMax;
|
|
defaultShotType.projPerFire = ProjPerFire;
|
|
defaultShotType.spread = Spread;
|
|
defaultShotType.projSpeed = projectileSpeed;
|
|
defaultShotType.momentum = momentum;
|
|
defaultShotType.spreadStyle = spreadStyle;
|
|
defaultShotType.bShouldBounce = bShouldBounce;
|
|
defaultShotType.shotDamageType = class<NiceWeaponDamageType>(DamageType);
|
|
defaultShotType.explosionDamageType = explosionDamageType;
|
|
defaultShotType.bulletClass = bulletClass;
|
|
defaultShotType.explosionDamage = explosionDamage;
|
|
defaultShotType.explosionRadius = explosionRadius;
|
|
defaultShotType.explosionExponent = explosionExponent;
|
|
defaultShotType.explosionMomentum = explosionMomentum;
|
|
defaultShotType.fuseTime = fuseTime;
|
|
defaultShotType.explodeOnFuse = explodeOnFuse;
|
|
defaultShotType.explodeOnPawnHit = explodeOnPawnHit;
|
|
defaultShotType.explodeOnWallHit = explodeOnWallHit;
|
|
defaultShotType.projAffectedByScream = projAffectedByScream;
|
|
defaultShotType.bCausePain = bCausePain;
|
|
defaultShotType.bShouldStick = bShouldStick;
|
|
fireShots[0] = defaultShotType;
|
|
super.PostBeginPlay();
|
|
}
|
|
simulated function int GetBurstLength(){
|
|
local NicePlayerController nicePlayer;
|
|
if(Instigator != none)
|
|
nicePlayer = NicePlayerController(Instigator.Controller);
|
|
if(nicePlayer != none && class'NiceVeterancyTypes'.static.hasSkill(nicePlayer, class'NiceSkillCommandoExplosivePower')) {
|
|
return currentContext.burstLength + 1;
|
|
}
|
|
return currentContext.burstLength;
|
|
}
|
|
|
|
simulated function ModeTick(float delta){
|
|
local float headLevel;
|
|
local NiceMonster currTarget;
|
|
if (spreadLostPerSecond > 0 && activeSpreadScale > minimalSpreadScale
|
|
&& niceNextFireTime + fireRate <= Level.TimeSeconds) {
|
|
activeSpreadScale -= spreadLostPerSecond * delta;
|
|
activeSpreadScale = FMax(minimalSpreadScale, activeSpreadScale);
|
|
}
|
|
if(currentContext.Instigator == none)
|
|
currentContext.Instigator = NiceHumanPawn(Instigator);
|
|
if(currentContext.sourceWeapon == none)
|
|
currentContext.sourceWeapon = NiceWeapon(Weapon);
|
|
if(burstShotsMade >= GetBurstLength() && currentContext.bIsBursting){
|
|
SetTimer(0, false);
|
|
currentContext.bIsBursting = false;
|
|
}
|
|
// Lock-on update
|
|
if(Instigator.Role < Role_AUTHORITY){
|
|
period += delta;
|
|
headLevel = TraceZed(currTarget);
|
|
if(headLevel <= 0.0 || currTarget == none){
|
|
currentContext.lockonTime = 0.0;
|
|
currentContext.lockonZed = none;
|
|
}
|
|
else{
|
|
if(currTarget == currentContext.lockonZed)
|
|
currentContext.lockonTime += delta ;
|
|
else
|
|
currentContext.lockonTime = 0.0;
|
|
currentContext.lockonZed = currTarget;
|
|
}
|
|
if(period > 0.1 && currentContext.lockonTime > 0.0)
|
|
period = 0.0;
|
|
}
|
|
// Reset 'FireCount'
|
|
if(Instigator.Controller.bFire == 0 && Instigator.Controller.bAltFire == 0)
|
|
FireCount = 0;
|
|
super.ModeTick(delta);
|
|
}
|
|
simulated function Timer(){
|
|
if(!AllowFire())
|
|
burstShotsMade = GetBurstLength();
|
|
if(currentContext.bIsBursting && burstShotsMade < GetBurstLength())
|
|
ModeDoFire();
|
|
}
|
|
simulated function bool AllowFire(){
|
|
local int magAmmo;
|
|
local bool allowZeroShot;
|
|
local bool bRegularFire;
|
|
local KFPawn kfPwn;
|
|
bRegularFire = !currentContext.sourceWeapon.bHasSecondaryAmmo || ThisModeNum == 0;
|
|
//if(niceNextFireTime > Level.TimeSeconds)
|
|
// return false;
|
|
if(currentContext.sourceWeapon == none || Instigator == none)
|
|
return false;
|
|
if(currentContext.bIsBursting && burstShotsMade >= GetBurstLength())
|
|
return false;
|
|
if(currentContext.sourceWeapon.bHasChargePhase && !currentContext.sourceWeapon.bRoundInChamber && bRegularFire)
|
|
return false;
|
|
if(currentContext.sourceWeapon.secondaryCharge < default.AmmoPerFire && !bCanFireIncomplete && !bRegularFire)
|
|
return false;
|
|
// Check reloading
|
|
magAmmo = currentContext.sourceWeapon.GetMagazineAmmo();
|
|
if(currentContext.sourceWeapon != none)
|
|
allowZeroShot = (Instigator.Role == Role_AUTHORITY && !currentContext.sourceWeapon.bServerFiredLastShot);
|
|
if(currentContext.sourceWeapon.bIsReloading || (magAmmo < 1 && !allowZeroShot && bRegularFire)
|
|
|| (magAmmo < default.AmmoPerFire && !bCanFireIncomplete && !allowZeroShot && bRegularFire))
|
|
return false;
|
|
// Check pawn actions
|
|
kfPwn = KFPawn(Instigator);
|
|
if(kfPwn == none || kfPwn.SecondaryItem != none || kfPwn.bThrowingNade)
|
|
return false;
|
|
return super(WeaponFire).AllowFire();
|
|
}
|
|
simulated function DoBurst(optional bool bSkipFirstShot){
|
|
local NicePlayerController nicePlayer;
|
|
local class<NiceVeterancyTypes> niceVet;
|
|
if(NextFireTime > Level.TimeSeconds || currentContext.bIsBursting)
|
|
return;
|
|
nicePlayer = NicePlayerController(Instigator.Controller);
|
|
if(nicePlayer != none)
|
|
niceVet = class'NiceVeterancyTypes'.static.GetVeterancy(nicePlayer.PlayerReplicationInfo);
|
|
currentContext.bIsBursting = true;
|
|
burstShotsMade = 0;
|
|
FireCount = 0;
|
|
if(!bSkipFirstShot)
|
|
ModeDoFire();
|
|
SetTimer(FireRate / GetBurstLength(), true);
|
|
}
|
|
event ModeDoFire(){
|
|
local float Rec;
|
|
local int magAmmo;
|
|
local bool bForceBurst;
|
|
if(bDisabled || Instigator == none || Instigator.Controller == none || currentContext.sourceWeapon == none)
|
|
return;
|
|
// Update how much we can fire
|
|
magAmmo = currentContext.sourceWeapon.GetMagazineAmmo();
|
|
if(bCanFireIncomplete)
|
|
AmmoPerFire = min(default.AmmoPerFire, magAmmo);
|
|
else
|
|
AmmoPerFire = default.AmmoPerFire;
|
|
UpdateFireSpeed();
|
|
// Should we be allowed to fire?
|
|
if(!AllowFire())
|
|
return;
|
|
// Bursting
|
|
bForceBurst = bSemiMustBurst && bWaitForRelease;
|
|
if(!currentContext.bIsBursting && bForceBurst)
|
|
DoBurst(true);
|
|
if(currentContext.bIsBursting)
|
|
burstShotsMade ++;
|
|
// If we made it this far with zero AmmoPerFire, - it's a last shot that can be incomplete and was enforced, so make it shoot something
|
|
if(AmmoPerFire <= 0 && bCanFireIncomplete)
|
|
AmmoPerFire = 1;
|
|
if(Level.TimeSeconds > niceNextFireTime + FireRate){
|
|
currentContLenght = 1;
|
|
currentContext.continiousBonus = 1.0;
|
|
}
|
|
else{
|
|
currentContLenght ++;
|
|
if(currentContLenght > maxBonusContLenght)
|
|
{
|
|
if(contBonusReset)
|
|
currentContext.continiousBonus = 1.0;
|
|
}
|
|
else
|
|
currentContext.continiousBonus *= contBonus;
|
|
}
|
|
MDFEffects(AmmoPerFire);
|
|
if(Instigator.Role == Role_AUTHORITY)
|
|
MDFEffectsServer(AmmoPerFire);
|
|
else{
|
|
// Compute recoil
|
|
Rec = 1.0;
|
|
if(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo) != none && KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill != none)
|
|
KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill.static.ModifyRecoilSpread(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo), self, Rec);
|
|
if(currentContext.bIsBursting)
|
|
Rec = Rec / GetBurstLength();
|
|
MDFEffectsClient(AmmoPerFire, Rec);
|
|
}
|
|
}
|
|
// Fire effects that should affect both client and server
|
|
simulated function MDFEffects(float newAmmoPerFire){
|
|
local NicePlayerController nicePlayer;
|
|
if(instigator != none)
|
|
nicePlayer = NicePlayerController(instigator.controller);
|
|
if(Weapon.Owner != none && !bFiringDoesntAffectMovement && Weapon.Owner.Physics != PHYS_Falling){
|
|
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;
|
|
}
|
|
}
|
|
if(MaxHoldTime > 0.0)
|
|
HoldTime = FMin(HoldTime, MaxHoldTime);
|
|
Weapon.IncrementFlashCount(ThisModeNum);
|
|
niceNextFireTime = UpdateNextFireTime(niceNextFireTime);
|
|
NextFireTime = niceNextFireTime;
|
|
AmmoPerFire = newAmmoPerFire;
|
|
Load = AmmoPerFire;
|
|
HoldTime = 0;
|
|
if(currentContext.sourceWeapon != none && (zoomOutOnShot || currentContext.sourceWeapon.reloadType == RTYPE_AUTO))
|
|
currentContext.sourceWeapon.ZoomOut(false);
|
|
if(Instigator.PendingWeapon != Weapon && Instigator.PendingWeapon != none){
|
|
bIsFiring = false;
|
|
Weapon.PutDown();
|
|
}
|
|
for(currentShot = 0;currentShot < fireShots.Length;currentShot ++)
|
|
DoFireEffect();
|
|
}
|
|
// Fire effects that should only affect shooting client.
|
|
simulated function MDFEffectsClient(float newAmmoPerFire, float Rec){
|
|
local NicePlayerController nicePlayer;
|
|
if(Instigator != none)
|
|
nicePlayer = NicePlayerController(Instigator.Controller);
|
|
if(nicePlayer == none || !nicePlayer.IsZedTimeActive() || !class'NiceVeterancyTypes'.static.hasSkill(nicePlayer, class'NiceSkillSharpshooterZEDHundredGauntlets'))
|
|
ReduceAmmoClient();
|
|
InitEffects();
|
|
ShakeView();
|
|
PlayFiring();
|
|
FlashMuzzleFlash();
|
|
StartMuzzleSmoke();
|
|
currentContext.lockonTime = 0.0;
|
|
currentContext.lockonZed = none;
|
|
if(bDoClientRagdollShotFX && Weapon.Level.NetMode == NM_Client)
|
|
DoClientOnlyFireEffect();
|
|
HandleRecoil(Rec);
|
|
}
|
|
// Fire effects that should only be done on server
|
|
simulated function MDFEffectsServer(float newAmmoPerFire){
|
|
local NicePlayerController nicePlayer;
|
|
if(Instigator != none)
|
|
nicePlayer = NicePlayerController(Instigator.Controller);
|
|
HoldTime = 0;
|
|
if((Instigator == none) || (Instigator.Controller == none))
|
|
return;
|
|
Instigator.DeactivateSpawnProtection();
|
|
ServerPlayFiring();
|
|
if(currentContext.sourceWeapon.MagAmmoRemaining <= 0)
|
|
currentContext.sourceWeapon.bServerFiredLastShot = true;
|
|
}
|
|
simulated function ReduceAmmoClient(){
|
|
if(currentContext.sourceWeapon.MagAmmoRemainingClient > 0 && (ThisModeNum == 0 || !currentContext.sourceWeapon.bHasSecondaryAmmo)){
|
|
currentContext.sourceWeapon.MagAmmoRemainingClient -= Load;
|
|
if(currentContext.sourceWeapon.MagAmmoRemainingClient <= 0){
|
|
currentContext.sourceWeapon.MagAmmoRemainingClient = 0;
|
|
currentContext.sourceWeapon.bRoundInChamber = false;
|
|
}
|
|
// Force server's magazine size
|
|
currentContext.sourceWeapon.ServerReduceMag(currentContext.sourceWeapon.MagAmmoRemainingClient, Level.TimeSeconds, ThisModeNum);
|
|
}
|
|
else if(ThisModeNum == 1){
|
|
currentContext.sourceWeapon.secondaryCharge -= Load;
|
|
currentContext.sourceWeapon.ServerReduceMag(currentContext.sourceWeapon.MagAmmoRemainingClient, Level.TimeSeconds, ThisModeNum);
|
|
currentContext.sourceWeapon.ServerSetSndCharge(currentContext.sourceWeapon.secondaryCharge);
|
|
}
|
|
}
|
|
function name GetCorrectAnim(bool bLoop, bool bAimed){
|
|
local bool bIncomplete;
|
|
bIncomplete = Load < default.AmmoPerFire;
|
|
if(bLoop){
|
|
if(bAimed){
|
|
if(bIncomplete && Weapon.HasAnim(FireIncompleteLoopAimedAnim))
|
|
return FireIncompleteLoopAimedAnim;
|
|
else
|
|
return FireLoopAimedAnim;
|
|
}
|
|
else{
|
|
if(bIncomplete && Weapon.HasAnim(FireIncompleteLoopAnim))
|
|
return FireIncompleteLoopAnim;
|
|
else
|
|
return FireLoopAnim;
|
|
}
|
|
}
|
|
else{
|
|
if(bAimed){
|
|
if(bIncomplete && Weapon.HasAnim(FireIncompleteAimedAnim))
|
|
return FireIncompleteAimedAnim;
|
|
else
|
|
return FireAimedAnim;
|
|
}
|
|
else{
|
|
if(bIncomplete && Weapon.HasAnim(FireIncompleteAnim))
|
|
return FireIncompleteAnim;
|
|
else
|
|
return FireAnim;
|
|
}
|
|
}
|
|
return FireAnim;
|
|
}
|
|
function PlayFiring(){
|
|
local float RandPitch;
|
|
if(Weapon.Mesh != none){
|
|
if(FireCount > 0){
|
|
if(KFWeap.bAimingRifle){
|
|
if(Weapon.HasAnim(FireLoopAimedAnim))
|
|
Weapon.PlayAnim(GetCorrectAnim(true, true), FireLoopAnimRate, 0.0);
|
|
else if(Weapon.HasAnim(FireAimedAnim))
|
|
Weapon.PlayAnim(GetCorrectAnim(false, true), FireAnimRate, TweenTime);
|
|
else
|
|
Weapon.PlayAnim(GetCorrectAnim(false, false), FireAnimRate, TweenTime);
|
|
}
|
|
else{
|
|
if(Weapon.HasAnim(FireLoopAnim))
|
|
Weapon.PlayAnim(GetCorrectAnim(true, false), FireLoopAnimRate, 0.0);
|
|
else
|
|
Weapon.PlayAnim(GetCorrectAnim(false, false), FireAnimRate, TweenTime);
|
|
}
|
|
}
|
|
else{
|
|
if(KFWeap.bAimingRifle){
|
|
if(Weapon.HasAnim(FireAimedAnim))
|
|
Weapon.PlayAnim(GetCorrectAnim(false, true), FireAnimRate, TweenTime);
|
|
else
|
|
Weapon.PlayAnim(GetCorrectAnim(false, false), FireAnimRate, TweenTime);
|
|
}
|
|
else
|
|
Weapon.PlayAnim(GetCorrectAnim(false, false), 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);
|
|
if(!currentContext.bIsBursting)
|
|
FireCount ++;
|
|
}
|
|
|
|
// Handle setting new recoil
|
|
simulated function HandleRecoil(float Rec)
|
|
{
|
|
local int stationarySeconds;
|
|
local rotator NewRecoilRotation;
|
|
local NicePlayerController nicePlayer;
|
|
local NiceHumanPawn nicePawn;
|
|
local vector AdjustedVelocity;
|
|
local float AdjustedSpeed;
|
|
local KFWeapon KFW;
|
|
|
|
if(Instigator != none)
|
|
{
|
|
nicePlayer = NicePlayerController(Instigator.Controller);
|
|
nicePawn = NiceHumanPawn(Instigator);
|
|
}
|
|
if(nicePlayer == none || nicePawn == none)
|
|
return;
|
|
if(bResetRecoil || nicePlayer.IsZedTimeActive() && class'NiceVeterancyTypes'.static.hasSkill(nicePlayer, class'NiceSkillEnforcerZEDBarrage'))
|
|
{
|
|
Rec = 0.0;
|
|
bResetRecoil = false;
|
|
}
|
|
KFW= KFWeapon(Weapon);
|
|
if (KFW.bAimingRifle)
|
|
{
|
|
Rec *= aimingSpreadReductionCoefficient;
|
|
}
|
|
if (nicePawn.stationaryTime > 0.0 && class'NiceVeterancyTypes'.static.hasSkill(nicePlayer, class'NiceSkillHeavyStablePosition'))
|
|
{
|
|
stationarySeconds = Ceil(2 * nicePawn.stationaryTime) - 1;
|
|
Rec *= FMax(0.0, 1.0 - (stationarySeconds * class'NiceSkillHeavyStablePosition'.default.recoilDampeningBonus));
|
|
}
|
|
if (!nicePlayer.bFreeCamera)
|
|
{
|
|
if (Weapon.GetFireMode(ThisModeNum).bIsFiring || currentContext.bIsBursting)
|
|
{
|
|
NewRecoilRotation.Pitch = RandRange(maxVerticalRecoilAngle * 0.5, maxVerticalRecoilAngle);
|
|
NewRecoilRotation.Yaw = RandRange(maxHorizontalRecoilAngle * 0.5, maxHorizontalRecoilAngle);
|
|
|
|
if (!bRecoilRightOnly && Rand(2) == 1)
|
|
NewRecoilRotation.Yaw *= -1;
|
|
|
|
if (RecoilVelocityScale > 0)
|
|
{
|
|
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);
|
|
|
|
// Reduce the falling recoil in low grav
|
|
NewRecoilRotation.Pitch += (AdjustedSpeed * RecoilVelocityScale * 0.5);
|
|
NewRecoilRotation.Yaw += (AdjustedSpeed * RecoilVelocityScale * 0.5);
|
|
}
|
|
else
|
|
{
|
|
NewRecoilRotation.Pitch += (VSize(Weapon.Owner.Velocity) * RecoilVelocityScale);
|
|
NewRecoilRotation.Yaw += (VSize(Weapon.Owner.Velocity) * RecoilVelocityScale);
|
|
}
|
|
}
|
|
|
|
NewRecoilRotation.Pitch += (Instigator.HealthMax / Instigator.Health * 5);
|
|
NewRecoilRotation.Yaw += (Instigator.HealthMax / Instigator.Health * 5);
|
|
NewRecoilRotation *= Rec;
|
|
|
|
if (default.FireRate <= 0)
|
|
nicePlayer.SetRecoil(NewRecoilRotation, RecoilRate);
|
|
else
|
|
nicePlayer.SetRecoil(NewRecoilRotation, RecoilRate * (FireRate / default.FireRate));
|
|
}
|
|
}
|
|
}
|
|
|
|
function DoFireEffect(){
|
|
local bool bIsShotgunBullet, bForceComplexTraj;
|
|
local bool bPinpoint;
|
|
local Vector StartProj, StartTrace, X,Y,Z;
|
|
local Rotator R, Aim;
|
|
local Vector HitLocation, HitNormal;
|
|
local Actor other;
|
|
local int p;
|
|
local float activeSpread;
|
|
local ESpreadStyle activeSpreadStyle;
|
|
local int SpawnCount;
|
|
local float theta;
|
|
local NicePlayerController nicePlayer;
|
|
nicePlayer = NicePlayerController(Instigator.Controller);
|
|
Instigator.MakeNoise(1.0);
|
|
Weapon.GetViewAxes(X, Y, Z);
|
|
StartTrace = Instigator.Location + Instigator.EyePosition();
|
|
StartProj = StartTrace + X * ProjSpawnOffset.X;
|
|
if(!Weapon.WeaponCentered() && !KFWeap.bAimingRifle)
|
|
StartProj = StartProj + Weapon.Hand * Y * ProjSpawnOffset.Y + Z * ProjSpawnOffset.Z;
|
|
other = Weapon.Trace(HitLocation, HitNormal, StartProj, StartTrace, false);
|
|
activeSpread = fireShots[currentShot].spread * activeSpreadScale;
|
|
if(class'NiceVeterancyTypes'.static.hasSkill(nicePlayer, class'NiceSkillEnforcerBombard')){
|
|
bPinpoint = true;
|
|
activeSpread *= class'NiceSkillEnforcerBombard'.default.spreadMult;
|
|
}
|
|
bIsShotgunBullet = ClassIsChildOf(fireShots[currentShot].bulletClass, class'NiceShotgunPellet');
|
|
if( bIsShotgunBullet && weapon.class != class'NiceSpas' && weapon.class != class'NiceNailGun'
|
|
&& class'NiceVeterancyTypes'.static.hasSkill(nicePlayer, class'NiceSkillSupportSlugs') )
|
|
activeSpread = 0.0;
|
|
if(bIsShotgunBullet && activeSpread <= 0.0 && !bPinpoint)
|
|
bForceComplexTraj = true;
|
|
if(other != none)
|
|
StartProj = HitLocation;
|
|
Aim = AdjustAim(StartProj, AimError);
|
|
SpawnCount = Max(0, fireShots[currentShot].projPerFire * int(Load));
|
|
if(class'NiceVeterancyTypes'.static.hasSkill(nicePlayer, class'NiceSkillSupportZEDBulletStorm') && nicePlayer.IsZedTimeActive())
|
|
SpawnCount *= class'NiceSkillSupportZEDBulletStorm'.default.projCountMult;
|
|
activeSpreadStyle = fireShots[currentShot].spreadStyle;
|
|
DoTraceHack(StartProj, Aim);
|
|
currentContext.bHipfire = false;
|
|
if(currentContext.sourceWeapon != none && !currentContext.sourceWeapon.bAimingRifle
|
|
&& !currentContext.sourceWeapon.bZoomingIn && !currentContext.sourceWeapon.bZoomingOut
|
|
&& !currentContext.sourceWeapon.bFastZoomOut)
|
|
currentContext.bHipfire = true;
|
|
switch(activeSpreadStyle){
|
|
case SS_Random:
|
|
X = Vector(Aim);
|
|
for(p = 0; p < SpawnCount;p ++){
|
|
if(p > 0 || !bPinpoint){
|
|
R.Yaw = activeSpread * (FRand()-0.5);
|
|
R.Pitch = activeSpread * (FRand()-0.5);
|
|
R.Roll = activeSpread * (FRand()-0.5);
|
|
}
|
|
class'NiceProjectileSpawner'.static.MakeProjectile(StartProj, Rotator(X >> R), fireShots[currentShot], currentContext, bForceComplexTraj);
|
|
}
|
|
break;
|
|
case SS_Line:
|
|
X = Vector(Aim);
|
|
for(p = 0; p < SpawnCount;p ++){
|
|
if(p > 0 || !bPinpoint){
|
|
theta = activeSpread * PI/32768 * (p - float(SpawnCount-1)/2.0);
|
|
X.X = cos(theta);
|
|
X.Y = sin(theta);
|
|
X.Z = 0.0;
|
|
}
|
|
class'NiceProjectileSpawner'.static.MakeProjectile(StartProj, Rotator(X >> Aim), fireShots[currentShot], currentContext, bForceComplexTraj);
|
|
}
|
|
break;
|
|
case SS_None:
|
|
default:
|
|
for(p = 0; p < SpawnCount;p ++)
|
|
class'NiceProjectileSpawner'.static.MakeProjectile(StartProj, Aim, fireShots[currentShot], currentContext, bForceComplexTraj);
|
|
}
|
|
if(Instigator != none && Instigator.Role == Role_AUTHORITY){
|
|
if(Instigator.Physics != PHYS_Falling)
|
|
Instigator.AddVelocity(KickMomentum >> Instigator.GetViewRotation());
|
|
else if(Instigator.Physics == PHYS_Falling && Instigator.PhysicsVolume.Gravity.Z > class'PhysicsVolume'.default.Gravity.Z)
|
|
Instigator.AddVelocity((KickMomentum * LowGravKickMomentumScale) >> Instigator.GetViewRotation());
|
|
}
|
|
if (activeSpreadScale < 1.0) {
|
|
if (KFWeap.bAimingRifle) {
|
|
activeSpreadScale += spreadGainedPerShot * 0.5;
|
|
}
|
|
else {
|
|
activeSpreadScale += spreadGainedPerShot;
|
|
}
|
|
activeSpreadScale = FMin(1.0, activeSpreadScale);
|
|
}
|
|
}
|
|
simulated function float TraceZed(out NiceMonster tracedZed, optional out Vector hitLoc, optional out Vector hitNorm,
|
|
optional float hsMultiplier){
|
|
local Vector Start, End, hitLocation, HitNormal;
|
|
local Rotator aim;
|
|
local Actor tracedActor;
|
|
if(hsMultiplier <= 0.0)
|
|
hsMultiplier = 1.0;
|
|
MaxRange();
|
|
Start = Instigator.Location + Instigator.EyePosition();
|
|
aim = AdjustAim(Start, AimError);
|
|
End = Start + TraceRange * Vector(aim);
|
|
tracedActor = Instigator.Trace(hitLocation, hitNormal, End, Start);
|
|
hitLoc = hitLocation;
|
|
hitNorm = hitNormal;
|
|
tracedZed = NiceMonster(tracedActor);
|
|
if(tracedZed == none && ExtendedZCollision(tracedActor) != none && tracedActor.owner != none)
|
|
tracedZed = NiceMonster(tracedActor.owner);
|
|
if(tracedZed != none)
|
|
return tracedZed.IsHeadshotClient(hitLocation, Vector(aim), tracedZed.clientHeadshotScale * hsMultiplier);
|
|
return 0;
|
|
}
|
|
simulated function TraceWall(out Actor tracedActor, optional out Vector hitLoc, optional out Vector hitNorm){
|
|
local Vector Start, End, hitLocation, hitNormal;
|
|
local Rotator aim;
|
|
MaxRange();
|
|
Start = Instigator.Location + Instigator.EyePosition();
|
|
aim = AdjustAim(Start, AimError);
|
|
End = Start + TraceRange * Vector(Aim);
|
|
tracedActor = Instigator.Trace(hitLocation, hitNormal, End, Start);
|
|
hitLoc = hitLocation;
|
|
hitNorm = hitNormal;
|
|
if(KFPawn(tracedActor) != none && ExtendedZCollision(tracedActor) != none)
|
|
tracedActor = none;
|
|
}
|
|
simulated function NiceReplicationInfo GetNiceRI(){
|
|
if(Instigator != none && NicePlayerController(Instigator.Controller) != none)
|
|
return NicePlayerController(Instigator.Controller).NiceRI;
|
|
return none;
|
|
}
|
|
function DoTraceHack(Vector Start, Rotator Dir){
|
|
local Actor other;
|
|
local array<int> HitPoints;
|
|
local Vector X, End, HitLocation, HitNormal;
|
|
X = Vector(Dir);
|
|
End = Start + TraceRange * X;
|
|
other = Instigator.HitPointTrace(HitLocation, HitNormal, End, HitPoints, Start,, 1);
|
|
if(Trigger(other) != none)
|
|
other.TakeDamage(35, Instigator, HitLocation, Momentum * X, DamageType);
|
|
}
|
|
// All weapons should have 100% accuracy anyway
|
|
simulated function AccuracyUpdate(float Velocity){}
|
|
// This function is called when 'FireRate', 'FireAnimRate' or 'ReloadAnimRate' need to be updated
|
|
simulated function UpdateFireSpeed(){
|
|
local float fireSpeedMod;
|
|
local NicePlayerController nicePlayer;
|
|
fireSpeedMod = GetFireSpeed();
|
|
if (instigator != none) {
|
|
nicePlayer = NicePlayerController(instigator.controller);
|
|
}
|
|
if(NiceSingle(Weapon) != none || NiceDualies(Weapon) != none)
|
|
fireSpeedMod /= (Level.TimeDilation / 1.1);
|
|
fireSpeedMod *= 1.0 + 1.1 * (zedTimeFireSpeedUp - 1.0) * (1.1 - Level.TimeDilation);
|
|
if(nicePlayer != none && nicePlayer.IsZedTimeActive() && class'NiceVeterancyTypes'.static.HasSkill(nicePlayer, class'NiceSkillEnforcerZEDBarrage')) {
|
|
fireSpeedMod *= 2.0;
|
|
}
|
|
FireRate = default.FireRate / fireSpeedMod;
|
|
FireAnimRate = default.FireAnimRate * fireSpeedMod;
|
|
ReloadAnimRate = default.ReloadAnimRate * fireSpeedMod;
|
|
}
|
|
// This function is called when next fire time needs to be updated
|
|
simulated function float UpdateNextFireTime(float fireTimeVar) {
|
|
local float usedFireRate;
|
|
local float burstSlowDown;
|
|
local NiceHumanPawn nicePawn;
|
|
local class<NiceVeterancyTypes> niceVet;
|
|
nicePawn = NiceHumanPawn(Instigator);
|
|
if(nicePawn != none)
|
|
niceVet = class'NiceVeterancyTypes'.static.GetVeterancy(nicePawn.PlayerReplicationInfo);
|
|
fireTimeVar = FMax(fireTimeVar, Level.TimeSeconds);
|
|
usedFireRate = fireRate;
|
|
if (maxBonusContLenght > 1) {
|
|
if (currentContLenght <= maxBonusContLenght) {
|
|
usedFireRate *= 0.8 + 0.2 * (maxBonusContLenght - currentContLenght);
|
|
} else {
|
|
usedFireRate *= 0.8;
|
|
}
|
|
}
|
|
if(bFireOnRelease){
|
|
if(bIsFiring)
|
|
fireTimeVar += MaxHoldTime + usedFireRate;
|
|
else
|
|
fireTimeVar = Level.TimeSeconds + usedFireRate;
|
|
}
|
|
else{
|
|
if(currentContext.bIsBursting && GetBurstLength() > 1){
|
|
if( niceVet != none
|
|
&& (bBetterBurst ||
|
|
niceVet.static.hasSkill(NicePlayerController(nicePawn.Controller), class'NiceSkillCommandoExplosivePower'))
|
|
)
|
|
burstSlowDown = 1.0;
|
|
else
|
|
burstSlowDown = 1.3;
|
|
fireTimeVar += usedFireRate * burstSlowDown;
|
|
}
|
|
else
|
|
fireTimeVar += usedFireRate;
|
|
fireTimeVar = FMax(fireTimeVar, Level.TimeSeconds);
|
|
}
|
|
return fireTimeVar;
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
zedTimeFireSpeedUp=1.000000
|
|
ProjPerFire=1
|
|
ProjectileSpeed=1524.000000
|
|
MaxBurstLength=3
|
|
bulletClass=class'NiceBullet'
|
|
contBonus=1.200000
|
|
contBonusReset=True
|
|
maxBonusContLenght=1
|
|
AmmoPerFire=1
|
|
minimalSpreadScale=1
|
|
activeSpreadScale=1
|
|
spreadGainedPerShot=0
|
|
spreadLostPerSecond=0
|
|
RecoilVelocityScale = 0
|
|
aimingSpreadReductionCoefficient = 0.5
|
|
}
|