2092 lines
56 KiB
Ucode
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
|
|
}
|