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

2092 lines
56 KiB
Ucode

class KFWeapon extends BaseKFWeapon
abstract;
#exec OBJ LOAD FILE=KF_9MMSnd.uax
var() vector FirstPersonFlashlightOffset;
var config enum KFScopeDetailSettings
{
KF_ModelScope,
KF_TextureScope,
KF_ModelScopeHigh,
KF_None
} KFScopeDetail; // Which detail setting for the scope
var bool bHasScope; // true for any sniper weapons, aka they'll have scopes
var(Zooming) float ZoomedDisplayFOVHigh; // What is the DisplayFOV when zoomed in on high scope detail setting
var float ForceZoomOutTime; // When set the weapon will zoom out at this time
var bool bForceLeaveIronsights; // Force the weapon out of ironsights on the next tick
var(Zooming) float ForceZoomOutOnFireTime; // How long to wait after firing to force zoom out. If zero don't force zoom out on fire
var(Zooming) float ForceZoomOutOnAltFireTime; // How long to wait after alt firing to force zoom out. If zero don't force zoom out on alt fire
var() int MagCapacity; // How Much ammo this weapon can hold in its magazine
var() float ReloadRate;
var float ReloadTimer;
var int MagAmmoRemaining;// How many bullets are left in the weapon's magazine
var bool bSpeedMeUp;
var bool bHasSecondaryAmmo; // This weapon has secondary ammo that we need to show on the hud
var bool bReduceMagAmmoOnSecondaryFire; // Some weapons (like the M4 with M203 launcher) don't want to have primary ammo decrement when secondary fire occurs, so set this to false in that case
var() sound ToggleSound;
var() name ReloadAnim;
var() float ReloadAnimRate;
var() bool bHoldToReload;
var bool bDoSingleReload; // The reload key has been released, but no rounds were added yet
var int NumLoadedThisReload; // Tracks number or rounds reloaded this reload cycle
var name FlashBoneName;
var name WeaponReloadAnim;
var() int MinimumFireRange; // Minimum distance to fire ...for avoiding LAW / Flamethrower casualties specifically.
var() name ModeSwitchAnim;
var() material HudImage; // What to display on the HUD, when this weapon isn't selected.
var() material SelectedHudImage; // What to display on the HUD, when this weapon is selected.
var bool bSteadyAim; // If Flicked, this weapon's accuracy is not affected by Movement.
//var() float HealRate;
//var transient float HealAccum;
var bool bIsReloading, bReloadEffectDone;
var() float Weight; // how much does it weigh
var bool bKFNeverThrow;
var bool bAmmoHUDAsBar;
var bool bUseCombos; // Disable combos for the time being...Too iffy
var bool bNoHit; // Hack for Syringe / Welder.
var() bool bTorchEnabled; // just a hook for the pawn, and his light. Dualies and Single have this set to true. the other weapons dont.
var bool bDualWeapon; // This weapon is a weapon where we are holding two of the same type of gun
var int StoppingPower; // How much each fire of the gun slows you down. Always in negative numbers.
// Iron sights aimin
var bool bAimingRifle; // If the weapon is in ironsights
var(Zooming) bool bHasAimingMode; // Whether or not this weapon can go into iron sights
var() name IdleAimAnim; // Idle anim when aiming
var(Zooming) sound AimInSound; // sound to play when going into aiming mode
var(Zooming) sound AimOutSound; // sound to play when going into aiming mode
var() class<InventoryAttachment> TacShineClass;
var Actor TacShine;
var Effect_TacLightProjector FlashLight;
var float NextAmmoCheckTime,LastAmmoResult,LastHasGunMsgTime;
// Keep track of what this weapon is doing on the client while we are throwing a grenade
var() enum EClientGrenadeState
{
GN_None,
GN_TempDown,
GN_BringUp,
} ClientGrenadeState; // this will always be none on the server
// Used for putting down/bringing up the weapon when throwing a grenade
var float QuickPutDownTime;
var float QuickBringUpTime;
var bool bConsumesPhysicalAmmo;
var bool bShowPullOutHint;
var float StandardDisplayFOV; // Stores the original nonadjusted display FOV (because we need to change the default, and want to know what the original default was)
var bool bPendingFlashlight;
var int NumClicks; // How many times the player has dry fired thier weapon
var bool bModeZeroCanDryFire; // FireMode zero can dry fire/cause a reload
// Added so we can do team specific sleeves, and later role specific - Ramm
var() byte SleeveNum; // Which skin is the sleeve?
var texture TraderInfoTexture; // Image for the trader menu
var Vector EndBeamEffect; // Used by weapons with laser sites to designate the end of the laser beam
var int SellValue;
// Achievement Helpers
var bool bPreviouslyDropped;
var bool bIsTier2Weapon;
var bool bIsTier3Weapon;
var PlayerController Tier3WeaponGiver;
// Dynamic Loading
var string MeshRef;
var array<string> SkinRefs;
var string SelectSoundRef;
var string HudImageRef;
var string SelectedHudImageRef;
var int ReferenceCount;
// Weapon DLC
var int AppID;
var int UnlockedByAchievement;
replication
{
reliable if(Role == ROLE_Authority)
MagAmmoRemaining, bForceLeaveIronsights;
reliable if( bNetDirty && bNetOwner && (Role==ROLE_Authority) )
MagCapacity, SellValue;
// TODO - which of these ACTUALLY need sending?
reliable if(Role < ROLE_Authority)
ReloadMeNow, ServerSetAiming, ServerSpawnLight, ServerRequestAutoReload,
ServerInterruptReload;
reliable if(Role == ROLE_Authority)
ClientReload, ClientFinishReloading, ClientReloadEffects, FlashLight,
ClientInterruptReload, ClientForceKFAmmoUpdate;
reliable if(Role < ROLE_Authority)
ServerChangeFireMode;
}
static function PreloadAssets(Inventory Inv, optional bool bSkipRefCount)
{
local int i;
if ( !bSkipRefCount )
{
default.ReferenceCount++;
}
UpdateDefaultMesh(SkeletalMesh(DynamicLoadObject(default.MeshRef, class'SkeletalMesh')));
default.HudImage = texture(DynamicLoadObject(default.HudImageRef, class'texture'));
default.SelectedHudImage = texture(DynamicLoadObject(default.SelectedHudImageRef, class'texture'));
default.SelectSound = sound(DynamicLoadObject(default.SelectSoundRef, class'sound'));
for ( i = 0; i < default.SkinRefs.Length; i++ )
{
default.Skins[i] = Material(DynamicLoadObject(default.SkinRefs[i], class'Material'));
}
if ( KFWeapon(Inv) != none )
{
Inv.LinkMesh(default.Mesh);
KFWeapon(Inv).HudImage = default.HudImage;
KFWeapon(Inv).SelectedHudImage = default.SelectedHudImage;
KFWeapon(Inv).SelectSound = default.SelectSound;
for ( i = 0; i < default.SkinRefs.Length; i++ )
{
Inv.Skins[i] = default.Skins[i];
}
}
}
static function bool UnloadAssets()
{
local int i;
default.ReferenceCount--;
log("UnloadAssets RefCount after: " @ default.ReferenceCount);
UpdateDefaultMesh(none);
default.HudImage = none;
default.SelectedHudImage = none;
for ( i = 0; i < default.SkinRefs.Length; i++ )
{
default.Skins[i] = none;
}
return default.ReferenceCount == 0;
}
simulated function PreTravelCleanUp(){}
simulated function AdjustIngameScope(){}
simulated function bool ShouldDrawPortal(){return false;}
simulated function DisplayDebug(Canvas Canvas, out float YL, out float YPos)
{
if ( FlashLight != none )
FlashLight.DisplayDebug(Canvas, YL, YPos);
super.DisplayDebug(Canvas, YL, YPos);
}
simulated function PostBeginPlay()
{
if ( default.mesh == none )
{
PreloadAssets(self, true);
}
// Weapon will handle FireMode instantiation
Super.PostBeginPlay();
if ( Level.NetMode == NM_DedicatedServer )
return;
if( !bHasScope )
{
KFScopeDetail = KF_None;
}
InitFOV();
}
// Set up the widescreen FOV values for this weapon
simulated final function InitFOV()
{
local KFPlayerController KFPC;
local float ResX, ResY;
local float AspectRatio;
KFPC = KFPlayerController(Level.GetLocalPlayerController());
if( KFPC == none )
{
return;
}
ResX = float(GUIController(KFPC.Player.GUIController).ResX);
ResY = float(GUIController(KFPC.Player.GUIController).ResY);
AspectRatio = ResX / ResY;
if ( KFPC.bUseTrueWideScreenFOV && AspectRatio >= 1.60 ) //1.6 = 16/10 which is 16:10 ratio and 16:9 comes to 1.77
{
PlayerIronSightFOV = CalcFOVForAspectRatio(default.PlayerIronSightFOV);
ZoomedDisplayFOV = CalcFOVForAspectRatio(default.ZoomedDisplayFOV);
ZoomedDisplayFOVHigh = CalcFOVForAspectRatio(default.ZoomedDisplayFOVHigh);
DisplayFOV = CalcFOVForAspectRatio(StandardDisplayFOV);
default.DisplayFOV = DisplayFOV;
AdjustIngameScope();
}
else
{
PlayerIronSightFOV = default.PlayerIronSightFOV;
ZoomedDisplayFOV = default.ZoomedDisplayFOV;
ZoomedDisplayFOVHigh = default.ZoomedDisplayFOVHigh;
DisplayFOV = StandardDisplayFOV;
default.DisplayFOV = DisplayFOV;
AdjustIngameScope();
}
}
// For a given 4/3 based FOV, give the proper FOV for the current
// aspect ratio
simulated final function float CalcFOVForAspectRatio(float OriginalFOV)
{
local float ResX, ResY;
local float AspectRatio;
local float OriginalAspectRatio;
local float NewFOV;
local KFPlayerController KFPC;
KFPC = KFPlayerController(Level.GetLocalPlayerController());
if( KFPC != none && KFPC.bUseTrueWideScreenFOV )
{
ResX = float(GUIController(KFPC.Player.GUIController).ResX);
ResY = float(GUIController(KFPC.Player.GUIController).ResY);
AspectRatio = ResX / ResY;
OriginalAspectRatio = 4/3;
NewFOV = (ATan((Tan((OriginalFOV*Pi)/360.0)*(AspectRatio/OriginalAspectRatio)),1)*360.0)/Pi;
return NewFOV;
}
return OriginalFOV;
}
// The empty sound if your out of ammo
simulated function Fire(float F)
{
if( bModeZeroCanDryFire && MagAmmoRemaining < 1 && !bIsReloading &&
FireMode[0].NextFireTime <= Level.TimeSeconds )
{
// We're dry, ask the server to autoreload
ServerRequestAutoReload();
PlayOwnedSound(FireMode[0].NoAmmoSound,SLOT_None,2.0,,,,false);
}
super.Fire(F);
}
// request an auto reload on the server - happens when the player dry fires
function ServerRequestAutoReload()
{
if( /*NumClicks > 0 &&*/ AllowReload() )
{
ReloadMeNow();
return;
}
NumClicks++;
}
simulated exec function ToggleIronSights()
{
if( bHasAimingMode )
{
if( bAimingRifle )
{
PerformZoom(false);
}
else
{
if( Owner != none && Owner.Physics == PHYS_Falling &&
Owner.PhysicsVolume.Gravity.Z <= class'PhysicsVolume'.default.Gravity.Z )
{
return;
}
InterruptReload();
if( bIsReloading || !CanZoomNow() )
return;
PerformZoom(True);
}
}
}
simulated exec function IronSightZoomIn()
{
if( bHasAimingMode )
{
if( Owner != none && Owner.Physics == PHYS_Falling &&
Owner.PhysicsVolume.Gravity.Z <= class'PhysicsVolume'.default.Gravity.Z )
{
return;
}
InterruptReload();
if( bIsReloading || !CanZoomNow() )
return;
PerformZoom(True);
}
}
simulated exec function IronSightZoomOut()
{
if( bHasAimingMode )
{
if( bAimingRifle )
PerformZoom(false);
if( Role < ROLE_Authority)
ServerZoomOut(false);
}
}
/**
* Handles all the functionality for zooming in including
* setting the parameters for the weapon, pawn, and playercontroller
*
* @param bAnimateTransition whether or not to animate this zoom transition
*/
simulated function ZoomIn(bool bAnimateTransition)
{
if ( ClientState != WS_PutDown && ClientState != WS_BringUp )
{
super.ZoomIn(bAnimateTransition);
bAimingRifle = True;
if( KFHumanPawn(Instigator)!=None )
KFHumanPawn(Instigator).SetAiming(True);
if( Level.NetMode != NM_DedicatedServer && KFPlayerController(Instigator.Controller) != none )
{
if( AimInSound != none )
{
PlayOwnedSound(AimInSound, SLOT_Misc,,,,, false);
}
KFPlayerController(Instigator.Controller).TransitionFOV(PlayerIronSightFOV,ZoomTime);
}
}
}
/**
* Handles all the functionality for zooming out including
* setting the parameters for the weapon, pawn, and playercontroller
*
* @param bAnimateTransition whether or not to animate this zoom transition
*/
simulated function ZoomOut(bool bAnimateTransition)
{
super.ZoomOut(bAnimateTransition);
bAimingRifle = False;
if( KFHumanPawn(Instigator)!=None )
KFHumanPawn(Instigator).SetAiming(False);
if( Level.NetMode != NM_DedicatedServer && KFPlayerController(Instigator.Controller) != none )
{
if( AimOutSound != none )
{
PlayOwnedSound(AimOutSound, SLOT_Misc,,,,, false);
}
KFPlayerController(Instigator.Controller).TransitionFOV(KFPlayerController(Instigator.Controller).DefaultFOV,ZoomTime);
}
}
/**
* Called by the native code when the interpolation of the first person weapon to the zoomed position finishes
*/
simulated event OnZoomInFinished()
{
local name anim;
local float frame, rate;
GetAnimParams(0, anim, frame, rate);
if (ClientState == WS_ReadyToFire)
{
// Play the iron idle anim when we're finished zooming in
if (anim == IdleAnim)
{
PlayIdle();
}
}
}
/**
* Called by the native code when the interpolation of the first person weapon from the zoomed position finishes
*/
simulated event OnZoomOutFinished()
{
local name anim;
local float frame, rate;
GetAnimParams(0, anim, frame, rate);
if (ClientState == WS_ReadyToFire)
{
// Play the regular idle anim when we're finished zooming out
if (anim == IdleAimAnim)
{
PlayIdle();
}
}
}
simulated function PlayIdle()
{
if( bAimingRifle )
{
LoopAnim(IdleAimAnim, IdleAnimRate, 0.2);
}
else
{
LoopAnim(IdleAnim, IdleAnimRate, 0.2);
}
}
simulated function vector GetEffectStart()
{
local Vector FlashLoc;
// jjs - this function should actually never be called in third person views
// any effect that needs a 3rdp weapon offset should figure it out itself
// 1st person
if (Instigator.IsFirstPerson())
{
if ( WeaponCentered() )
return CenteredEffectStart();
FlashLoc = GetBoneCoords(default.FlashBoneName).Origin;
return FlashLoc;
}
// 3rd person
else
{
return (Instigator.Location +
Instigator.EyeHeight*Vect(0,0,0.5) +
Vector(Instigator.Rotation) * 40.0);
}
}
function bool HandlePickupQuery( pickup Item )
{
local int i;
local class< KFWeaponPickup > KFWP, VariantPickupClass;
local bool bInInventory;
// check if Item and Self are the same weapon
if( Item.InventoryType==Class )
{
bInInventory = true;
}
// check if Item is a variant of Self
KFWP = class< KFWeaponPickup >( PickupClass );
for( i = 0; i < KFWP.default.VariantClasses.Length && !bInInventory; ++i )
{
VariantPickupClass = class<KFWeaponPickup>(KFWP.default.VariantClasses[i]);
if( Item.Class == VariantPickupClass )
{
bInInventory = true;
}
}
// check if Self is a variant of Item
KFWP = class< KFWeaponPickup >( Item.Class );
if( KFWP != none )
{
for( i = 0; i < KFWP.default.VariantClasses.Length && !bInInventory; ++i )
{
VariantPickupClass = class<KFWeaponPickup>(KFWP.default.VariantClasses[i]);
if( PickupClass == VariantPickupClass )
{
bInInventory = true;
}
}
}
if( bInInventory )
{
if( LastHasGunMsgTime<Level.TimeSeconds && PlayerController(Instigator.Controller)!=none )
{
LastHasGunMsgTime = Level.TimeSeconds+0.5;
PlayerController(Instigator.Controller).ReceiveLocalizedMessage(Class'KFMainMessages',1);
}
return true;
}
return Super.HandlePickupQuery(Item);
}
function LightFire() //simulated
{
ServerSpawnLight();
}
function ServerSpawnLight()
{
if (!FireMode[0].bIsFiring && !bIsReloading)
{
if( FlashLight==None && KFHumanPawn(Owner).TorchBatteryLife >= 1 && pawn(Owner).Health > 0 )
{
FlashLight=spawn(class'Effect_TacLightProjector',Instigator);
PlaySound(sound'KF_9MMSnd.NineMM_AltFire1',SLOT_Misc,100);
PlayAnim(ModeSwitchAnim,FireMode[0].FireAnimRate,FireMode[0].TweenTime);
FlashLight.bHasLight=!FlashLight.bHasLight;
}
else if( FlashLight!=None )
{
FlashLight.bHasLight=!FlashLight.bHasLight;
PlayAnim(ModeSwitchAnim,FireMode[0].FireAnimRate,FireMode[0].TweenTime);
//PlaySound(sound'KF_9MMSnd.NineMM_AltFire2 ',SLOT_Misc,100);
}
}
}
// Overridden to take out some UT stuff
simulated event RenderOverlays( Canvas Canvas )
{
local int m;
if (Instigator == None)
return;
if ( Instigator.Controller != None )
Hand = Instigator.Controller.Handedness;
if ((Hand < -1.0) || (Hand > 1.0))
return;
// draw muzzleflashes/smoke for all fire modes so idle state won't
// cause emitters to just disappear
for (m = 0; m < NUM_FIRE_MODES; m++)
{
if (FireMode[m] != None)
{
FireMode[m].DrawMuzzleFlash(Canvas);
}
}
SetLocation( Instigator.Location + Instigator.CalcDrawOffset(self) );
SetRotation( Instigator.GetViewRotation() + ZoomRotInterp);
//PreDrawFPWeapon(); // Laurent -- Hook to override things before render (like rotation if using a staticmesh)
bDrawingFirstPerson = true;
Canvas.DrawActor(self, false, false, DisplayFOV);
bDrawingFirstPerson = false;
}
exec function ReloadMeNow()
{
local float ReloadMulti;
if(!AllowReload())
return;
if ( bHasAimingMode && bAimingRifle )
{
FireMode[1].bIsFiring = False;
ZoomOut(false);
if( Role < ROLE_Authority)
ServerZoomOut(false);
}
if ( KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo) != none && KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill != none )
{
ReloadMulti = KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill.Static.GetReloadSpeedModifier(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo), self);
}
else
{
ReloadMulti = 1.0;
}
bIsReloading = true;
ReloadTimer = Level.TimeSeconds;
ReloadRate = Default.ReloadRate / ReloadMulti;
if( bHoldToReload )
{
NumLoadedThisReload = 0;
}
ClientReload();
Instigator.SetAnimAction(WeaponReloadAnim);
// Reload message commented out for now - Ramm
if ( Level.Game.NumPlayers > 1 && KFGameType(Level.Game).bWaveInProgress && KFPlayerController(Instigator.Controller) != none &&
Level.TimeSeconds - KFPlayerController(Instigator.Controller).LastReloadMessageTime > KFPlayerController(Instigator.Controller).ReloadMessageDelay )
{
KFPlayerController(Instigator.Controller).Speech('AUTO', 2, "");
KFPlayerController(Instigator.Controller).LastReloadMessageTime = Level.TimeSeconds;
}
}
simulated function ClientReload()
{
local float ReloadMulti;
if ( bHasAimingMode && bAimingRifle )
{
FireMode[1].bIsFiring = False;
ZoomOut(false);
if( Role < ROLE_Authority)
ServerZoomOut(false);
}
if ( KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo) != none && KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill != none )
{
ReloadMulti = KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill.Static.GetReloadSpeedModifier(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo), self);
}
else
{
ReloadMulti = 1.0;
}
bIsReloading = true;
PlayAnim(ReloadAnim, ReloadAnimRate*ReloadMulti, 0.1);
}
simulated function ClientReloadEffects(){}
//// client & server ////
// Overriden to support interrupting reloads
simulated function bool StartFire(int Mode)
{
local bool RetVal;
RetVal = super.StartFire(Mode);
if( RetVal )
{
if( Mode == 0 && ForceZoomOutOnFireTime > 0 )
{
ForceZoomOutTime = Level.TimeSeconds + ForceZoomOutOnFireTime;
}
else if( Mode == 1 && ForceZoomOutOnAltFireTime > 0 )
{
ForceZoomOutTime = Level.TimeSeconds + ForceZoomOutOnAltFireTime;
}
NumClicks=0;
InterruptReload();
}
return RetVal;
}
// Interrupt the reload for single bullet insert weapons
simulated function bool InterruptReload()
{
if( bHoldToReload && bIsReloading )
{
ServerInterruptReload();
if ( Level.NetMode != NM_StandAlone && (Level.NetMode != NM_ListenServer || !Instigator.IsLocallyControlled()) )
{
ClientInterruptReload();
}
return true;
}
return false;
}
simulated function ServerInterruptReload()
{
bDoSingleReload = false;
bIsReloading = false;
bReloadEffectDone = false;
PlayIdle();
}
// Server forces the reload to be cancelled
simulated function ClientInterruptReload()
{
bIsReloading = false;
PlayIdle();
}
//We shouldn't allow finishreloading to finish reloading unless
//the weapon works like that.
simulated function ActuallyFinishReloading()
{
bDoSingleReload=false;
ClientFinishReloading();
bIsReloading = false;
bReloadEffectDone = false;
}
simulated function ClientFinishReloading()
{
bIsReloading = false;
PlayIdle();
if(Instigator.PendingWeapon != none && Instigator.PendingWeapon != self)
Instigator.Controller.ClientSwitchToBestWeapon();
}
function ServerSetAiming(bool IsAiming)
{
if( KFHumanPawn(Instigator)!=None )
KFHumanPawn(Instigator).SetAiming(IsAiming);
bAimingRifle = IsAiming;
}
simulated function bool AllowReload()
{
UpdateMagCapacity(Instigator.PlayerReplicationInfo);
if(KFInvasionBot(Instigator.Controller) != none && !bIsReloading &&
MagAmmoRemaining < MagCapacity && AmmoAmount(0) > MagAmmoRemaining)
return true;
if(KFFriendlyAI(Instigator.Controller) != none && !bIsReloading &&
MagAmmoRemaining < MagCapacity && AmmoAmount(0) > MagAmmoRemaining)
return true;
if(FireMode[0].IsFiring() || FireMode[1].IsFiring() ||
bIsReloading || MagAmmoRemaining >= MagCapacity ||
ClientState == WS_BringUp ||
AmmoAmount(0) <= MagAmmoRemaining ||
(FireMode[0].NextFireTime - Level.TimeSeconds) > 0.1 )
return false;
return true;
}
simulated function PostNetReceive()
{
//This function changes the weapon in netplay if it has no ammo
//we WANT to be able to select guns without ammo. Therefore this
//function has to be overridden do it does.....nothing.
}
function ServerStopFire(byte Mode)
{
//TODO: This works, but could be better timing wise
// Still, at least the DBShottie/XBow don't have wierd
// negative clip sizes now
super.ServerStopFire(Mode);
if(MagCapacity==1)
MagAmmoRemaining=1;
}
// TODO: Play spot the difference
// cut n pasted to change putdown behaviours
simulated function Timer()
{
local int Mode;
local float OldDownDelay;
OldDownDelay = DownDelay;
DownDelay = 0;
if ( ClientState == WS_BringUp )
{
for( Mode = 0; Mode < NUM_FIRE_MODES; Mode++ )
FireMode[Mode].InitEffects();
PlayIdle();
ClientState = WS_ReadyToFire;
if ( bPendingFlashlight && bTorchEnabled )
{
if ( Level.NetMode != NM_Client )
{
LightFire();
}
else
{
ClientStartFire(1);
}
bPendingFlashlight = false;
}
}
else if ( ClientState == WS_PutDown )
{
if ( OldDownDelay > 0 )
{
if ( HasAnim(PutDownAnim) )
PlayAnim(PutDownAnim, PutDownAnimRate, 0.0);
SetTimer(PutDownTime, false);
return;
}
if ( Instigator.PendingWeapon == None )
{
if( ClientGrenadeState == GN_TempDown )
{
if(KFPawn(Instigator)!=none)
{
KFPawn(Instigator).WeaponDown();
}
}
else
{
PlayIdle();
}
ClientState = WS_ReadyToFire;
}
else
{
if( FlashLight!=none )
Tacshine.Destroy();
ClientState = WS_Hidden;
Instigator.ChangedWeapon();
if ( Instigator.Weapon == self )
{
PlayIdle();
ClientState = WS_ReadyToFire;
}
else
{
for( Mode = 0; Mode < NUM_FIRE_MODES; Mode++ )
FireMode[Mode].DestroyEffects();
}
}
}
}
// Kludge to prevent destroyed weapons destroying the ammo if other guns
// are still using the same ammo
simulated function Destroyed()
{
local byte m;
local Inventory InvIt;
local byte bSaveAmmo[NUM_FIRE_MODES]; // byte, because bool arrays aren't allowed :(
local Actor TempOwner;
AmbientSound = None;
if ( FlashLight != none )
FlashLight.Destroy();
if ( TacShine != none )
TacShine.Destroy();
if ( Owner == none )
{
TempOwner = Instigator;
}
else
{
TempOwner = Owner;
}
if ( TempOwner != none)
{
for(InvIt = TempOwner.Inventory; InvIt!=none; InvIt=InvIt.Inventory)
{
if(Weapon(InvIt)!=none && InvIt!=self)
{
for(m=0; m < NUM_FIRE_MODES; m++)
{
if( Weapon(InvIt).Ammo[m]==Ammo[m] )
bSaveAmmo[m] = 1;
}
}
}
for (m = 0; m < NUM_FIRE_MODES; m++)
{
if ( FireMode[m] != None )
FireMode[m].DestroyEffects();
if( Ammo[m] != none && bSaveAmmo[m]==0 )
{
Ammo[m].Destroy();
Ammo[m] = None;
}
}
if ( Pawn(TempOwner) != none )
{
Pawn(TempOwner).DeleteInventory(self);
}
}
if ( ThirdPersonActor != None )
ThirdPersonActor.Destroy();
}
simulated function Weapon PrevWeapon(Weapon CurrentChoice, Weapon CurrentWeapon)
{
//allow selection of empty guns, so that we can select them
//in order to chuck them away
if ( (CurrentChoice == None) )
{
if ( CurrentWeapon != self )
CurrentChoice = self;
}
else if ( InventoryGroup == CurrentWeapon.InventoryGroup )
{
if ( (GroupOffset < CurrentWeapon.GroupOffset) && ((CurrentChoice.InventoryGroup != InventoryGroup)
|| (GroupOffset > CurrentChoice.GroupOffset)) )
CurrentChoice = self;
}
else if ( InventoryGroup == CurrentChoice.InventoryGroup )
{
if ( GroupOffset > CurrentChoice.GroupOffset )
CurrentChoice = self;
}
else if ( InventoryGroup > CurrentChoice.InventoryGroup )
{
if ( (InventoryGroup < CurrentWeapon.InventoryGroup)
|| (CurrentChoice.InventoryGroup > CurrentWeapon.InventoryGroup)
|| ( (CurrentChoice.InventoryGroup == CurrentWeapon.InventoryGroup) && (CurrentChoice.GroupOffset>CurrentWeapon.GroupOffset) ) )
CurrentChoice = self;
}
else if ( (CurrentChoice.InventoryGroup > CurrentWeapon.InventoryGroup || ( (CurrentChoice.InventoryGroup == CurrentWeapon.InventoryGroup) && (CurrentChoice.GroupOffset>CurrentWeapon.GroupOffset) ))
&& (InventoryGroup < CurrentWeapon.InventoryGroup) )
CurrentChoice = self;
if ( Inventory == None )
return CurrentChoice;
else return Inventory.PrevWeapon(CurrentChoice,CurrentWeapon);
}
simulated function Weapon NextWeapon(Weapon CurrentChoice, Weapon CurrentWeapon)
{
if ( (CurrentChoice == None) )
{
if ( CurrentWeapon != self )
CurrentChoice = self;
}
else if ( InventoryGroup == CurrentWeapon.InventoryGroup )
{
if ( (GroupOffset > CurrentWeapon.GroupOffset)
&& ((CurrentChoice.InventoryGroup != InventoryGroup) || (GroupOffset < CurrentChoice.GroupOffset)) )
CurrentChoice = self;
}
else if ( InventoryGroup == CurrentChoice.InventoryGroup )
{
if ( GroupOffset < CurrentChoice.GroupOffset )
CurrentChoice = self;
}
else if ( InventoryGroup < CurrentChoice.InventoryGroup )
{
if ( (InventoryGroup > CurrentWeapon.InventoryGroup)
|| (CurrentChoice.InventoryGroup < CurrentWeapon.InventoryGroup)
|| ( (CurrentChoice.InventoryGroup == CurrentWeapon.InventoryGroup) && (CurrentChoice.GroupOffset<CurrentWeapon.GroupOffset) ) )
CurrentChoice = self;
}
else if ( (CurrentChoice.InventoryGroup < CurrentWeapon.InventoryGroup || (CurrentChoice.InventoryGroup == CurrentWeapon.InventoryGroup) && (CurrentChoice.GroupOffset<CurrentWeapon.GroupOffset))
&& (InventoryGroup > CurrentWeapon.InventoryGroup) )
CurrentChoice = self;
if ( Inventory == None )
return CurrentChoice;
else return Inventory.NextWeapon(CurrentChoice,CurrentWeapon);
}
simulated function Weapon WeaponChange( byte F, bool bSilent )
{
if ( InventoryGroup == F )
return self;
else if ( Inventory == None )
return None;
else return Inventory.WeaponChange(F,bSilent);
}
simulated function bool PutDown()
{
local int Mode;
InterruptReload();
if ( bIsReloading )
return false;
if( bAimingRifle )
{
ZoomOut(False);
}
// From Weapon.uc
if (ClientState == WS_BringUp || ClientState == WS_ReadyToFire)
{
if ( (Instigator.PendingWeapon != None) && !Instigator.PendingWeapon.bForceSwitch )
{
for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
{
// if _RO_
if( FireMode[Mode] == none )
continue;
// End _RO_
if ( FireMode[Mode].bFireOnRelease && FireMode[Mode].bIsFiring )
return false;
if ( FireMode[Mode].NextFireTime > Level.TimeSeconds + FireMode[Mode].FireRate*(1.f - MinReloadPct))
DownDelay = FMax(DownDelay, FireMode[Mode].NextFireTime - Level.TimeSeconds - FireMode[Mode].FireRate*(1.f - MinReloadPct));
}
}
if (Instigator.IsLocallyControlled())
{
for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
{
// if _RO_
if( FireMode[Mode] == none )
continue;
// End _RO_
if ( FireMode[Mode].bIsFiring )
ClientStopFire(Mode);
}
if ( DownDelay <= 0 || KFPawn(Instigator).bIsQuickHealing > 0)
{
if ( ClientState == WS_BringUp || KFPawn(Instigator).bIsQuickHealing > 0 )
TweenAnim(SelectAnim,PutDownTime);
else if ( HasAnim(PutDownAnim) )
{
if( ClientGrenadeState == GN_TempDown || KFPawn(Instigator).bIsQuickHealing > 0)
{
PlayAnim(PutDownAnim, PutDownAnimRate * (PutDownTime/QuickPutDownTime), 0.0);
}
else
{
PlayAnim(PutDownAnim, PutDownAnimRate, 0.0);
}
}
}
}
ClientState = WS_PutDown;
if ( Level.GRI.bFastWeaponSwitching )
DownDelay = 0;
if ( DownDelay > 0 )
{
SetTimer(DownDelay, false);
}
else
{
if( ClientGrenadeState == GN_TempDown )
{
SetTimer(QuickPutDownTime, false);
}
else
{
SetTimer(PutDownTime, false);
}
}
}
for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
{
// if _RO_
if( FireMode[Mode] == none )
continue;
// End _RO_
FireMode[Mode].bServerDelayStartFire = false;
FireMode[Mode].bServerDelayStopFire = false;
}
Instigator.AmbientSound = None;
OldWeapon = None;
return true; // return false if preventing weapon switch
}
simulated function BringUp(optional Weapon PrevWeapon)
{
local int Mode;
local KFPlayerController Player;
HandleSleeveSwapping();
// Hint check
Player = KFPlayerController(Instigator.Controller);
if ( Player != none && ClientGrenadeState != GN_BringUp )
{
if ( class == class'Single' )
{
Player.CheckForHint(10);
}
else if ( class == class'Dualies' )
{
Player.CheckForHint(11);
}
else if ( class == class'Deagle' )
{
Player.CheckForHint(12);
}
else if ( class == class'Bullpup' )
{
Player.CheckForHint(13);
}
else if ( class == class'Shotgun' )
{
Player.CheckForHint(14);
}
else if ( class == class'Winchester' )
{
Player.CheckForHint(15);
}
else if ( class == class'Crossbow' )
{
Player.CheckForHint(16);
}
else if ( class == class'BoomStick' )
{
Player.CheckForHint(17);
Player.WeaponPulloutRemark(21);
}
else if ( class == class'FlameThrower' )
{
Player.CheckForHint(18);
}
else if ( class == class'LAW' )
{
Player.CheckForHint(19);
Player.WeaponPulloutRemark(23);
}
else if ( class == class'Knife' && bShowPullOutHint )
{
Player.CheckForHint(20);
}
else if ( class == class'Machete' )
{
Player.CheckForHint(21);
}
else if ( class == class'Axe' )
{
Player.CheckForHint(22);
Player.WeaponPulloutRemark(24);
}
else if ( class == class'DualDeagle' || class == class'GoldenDualDeagle' )
{
Player.WeaponPulloutRemark(22);
}
bShowPullOutHint = true;
}
if ( KFHumanPawn(Instigator) != none )
KFHumanPawn(Instigator).SetAiming(false);
bAimingRifle = false;
bIsReloading = false;
IdleAnim = default.IdleAnim;
//Super.BringUp(PrevWeapon);
// From Weapon.uc
if ( ClientState == WS_Hidden || ClientGrenadeState == GN_BringUp || KFPawn(Instigator).bIsQuickHealing > 0 )
{
PlayOwnedSound(SelectSound, SLOT_Interact,,,,, false);
ClientPlayForceFeedback(SelectForce); // jdf
if ( Instigator.IsLocallyControlled() )
{
if ( (Mesh!=None) && HasAnim(SelectAnim) )
{
if( ClientGrenadeState == GN_BringUp || KFPawn(Instigator).bIsQuickHealing > 0 )
{
PlayAnim(SelectAnim, SelectAnimRate * (BringUpTime/QuickBringUpTime), 0.0);
}
else
{
PlayAnim(SelectAnim, SelectAnimRate, 0.0);
}
}
}
ClientState = WS_BringUp;
if( ClientGrenadeState == GN_BringUp || KFPawn(Instigator).bIsQuickHealing > 0 )
{
ClientGrenadeState = GN_None;
SetTimer(QuickBringUpTime, false);
}
else
{
SetTimer(BringUpTime, false);
}
}
for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
{
FireMode[Mode].bIsFiring = false;
FireMode[Mode].HoldTime = 0.0;
FireMode[Mode].bServerDelayStartFire = false;
FireMode[Mode].bServerDelayStopFire = false;
FireMode[Mode].bInstantStop = false;
}
if ( (PrevWeapon != None) && PrevWeapon.HasAmmo() && !PrevWeapon.bNoVoluntarySwitch )
OldWeapon = PrevWeapon;
else
OldWeapon = None;
}
//------------------------------------------------------------------------------
// HandleSleeveSwapping() - This function will handle sleeve swapping for
// weapons depending on which player the person who picked the weapon up is.
//------------------------------------------------------------------------------
simulated function HandleSleeveSwapping()
{
local XPawn XP;
local Material SleeveTexture;
// don't bother with AI players
if( !Instigator.IsHumanControlled() || !Instigator.IsLocallyControlled() )
return;
XP = XPawn(Instigator);
if( XP == none )
{
return;
}
SleeveTexture = Class<KFSpeciesType>(XP.Species).static.GetSleeveTexture();
if( SleeveTexture != none )
Skins[SleeveNum] = SleeveTexture;
}
simulated function bool ConsumeAmmo( int Mode, float Load, optional bool bAmountNeededIsMax )
{
local Inventory Inv;
local bool bOutOfAmmo;
local KFWeapon KFWeap;
if ( Super.ConsumeAmmo(Mode, Load, bAmountNeededIsMax) )
{
if ( Load > 0 && (Mode == 0 || bReduceMagAmmoOnSecondaryFire) )
MagAmmoRemaining--;
NetUpdateTime = Level.TimeSeconds - 1;
if ( FireMode[Mode].AmmoPerFire > 0 && InventoryGroup > 0 && !bMeleeWeapon && bConsumesPhysicalAmmo &&
(Ammo[0] == none || FireMode[0] == none || FireMode[0].AmmoPerFire <= 0 || Ammo[0].AmmoAmount < FireMode[0].AmmoPerFire) &&
(Ammo[1] == none || FireMode[1] == none || FireMode[1].AmmoPerFire <= 0 || Ammo[1].AmmoAmount < FireMode[1].AmmoPerFire) )
{
bOutOfAmmo = true;
for ( Inv = Instigator.Inventory; Inv != none; Inv = Inv.Inventory )
{
KFWeap = KFWeapon(Inv);
if ( Inv.InventoryGroup > 0 && KFWeap != none && !KFWeap.bMeleeWeapon && KFWeap.bConsumesPhysicalAmmo &&
((KFWeap.Ammo[0] != none && KFWeap.FireMode[0] != none && KFWeap.FireMode[0].AmmoPerFire > 0 &&KFWeap.Ammo[0].AmmoAmount >= KFWeap.FireMode[0].AmmoPerFire) ||
(KFWeap.Ammo[1] != none && KFWeap.FireMode[1] != none && KFWeap.FireMode[1].AmmoPerFire > 0 && KFWeap.Ammo[1].AmmoAmount >= KFWeap.FireMode[1].AmmoPerFire)) )
{
bOutOfAmmo = false;
break;
}
}
if ( bOutOfAmmo )
{
PlayerController(Instigator.Controller).Speech('AUTO', 3, "");
}
}
return true;
}
return false;
}
//TODO - this should, in theory, let us buy/carry more ammo.
// is this intended, or should this side-effect be squashed?
function ClipUpgrade()
{
MagCapacity += (0.25 * default.MagCapacity);
}
function OwnerEvent(name EventName)
{
if( EventName=='ChangedWeapon' )
{
if( TacShine!=None )
TacShine.Destroy();
if( FlashLight!=None && FlashLight.bHasLight )
ServerSpawnLight();
}
Super.OwnerEvent(EventName);
}
simulated function WeaponTick(float dt)
{
local float LastSeenSeconds,ReloadMulti;
if( bHasAimingMode )
{
if( bForceLeaveIronsights )
{
if( bAimingRifle )
{
ZoomOut(true);
if( Role < ROLE_Authority)
ServerZoomOut(false);
}
bForceLeaveIronsights = false;
}
if( ForceZoomOutTime > 0 )
{
if( bAimingRifle )
{
if( Level.TimeSeconds - ForceZoomOutTime > 0 )
{
ForceZoomOutTime = 0;
ZoomOut(true);
if( Role < ROLE_Authority)
ServerZoomOut(false);
}
}
else
{
ForceZoomOutTime = 0;
}
}
}
if ( (Level.NetMode == NM_Client) || Instigator == None || KFFriendlyAI(Instigator.Controller) == none && Instigator.PlayerReplicationInfo == None)
return;
// Turn it off on death / battery expenditure
if (FlashLight != none)
{
// Keep the 1Pweapon client beam up to date.
AdjustLightGraphic();
if (FlashLight.bHasLight)
{
if (Instigator.Health <= 0 || KFHumanPawn(Instigator).TorchBatteryLife <= 0 || Instigator.PendingWeapon != none )
{
//Log("Killing Light...you're out of batteries, or switched / dropped weapons");
KFHumanPawn(Instigator).bTorchOn = false;
ServerSpawnLight();
}
}
}
UpdateMagCapacity(Instigator.PlayerReplicationInfo);
if(!bIsReloading)
{
if(!Instigator.IsHumanControlled())
{
LastSeenSeconds = Level.TimeSeconds - Instigator.Controller.LastSeenTime;
if(MagAmmoRemaining == 0 || ((LastSeenSeconds >= 5 || LastSeenSeconds > MagAmmoRemaining) && MagAmmoRemaining < MagCapacity))
ReloadMeNow();
}
}
else
{
if((Level.TimeSeconds - ReloadTimer) >= ReloadRate)
{
if(AmmoAmount(0) <= MagCapacity && !bHoldToReload)
{
MagAmmoRemaining = AmmoAmount(0);
ActuallyFinishReloading();
}
else
{
if ( KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo) != none && KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill != none )
{
ReloadMulti = KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill.Static.GetReloadSpeedModifier(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo), self);
}
else
{
ReloadMulti = 1.0;
}
AddReloadedAmmo();
if( bHoldToReload )
{
NumLoadedThisReload++;
}
if(MagAmmoRemaining < MagCapacity && MagAmmoRemaining < AmmoAmount(0) && bHoldToReload)
ReloadTimer = Level.TimeSeconds;
if(MagAmmoRemaining >= MagCapacity || MagAmmoRemaining >= AmmoAmount(0) || !bHoldToReload || bDoSingleReload)
ActuallyFinishReloading();
else if( Level.NetMode!=NM_Client )
Instigator.SetAnimAction(WeaponReloadAnim);
}
}
else if(bIsReloading && !bReloadEffectDone && Level.TimeSeconds - ReloadTimer >= ReloadRate / 2)
{
bReloadEffectDone = true;
ClientReloadEffects();
}
}
}
// Add the ammo for this reload
function AddReloadedAmmo()
{
UpdateMagCapacity(Instigator.PlayerReplicationInfo);
if(AmmoAmount(0) >= MagCapacity)
MagAmmoRemaining = MagCapacity;
else
MagAmmoRemaining = AmmoAmount(0) ;
// Don't do this on a "Hold to reload" weapon, as it can update too quick actually and cause issues maybe - Ramm
if( !bHoldToReload )
{
ClientForceKFAmmoUpdate(MagAmmoRemaining,AmmoAmount(0));
}
if ( PlayerController(Instigator.Controller) != none && KFSteamStatsAndAchievements(PlayerController(Instigator.Controller).SteamStatsAndAchievements) != none )
{
KFSteamStatsAndAchievements(PlayerController(Instigator.Controller).SteamStatsAndAchievements).OnWeaponReloaded();
}
}
simulated function ClientForceKFAmmoUpdate(int NewMagAmmoRemaining, int TotalAmmoRemaining)
{
//log(self$" ClientForceKFAmmoUpdate NewMagAmmoRemaining "$NewMagAmmoRemaining$" TotalAmmoRemaining "$TotalAmmoRemaining);
ClientForceAmmoUpdate(0, TotalAmmoRemaining);
}
simulated function DoToggle ()
{
local PlayerController Player;
if( IsFiring() )
{
return;
}
Player = Level.GetLocalPlayerController();
if ( Player!=None )
{
//PlayOwnedSound(sound'Inf_Weapons_Foley.stg44_firemodeswitch01',SLOT_None,2.0,,,,false);
FireMode[0].bWaitForRelease = !FireMode[0].bWaitForRelease;
if ( FireMode[0].bWaitForRelease )
Player.ReceiveLocalizedMessage(class'KFmod.BullpupSwitchMessage',0);
else Player.ReceiveLocalizedMessage(class'KFmod.BullpupSwitchMessage',1);
}
PlayOwnedSound(ToggleSound,SLOT_None,2.0,,,,false);
ServerChangeFireMode(FireMode[0].bWaitForRelease);
}
// Set the new fire mode on the server
function ServerChangeFireMode(bool bNewWaitForRelease)
{
FireMode[0].bWaitForRelease = bNewWaitForRelease;
}
// TODO - Decode the purpose of this mangled mess
simulated function float ChargeBar()
{
Return 0;
}
function byte BestMode()
{
return 0;
}
// complete cut n' paste job needed, so that it can be modified
// to stop this function giving ammo to empty guns that have been
// thrown out
function GiveAmmo(int m, WeaponPickup WP, bool bJustSpawned)
{
local bool bJustSpawnedAmmo;
local int addAmount, InitialAmount;
local KFPawn KFP;
local KFPlayerReplicationInfo KFPRI;
KFP = KFPawn(Instigator);
if( KFP != none )
{
KFPRI = KFPlayerReplicationInfo(KFP.PlayerReplicationInfo);
}
UpdateMagCapacity(Instigator.PlayerReplicationInfo);
if ( FireMode[m] != None && FireMode[m].AmmoClass != None )
{
Ammo[m] = Ammunition(Instigator.FindInventoryType(FireMode[m].AmmoClass));
bJustSpawnedAmmo = false;
if ( bNoAmmoInstances )
{
if ( (FireMode[m].AmmoClass == None) || ((m != 0) && (FireMode[m].AmmoClass == FireMode[0].AmmoClass)) )
return;
InitialAmount = FireMode[m].AmmoClass.Default.InitialAmount;
if(WP!=none && WP.bThrown==true)
InitialAmount = WP.AmmoAmount[m];
else
{
// Other change - if not thrown, give the gun a full clip
MagAmmoRemaining = MagCapacity;
}
if ( Ammo[m] != None )
{
addamount = InitialAmount + Ammo[m].AmmoAmount;
Ammo[m].Destroy();
}
else
addAmount = InitialAmount;
AddAmmo(addAmount,m);
}
else
{
if ( (Ammo[m] == None) && (FireMode[m].AmmoClass != None) )
{
Ammo[m] = Spawn(FireMode[m].AmmoClass, Instigator);
Instigator.AddInventory(Ammo[m]);
bJustSpawnedAmmo = true;
}
else if ( (m == 0) || (FireMode[m].AmmoClass != FireMode[0].AmmoClass) )
bJustSpawnedAmmo = ( bJustSpawned || ((WP != None) && !WP.bWeaponStay) );
// and here is the modification for instanced ammo actors
if(WP!=none && WP.bThrown==true)
{
addAmount = WP.AmmoAmount[m];
}
else if ( bJustSpawnedAmmo )
{
if (default.MagCapacity == 0)
addAmount = 0; // prevent division by zero.
else
addAmount = Ammo[m].InitialAmount * (float(MagCapacity) / float(default.MagCapacity));
}
// Don't double add ammo if primary and secondary fire modes share the same ammo class
if ( WP != none && m > 0 && (FireMode[m].AmmoClass == FireMode[0].AmmoClass) )
{
return;
}
// AddAmmo caps at MaxAmmo, but veterancy might allow for more than max,
// so take that into account
if( KFPRI != none && KFPRI.ClientVeteranSkill != none )
{
Ammo[m].MaxAmmo = float(Ammo[m].MaxAmmo) * KFPRI.ClientVeteranSkill.Static.AddExtraAmmoFor(KFPRI, Ammo[m].Class);
}
Ammo[m].AddAmmo(addAmount);
Ammo[m].GotoState('');
}
}
}
//this is a terribly ugly hack allowing us to silently add weapons
//in the case of buy menu consumables
//This does NOT work online and has been replaced.
function SilentGiveTo(Pawn Other, optional Pickup Pickup)
{
local int m;
local weapon w;
local bool bJustSpawned;
Instigator = Other;
W = Weapon(Instigator.FindInventoryType(class));
if ( W == None || W.Class != Class ) // added class check because somebody made FindInventoryType() return subclasses for some reason
{
bJustSpawned = true;
GiveTo(Other);
W = self;
}
for (m = 0; m < NUM_FIRE_MODES; m++)
{
if ( FireMode[m] != None )
{
FireMode[m].Instigator = Instigator;
W.GiveAmmo(m,WeaponPickup(Pickup),bJustSpawned);
}
}
if ( !bJustSpawned )
{
for (m = 0; m < NUM_FIRE_MODES; m++)
Ammo[m] = None;
Destroy();
}
}
function GiveTo( pawn Other, optional Pickup Pickup )
{
UpdateMagCapacity(Other.PlayerReplicationInfo);
if ( KFWeaponPickup(Pickup)!=None && Pickup.bDropped )
{
MagAmmoRemaining = Clamp(KFWeaponPickup(Pickup).MagAmmoRemaining, 0, MagCapacity);
}
else
MagAmmoRemaining = MagCapacity;
Super.GiveTo(Other,Pickup);
}
// Modded to allow for throwing even when out of ammo. (also added : You cannot throw while reloading)
simulated function bool CanThrow()
{
local int Mode;
if(bKFNeverThrow)
return false;
for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
{
if ( FireMode[Mode].bFireOnRelease && FireMode[Mode].bIsFiring )
return false;
if ( FireMode[Mode].NextFireTime > Level.TimeSeconds)
return false;
}
return (bCanThrow && !bIsReloading && (ClientState == WS_ReadyToFire || (Level.NetMode == NM_DedicatedServer) || (Level.NetMode == NM_ListenServer)));
}
simulated function ClientWeaponThrown()
{
local int m;
local Inventory InvIt;
local byte bSaveAmmo[NUM_FIRE_MODES];
AmbientSound = None;
Instigator.AmbientSound = None;
if( Level.NetMode != NM_Client )
return;
for ( InvIt = Instigator.Inventory; InvIt != none; InvIt = InvIt.Inventory )
{
if ( Weapon(InvIt) != none && InvIt != self)
{
for ( m = 0; m < NUM_FIRE_MODES; m++ )
{
if ( Weapon(InvIt).Ammo[m] == Ammo[m] )
{
bSaveAmmo[m] = 1;
}
}
}
}
Instigator.DeleteInventory(self);
for (m = 0; m < NUM_FIRE_MODES; m++)
{
if (Ammo[m] != none && bSaveAmmo[m] == 0 )
Instigator.DeleteInventory(Ammo[m]);
}
}
function DropFrom(vector StartLocation)
{
local int m;
local Pickup Pickup;
local vector Direction;
if (!bCanThrow)
return;
ClientWeaponThrown();
for (m = 0; m < NUM_FIRE_MODES; m++)
{
if (FireMode[m].bIsFiring)
StopFire(m);
}
if ( Instigator != None )
{
DetachFromPawn(Instigator);
Direction = vector(Instigator.Rotation);
}
else if ( Owner != none )
{
Direction = vector(Owner.Rotation);
}
Pickup = Spawn(PickupClass,,, StartLocation);
if ( Pickup != None )
{
Pickup.InitDroppedPickupFor(self);
Pickup.Velocity = Velocity + (Direction * 100);
if (Instigator.Health > 0)
WeaponPickup(Pickup).bThrown = true;
}
Destroyed();
Destroy();
}
// Only fill to initial the FIRST time we come across this weapon.
simulated function FillToInitialAmmo()
{
if ( bNoAmmoInstances )
{
if ( AmmoClass[0] != None )
AmmoCharge[0] = Max(AmmoCharge[0], AmmoClass[0].Default.InitialAmount);
if ( (AmmoClass[1] != None) && (AmmoClass[0] != AmmoClass[1]) )
AmmoCharge[1] = Max(AmmoCharge[1], AmmoClass[1].Default.InitialAmount);
return;
}
if ( Ammo[0] != None )
Ammo[0].AmmoAmount = /*Max(*/Ammo[0].AmmoAmount;//,Ammo[0].InitialAmount * (float(MagCapacity) / float(default.MagCapacity)));
if ( Ammo[1] != None )
Ammo[1].AmmoAmount = Max(Ammo[1].AmmoAmount,Ammo[1].InitialAmount * (float(MagCapacity) / float(default.MagCapacity)));
}
// Change the Accuracy based on player movement
simulated function AccuracyUpdate(float Velocity)
{
if (Owner != none)
{
if (KFFire(FireMode[0])!= none)
KFFire(FireMode[0]).AccuracyUpdate(Velocity);
else
if (KFShotgunFire(FireMode[0]) !=none)
KFShotgunFire(FireMode[0]).AccuracyUpdate(Velocity);
}
}
function AdjustLightGraphic()
{
if ( TacShine==none )
{
TacShine = Spawn(TacShineClass,,,,);
AttachToBone(TacShine,'LightBone');
}
if( FlashLight!=none )
Tacshine.bHidden = !FlashLight.bHasLight;
}
simulated function PlayAnimZoom( bool bZoomNow ); // Called from KFZoom whenever start or end the zooming.
simulated function bool CanZoomNow()
{
return true;
}
simulated function float GetAmmoMulti()
{
if ( NextAmmoCheckTime > Level.TimeSeconds )
{
return LastAmmoResult;
}
NextAmmoCheckTime = Level.TimeSeconds + 1;
if ( FireMode[0] != none && FireMode[0].AmmoClass != none && KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo) != none &&
KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill != none )
{
LastAmmoResult = KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill.static.AddExtraAmmoFor(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo), FireMode[0].AmmoClass);
}
else
{
LastAmmoResult = 1;
}
return LastAmmoResult;
}
simulated function int MaxAmmo(int mode)
{
if ( AmmoClass[mode] != None )
return AmmoClass[mode].Default.MaxAmmo*GetAmmoMulti();
return 0;
}
simulated function bool AmmoMaxed(int mode)
{
if ( AmmoClass[mode] == None )
return false;
if ( bNoAmmoInstances )
{
if ( AmmoClass[0] == AmmoClass[mode] )
mode = 0;
return AmmoCharge[mode] >= MaxAmmo(mode);
}
if ( Ammo[mode] == None )
return false;
return (Ammo[mode].AmmoAmount >= MaxAmmo(mode));
}
simulated function float AmmoStatus(optional int Mode) // returns float value for ammo amount
{
if ( bNoAmmoInstances )
{
if ( AmmoClass[Mode] == None )
return 0;
if ( AmmoClass[0] == AmmoClass[mode] )
mode = 0;
return float(AmmoCharge[Mode])/float(MaxAmmo(Mode));
}
if (Ammo[Mode] == None)
return 0.0;
else return float(Ammo[Mode].AmmoAmount) / float(Ammo[Mode].MaxAmmo)*GetAmmoMulti();
}
// Avoid potential suicides...
function bool CanAttack(Actor Other)
{
local float Dist, CheckDist;
local vector HitLocation, HitNormal,X,Y,Z, projStart;
local actor HitActor;
local int m;
local bool bInstantHit;
if ( (Instigator == None) || (Instigator.Controller == None) )
return false;
// check that target is within range
Dist = VSize(Instigator.Location - Other.Location);
if ( (Dist > FireMode[0].MaxRange()) && (Dist > FireMode[1].MaxRange()) ||
Dist < MinimumFireRange)
return false;
// check that can see target
if ( !Instigator.Controller.LineOfSightTo(Other) )
return false;
for (m = 0; m < NUM_FIRE_MODES; m++)
{
if ( FireMode[m].bInstantHit )
bInstantHit = true;
else
{
CheckDist = FMax(CheckDist, 0.5 * FireMode[m].ProjectileClass.Default.Speed);
CheckDist = FMax(CheckDist, 300);
CheckDist = FMin(CheckDist, VSize(Other.Location - Location));
}
}
// check that would hit target, and not a friendly
GetAxes(Instigator.Controller.Rotation, X,Y,Z);
projStart = GetFireStart(X,Y,Z);
if ( bInstantHit )
HitActor = Trace(HitLocation, HitNormal, Other.Location + Other.CollisionHeight * vect(0,0,0.8), projStart, true);
else
{
// for non-instant hit, only check partial path (since others may move out of the way)
HitActor = Trace(HitLocation, HitNormal,
projStart + CheckDist * Normal(Other.Location + Other.CollisionHeight * vect(0,0,0.8) - Location),
projStart, true);
}
if ( (HitActor == None) || (HitActor == Other) )
return true;
if ( Pawn(HitActor) == None )
return !HitActor.BlocksShotAt(Other);
if ( (Pawn(HitActor).Controller == None) || !Instigator.Controller.SameTeamAs(Pawn(HitActor).Controller) )
return true;
return false;
}
simulated function GetAmmoCount(out float MaxAmmoPrimary, out float CurAmmoPrimary)
{
if ( Instigator == None || Instigator.Controller == None )
{
return;
}
if ( AmmoClass[0] == None )
{
return;
}
if ( bNoAmmoInstances )
{
MaxAmmoPrimary = MaxAmmo(0);
CurAmmoPrimary = AmmoCharge[0];
if ( KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo) != none && KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill != none )
{
MaxAmmoPrimary *= KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill.static.AddExtraAmmoFor(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo), AmmoClass[0]);
MaxAmmoPrimary = int(MaxAmmoPrimary);
}
return;
}
if ( Ammo[0] == None )
{
return;
}
MaxAmmoPrimary = Ammo[0].default.MaxAmmo;
CurAmmoPrimary = Ammo[0].AmmoAmount;
if ( KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo) != none && KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill != none )
{
MaxAmmoPrimary *= KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill.static.AddExtraAmmoFor(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo), Ammo[0].class);
MaxAmmoPrimary = int(MaxAmmoPrimary);
}
}
simulated function GetSecondaryAmmoCount(out float MaxAmmoSecondary, out float CurAmmoSecondary)
{
if ( Instigator == none || Instigator.Controller == none || !bHasSecondaryAmmo || AmmoClass[1] == none )
{
MaxAmmoSecondary = 0;
CurAmmoSecondary = 0;
return;
}
if ( bNoAmmoInstances )
{
MaxAmmoSecondary = MaxAmmo(1);
CurAmmoSecondary = AmmoCharge[1];
if ( KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo) != none && KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill != none )
{
MaxAmmoSecondary *= KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill.static.AddExtraAmmoFor(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo), AmmoClass[1]);
MaxAmmoSecondary = int(MaxAmmoSecondary);
}
return;
}
if ( Ammo[1] == None )
{
return;
}
MaxAmmoSecondary = Ammo[1].default.MaxAmmo;
CurAmmoSecondary = Ammo[1].AmmoAmount;
if ( KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo) != none && KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill != none )
{
MaxAmmoSecondary *= KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill.static.AddExtraAmmoFor(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo), Ammo[1].class);
MaxAmmoSecondary = int(MaxAmmoSecondary);
}
}
simulated function UpdateMagCapacity(PlayerReplicationInfo PRI)
{
if ( KFPlayerReplicationInfo(PRI) != none && KFPlayerReplicationInfo(PRI).ClientVeteranSkill != none )
{
MagCapacity = default.MagCapacity * KFPlayerReplicationInfo(PRI).ClientVeteranSkill.Static.GetMagCapacityMod(KFPlayerReplicationInfo(PRI), self);
}
else
{
MagCapacity = default.MagCapacity;
}
}
defaultproperties
{
FlashBoneName="tip"
WeaponReloadAnim = "Reload1"
bReloadEffectDone=True
Weight=10.000000
bNoAmmoInstances=False
Description="This is a very generic weapon."
LightType=LT_Steady
LightEffect=LE_NonIncidence
LightHue=30
LightSaturation=150
LightBrightness=255.000000
LightRadius=10.000000
LightPeriod=3
//PrePivot=(X=0.250000,Y=3.300000,Z=2.000000)
TransientSoundVolume=100.000000
StoppingPower = -1000
HudImage = none
AmbientGlow = 0
ModeSwitchAnim = 'AltFire'
TacShineClass = class 'TacLightShineAttachment'
bSniping=True
QuickPutDownTime=0.15
QuickBringUpTime=0.15
bHasAimingMode=false
bConsumesPhysicalAmmo=true
bReduceMagAmmoOnSecondaryFire=true
// Failsafe defaults, should be plugged in for each weapon
StandardDisplayFOV=90.0
SleeveNum=1
SellValue=-1
AppID=0
UnlockedByAchievement = -1
}