//============================================================================= // KFWeap_DualBase //============================================================================= // Dual weapon base class //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC // Jeff Robinson //============================================================================= class KFWeap_DualBase extends KFWeap_PistolBase native; // trader needs this to be native for now (6/10/15) so we can access SingleClass /** Animations to play when the weapon is fired */ var(Animations) const editconst name LeftFireAnim; /** Animation to play when the weapon is fired */ var(Animations) const editconst array LeftFireSightedAnims; /** A muzzle flash instance for left weapon */ var KFMuzzleFlash LeftMuzzleFlash; /** Holds an offest for spawning protectile effects for left weapon */ var() vector LeftFireOffset; /** Whether we should fire from the right-side weapon (alternates right and left fire) */ var transient bool bFireFromRightWeapon; /** class of the single variant of this weapon */ var class SingleClass; /********************************************************************************************* * @name Animations (Using const anim names to reduce instanced data cost) ********************************************************************************************* */ /** Cached anim nodes */ var AnimNodeBlendPerBone EmptyMagBlendNode_L; /** array of bones to lock when out of ammo */ var array BonesToLockOnEmpty_L; /** Anims for ironsight and alternate ironsight mode **/ var(Animations) const editconst name IdleToIronSightAnim; var(Animations) const editconst name IdleToIronSightAnim_Alt; var(Animations) const editconst name IronSightToIdleAnim; var(Animations) const editconst name IronSightToIdleAnim_Alt; var(Animations) const editconst array IdleSightedAnims_Alt; var(Animations) const editconst name FireSightedAnim_Alt; var(Animations) const editconst name LeftFireSightedAnim_Alt; var(Animations) const editconst name EquipAnimIS; var(Animations) const editconst name EquipAnimISAlt; var(Animations) const editconst name LeftFireLastAnim; var(Animations) const editconst name LeftFireLastSightedAnim; var(Animations) const editconst name FireLastSightedAnim_Alt; var(Animations) const editconst name LeftFireLastSightedAnim_Alt; const ReloadOneEmptyAnim = 'Reload_Empty_Half'; const ReloadOneEmptyEliteAnim = 'Reload_Empty_Half_Elite'; /********************************************************************************************* * @name Revolver vars ********************************************************************************************* */ var CylinderRotationInfo CylinderRotInfo_L; cpptext { virtual void PreBeginPlay(); virtual void RefreshSkinItemId(); // cylinder is incrementally rotated virtual void TickSpecial( FLOAT DeltaSeconds ); }; /** Cache Anim Nodes from the tree * @note: skipped on server because AttachComponent/AttachWeaponTo is not called */ simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp) { local KFGameEngine KFGEngine; super.PostInitAnimTree( SkelComp ); EmptyMagBlendNode_L = AnimNodeBlendPerBone(SkelComp.FindAnimNode('EmptyMagBlend_L')); if( EmptyMagBlendNode_L != none && BonesToLockOnEmpty_L.Length > 0 ) { BuildEmptyMagNodeWeightList( EmptyMagBlendNode_L, BonesToLockOnEmpty_L ); } KFGEngine = KFGameEngine(Class'KFGameEngine'.static.GetEngine()); if(KFGEngine != none) { bUseAltFireMode = KFGEngine.bUseAltAimOnDual; } if( !bRevolver ) { return; } CylinderRotInfo_L.Control = SkelControlSingleBone( SkelComp.FindSkelControl('CylinderControl_L') ); if( CylinderRotInfo_L.Control != none ) { CylinderRotInfo_L.Control.SetSkelControlActive( true ); } } /** * Called on a client, this function Attaches the WeaponAttachment * to the Mesh. * * Overridden to attach LeftMuzzleFlash */ simulated function AttachMuzzleFlash() { super.AttachMuzzleFlash(); if ( MySkelMesh != none ) { if (MuzzleFlashTemplate != None) { LeftMuzzleFlash = new(self) Class'KFMuzzleFlash'(MuzzleFlashTemplate); LeftMuzzleFlash.AttachMuzzleFlash(MySkelMesh, 'MuzzleFlash_L', 'ShellEject_L'); } } } /********************************************************************************************* * State Active * A Weapon this is being held by a pawn should be in the active state. In this state, * a weapon should loop any number of idle animations, as well as check the PendingFire flags * to see if a shot has been fired. *********************************************************************************************/ simulated state Active { simulated function ZoomIn(bool bAnimateTransition, float ZoomTimeToGo) { GotoState('ActiveIronSights'); } simulated function PlayIdleAnim() { local int IdleIndex; if ( Instigator.IsFirstPerson() ) { if( bUsingSights && IdleSightedAnims.Length > 0 ) { if( bUseAltFireMode ) { IdleIndex = Rand(IdleSightedAnims_Alt.Length); PlayAnimation(IdleSightedAnims_Alt[IdleIndex], 0.0, true, 0.1); } else { IdleIndex = Rand(IdleSightedAnims.Length); PlayAnimation(IdleSightedAnims[IdleIndex], 0.0, true, 0.1); } } else if ( IdleAnims.Length > 0 ) { IdleIndex = Rand(IdleAnims.Length); PlayAnimation(IdleAnims[IdleIndex], 0.0, true, 0.2); } StartIdleFidgetTimer(); ToggleAdditiveBobAnim(!bUsingSights); } } simulated function bool CanPlayIdleFidget(optional bool bOnReload) { // Make sure we can't play idle fidget if one of the pistols is empty. if(AmmoCount[0] < 2) { return false; } return Super.CanPlayIdleFidget(bOnReload); } } /********************************************************************************************* * State WeaponEquipping * The Weapon is in this state while transitioning from Inactive to Active state. * Typically, the weapon will remain in this state while its selection animation is being played. * While in this state, the weapon cannot be fired. *********************************************************************************************/ /** Get equip anim name (overridden as necessary) */ simulated function name GetEquipAnimName() { // since duals use anims to get into / out of iron sights, // we need to play equip anims that blend to iron sight idle (including alt iron) if( bIronSightOnBringUp ) { return bUseAltFireMode ? EquipAnimISAlt : EquipAnimIS; } else { return EquipAnim; } } /********************************************************************************************* * State ActiveIronSights * Only duals play an animation when transitioning to or from iron sights, but this animation * should only be played in the "Active" state. This state allows us to encapsulate this * functionality. *********************************************************************************************/ simulated state ActiveIronSights extends Active { simulated function ZoomOut(bool bAnimateTransition, float ZoomTimeToGo) { local name IronToIdleAnimName; IronToIdleAnimName = GetIronToIdleAnim(); ZoomTimeToGo = MySkelMesh.GetAnimLength(IronToIdleAnimName); Global.ZoomOut( true, ZoomTimeToGo ); PlayAnimation( IronToIdleAnimName, ZoomTime, false ); GotoState('Active'); } simulated function BeginState( Name PreviousStateName ) { local float ZoomTimeToGo; local name IdleToIronAnimName; IdleToIronAnimName = GetIdleToIronAnim(); ZoomTimeToGo = MySkelMesh.GetAnimLength(IdleToIronAnimName); Global.ZoomIn( true, ZoomTimeToGo ); PlayAnimation( IdleToIronAnimName, ZoomTime, false ); } } /** Gets idle-to-iron anim with regard to "stance" (toggle by alt fire) */ simulated function name GetIdleToIronAnim() { return bUseAltFireMode ? IdleToIronSightAnim_Alt : IdleToIronSightAnim; } /** Gets iron-to-idle anim with regard to "stance" (toggle by alt fire) */ simulated function name GetIronToIdleAnim() { return bUseAltFireMode ? IronSightToIdleAnim_Alt : IronSightToIdleAnim; } /********************************************************************************************* * State WeaponSingleFiring * Fire must be released between every shot. * * Overridden to alternate between right and left fire before calculated muzzle positions, etc. *********************************************************************************************/ simulated state WeaponSingleFiring { simulated function FireAmmunition() { bFireFromRightWeapon = !bFireFromRightWeapon; Super.FireAmmunition(); } } /********************************************************************************************* * State Reloading * This is the default Reloading State. It's performed on both the client and the server. * * Overridden to reset our alternating weapon fire *********************************************************************************************/ simulated state Reloading { simulated function BeginState(name PreviousStateName) { super.BeginState( PreviousStateName ); bFireFromRightWeapon = false; if( bRevolver ) { ResetCylinderLeft(); } } simulated function byte GetWeaponStateId() { local KFPerk Perk; local bool bTacticalReload; Perk = GetPerk(); bTacticalReload = (Perk != None && Perk.GetUsingTactialReload(self)); if( AmmoCount[0] == 1 ) { return bTacticalReload ? WEP_ReloadDualsOneEmpty_Elite : WEP_ReloadDualsOneEmpty; } return super.GetWeaponStateId(); } } /** Returns animation to play based on reload type and status */ simulated function name GetReloadAnimName( bool bTacticalReload ) { if( AmmoCount[0] == 1 ) { // if one gun is empty (always the right), play our half empty reload return bTacticalReload ? ReloadOneEmptyEliteAnim : ReloadOneEmptyAnim; } else { return super.GetReloadAnimName( bTacticalReload ); } } /** * Increment Pawn's FlashCount variable. * This is used to play weapon fire effects on remote clients. * Call this on the server and local player. * * Network: Server and Local Player * * COMPLETELY OVERRIDDEN to set FlashCount to an even number for left fire. * Also copies implementation of Pawn::IncrementFlashCount (usually called from super) */ simulated function IncrementFlashCount() { if( Instigator != none ) { if( Instigator.FlashCount > 0 || bFireFromRightWeapon ) { Instigator.FlashCount += 1; } else { Instigator.FlashCount += 2; } //// BEGIN PAWN.INCREMENTFLASHCOUNT Instigator.SetFiringMode( Self, CurrentFireMode ); // This weapon has fired. Instigator.FlashCountUpdated( Self, Instigator.FlashCount, FALSE ); //// END PAWN.INCREMENTFLASHCOUNT } } /** Return true if this weapon should play the fire last animation for this shoot animation */ simulated function bool ShouldPlayFireLast(byte FireModeNum) { if ( bHasFireLastAnims ) { if( bFireFromRightWeapon ) { if( (!bAllowClientAmmoTracking && Role < ROLE_Authority && AmmoCount[GetAmmoType(FireModeNum)] <= 2) || ((bAllowClientAmmoTracking || Role == ROLE_Authority) && AmmoCount[GetAmmoType(FireModeNum)] == 1) ) { return true; } } else { if( (!bAllowClientAmmoTracking && Role < ROLE_Authority && AmmoCount[GetAmmoType(FireModeNum)] <= 1) || ((bAllowClientAmmoTracking || Role == ROLE_Authority) && AmmoCount[GetAmmoType(FireModeNum)] == 0) ) { return true; } } } return false; } /** Get name of the animation to play for PlayFireEffects * * Overridden to allow for left weapon anims */ simulated function name GetWeaponFireAnim(byte FireModeNum) { local bool bPlayFireLast; bPlayFireLast = ShouldPlayFireLast(FireModeNum); if( bFireFromRightWeapon ) { if ( bUsingSights ) { if( bPlayFireLast ) { return bUseAltFireMode ? FireLastSightedAnim_Alt : FireLastSightedAnim; } else { return bUseAltFireMode ? FireSightedAnim_Alt : FireSightedAnims[Rand(LeftFireSightedAnims.Length)]; } } else { if( bPlayFireLast ) { return FireLastAnim; } else { return FireAnim; } } } else { return GetLeftWeaponFireAnim(FireModeNum, bPlayFireLast); } } /** Get name of the animation to play for PlayFireEffects */ simulated function name GetLeftWeaponFireAnim(byte FireModeNum, bool bPlayFireLast) { // ironsights animations if ( bUsingSights ) { if( bPlayFireLast ) { return bUseAltFireMode ? LeftFireLastSightedAnim_Alt : LeftFireLastSightedAnim; } else { return bUseAltFireMode ? LeftFireSightedAnim_Alt : LeftFireSightedAnims[Rand(LeftFireSightedAnims.Length)]; } } else { if( bPlayFireLast ) { return LeftFireLastAnim; } else { return LeftFireAnim; } } } /** * This function returns the world location for spawning the visual effects * * Overridden to allow for left weapon location */ simulated event vector GetMuzzleLoc() { if( bFireFromRightWeapon ) { return super.GetMuzzleLoc(); } else { return GetLeftMuzzleLoc(); } } /** * This function returns the world location for spawning the visual effects. * Uses both X and Y position of LeftFireOffset. */ simulated event vector GetLeftMuzzleLoc() { local Rotator ViewRotation; if( Instigator != none ) { ViewRotation = Instigator.GetViewRotation(); // Add in the free-aim rotation if ( KFPlayerController(Instigator.Controller) != None ) { ViewRotation += KFPlayerController(Instigator.Controller).WeaponBufferRotation; } if( bUsingSights ) { return Instigator.GetWeaponStartTraceLocation() + (LeftFireOffset >> ViewRotation); } else { return Instigator.GetPawnViewLocation() + (LeftFireOffset >> ViewRotation); } } return Location; } /** * Causes the muzzle flash to turn on and setup a time to * turn it back off again. * * Overridden to cause left weapon flash */ simulated function CauseMuzzleFlash(byte FireModeNum) { if( MuzzleFlash == None || LeftMuzzleFlash == None ) { AttachMuzzleFlash(); } if( bFireFromRightWeapon ) { if (MuzzleFlash != None ) { MuzzleFlash.CauseMuzzleFlash(FireModeNum); if ( MuzzleFlash.bAutoActivateShellEject ) { MuzzleFlash.CauseShellEject(); } } } else { if( LeftMuzzleFlash != None ) { LeftMuzzleFlash.CauseMuzzleFlash( FireModeNum ); if( LeftMuzzleFlash.bAutoActivateShellEject ) { LeftMuzzleFlash.CauseShellEject(); } } } } /** * Remove/Detach the muzzle flash components */ simulated function DetachMuzzleFlash() { super.DetachMuzzleFlash(); if (MySkelMesh != none && LeftMuzzleFlash != None) { LeftMuzzleFlash.DetachMuzzleFlash(MySkelMesh); LeftMuzzleFlash = None; } } /** * Adjust the FOV for the first person weapon and arms. */ simulated event SetFOV( float NewFOV ) { super.SetFOV( NewFOV ); if( LeftMuzzleFlash != none ) { LeftMuzzleFlash.SetFOV( NewFOV ); } } simulated function StopFireEffects(byte FireModeNum) { super.StopFireEffects( FireModeNum ); if (LeftMuzzleFlash != None) { LeftMuzzleFlash.StopMuzzleFlash(); } } /** Returns ID of muzzle to spawn projectile at / play effects at * Overrides super */ simulated function byte GetCurrentMuzzleID() { return bFireFromRightWeapon ? 0 : 1; } /** Overridden to spawn a single and set the pickup class to single */ function SetupDroppedPickup( out DroppedPickup P, vector StartVelocity ) { local KFWeapon NewSingle; local KFInventoryManager KFIM; local vector X,Y,Z; local int NewSingleUpgradeIndex; local int SingleSpareAmmoCount; // For now, force the dropped single to be un-upgraded by setting this dual's upgrade index to 0. // But remember what the upgrade index was so we can give the player a correctly upgraded single. // SetWeaponUpgradeLevel(0) must be called before the super, because the pickup mesh is set there. NewSingleUpgradeIndex = CurrentWeaponUpgradeIndex; SetWeaponUpgradeLevel(0); super.SetupDroppedPickup( P, StartVelocity ); if( Instigator != None && Instigator.InvManager != None ) { KFIM = KFInventoryManager( Instigator.InvManager ); KFIM.bSuppressPickupMessages = true; KFIM.bInfiniteWeight = true; // force CanCarryWeapon() to succeed NewSingle = KFWeapon( KFIM.CreateInventory(SingleClass, true) ); KFIM.bInfiniteWeight = false; KFIM.bSuppressPickupMessages = false; } if( NewSingle != none ) { // divide ammo between make sure we don't lose a round due to truncation. NewSingle.AmmoCount[0] = (AmmoCount[0] & 1) == 0 ? (AmmoCount[0] / 2) : (AmmoCount[0] / 2) + 1; AmmoCount[0] /= 2; SingleSpareAmmoCount = Min(SpareAmmoCount[0], NewSingle.SpareAmmoCapacity[0]); NewSingle.SpareAmmoCount[0] = SingleSpareAmmoCount; SpareAmmoCount[0] -= SingleSpareAmmoCount; // tell client about our modification NewSingle.ClientForceAmmoUpdate(NewSingle.AmmoCount[0],NewSingle.SpareAmmoCount[0]); NewSingle.ClientForceSecondaryAmmoUpdate(NewSingle.AmmoCount[1]); NewSingle.SetWeaponUpgradeLevel(NewSingleUpgradeIndex); if(NewSingleUpgradeIndex > 0) { KFInventoryManager(InvManager).AddCurrentCarryBlocks(NewSingle.static.GetUpgradeWeight(NewSingleUpgradeIndex)); KFPawn(Instigator).NotifyInventoryWeightChanged(); } NewSingle.bGivenAtStart = bGivenAtStart; // Drop second gun on death if( Instigator.bPlayedDeath || Instigator.Health <= 0 ) { GetAxes( Instigator.Rotation, X,Y,Z ); NewSingle.DropFrom( P.Location + Y*20, StartVelocity*(1.f + fRand()*0.1f) ); } else { Instigator.InvManager.SetCurrentWeapon( NewSingle ); } } P.InventoryClass = SingleClass; } /********************************************************************************************* * @name Ammo ********************************************************************************************* */ // /** Performs actual ammo reloading */ simulated function PerformReload(optional byte FireModeNum) { super.PerformReload(FireModeNum); if( !bRevolver ) { return; } // reset cylinder rotation CylinderRotInfo_L.PrevDegrees = 0; CylinderRotInfo_L.NextDegrees = 0; } /** Locks the bolt bone in place to the open position (Called by animnotify) * Completely overrides super to pick which bolt to lock back */ simulated function ANIMNOTIFY_LockBolt() { UpdateOutOfAmmoEffects(0.f); } /** Unlocks the bolt bone (Called by animnotify) */ simulated function ANIMNOTIFY_UnLockBolt() { super.ANIMNOTIFY_UnLockBolt(); EmptyMagBlendNode_L.SetBlendTarget(0, 0); } /** Check AmmoCount and update anim tree nodes if needed */ simulated function UpdateOutOfAmmoEffects(float BlendTime) { if ( WorldInfo.NetMode == NM_DedicatedServer ) return; if( EmptyMagBlendNode != None ) { // Differentiate Left/Right if ( bAllowClientAmmoTracking && AmmoCount[0] <= 1 ) { EmptyMagBlendNode.SetBlendTarget(1, 0); if ( AmmoCount[0] == 0 ) { EmptyMagBlendNode_L.SetBlendTarget(1,0); } } } } /********************************************************************************************* * @name Revolver ********************************************************************************************* */ simulated event PostInitAnimTreeRevolver( SkeletalMeshComponent SkelComp ) { super.PostInitAnimTreeRevolver( SkelComp ); CylinderRotInfo_L.Control = SkelControlSingleBone( SkelComp.FindSkelControl('CylinderControl') ); if( CylinderRotInfo_L.Control != none ) { CylinderRotInfo_L.Control.SetSkelControlActive( true ); } } simulated function ConsumeAmmoRevolver() { // no super if( bFireFromRightWeapon ) { CheckCylinderRotation( CylinderRotInfo_L ); CylinderRotInfo.State = CYLINDERSTATE_PENDING; } else { CheckCylinderRotation( CylinderRotInfo ); CylinderRotInfo_L.State = CYLINDERSTATE_PENDING; } } /** Initiate cylinder rotation (rotation occurs in tickspecial in native) */ simulated function ANIMNOTIFY_RotateCylinder() { if( bFireFromRightWeapon ) { super.ANIMNOTIFY_RotateCylinder(); return; } RotateCylinder( CylinderRotInfo_L ); } simulated function InitializeReload() { // call kfweapon super so we don't rotate the other cylinder too super(KFWeapon).InitializeReload(); CheckCylinderRotation( CylinderRotInfo_L, true ); } simulated function ANIMNOTIFY_ResetBulletMeshesLeft() { ResetBulletMeshesLeft(); } /** Resets cylinder orientation to initial state and repositions bullet meshes to line up with their pre-reset locations */ simulated function ResetCylinder() { local int UsedStartIdx, UsedEndIdx, UsedBullets; // reset cylinder rotation SetCylinderRotation( CylinderRotInfo, 0 ); ResetCylinderInfo( CylinderRotInfo ); // now, we need to make sure the used bullets are still in the correct positions // if we fired all our ammo, it doesn't matter where the bullets are, because they're all the same (used) if( AmmoCount[DEFAULT_FIREMODE] <= 1 ) { return; } // find the range of bullets that have been used UsedStartIdx = BulletMeshComponents.Length - 2; UsedBullets = FCeil( float(MagazineCapacity[DEFAULT_FIREMODE] - AmmoCount[DEFAULT_FIREMODE]) / 2.f ); UsedEndIdx = UsedStartIdx - UsedBullets * 2; RepositionUsedBullets( 0, UsedStartIdx, UsedEndIdx ); } /** Resets cylinder orientation to initial state and repositions bullet meshes to line up with their pre-reset locations */ simulated function ResetCylinderLeft() { local int UsedStartIdx, UsedEndIdx, UsedBullets; // reset cylinder rotation (skel control needs to be reset to initial state while bullet casings are not on screen) SetCylinderRotation( CylinderRotInfo_L, 0 ); ResetCylinderInfo( CylinderRotInfo_L ); // now, we need to make sure the used bullets are still in the correct positions // if we fired all our ammo, it doesn't matter where the bullets are, because they're all the same (used) if( AmmoCount[DEFAULT_FIREMODE] <= 0 ) { return; } // find the range of bullets that have been used UsedStartIdx = BulletMeshComponents.Length - 1; UsedBullets = FFloor( float(MagazineCapacity[DEFAULT_FIREMODE] - AmmoCount[DEFAULT_FIREMODE]) / 2.f ); UsedEndIdx = UsedStartIdx - UsedBullets * 2; RepositionUsedBullets( 1, UsedStartIdx, UsedEndIdx ); } /** Sets correct bullet mesh (used/unused) in each slot after resetting cylinder */ simulated function RepositionUsedBullets( int FirstIndex, int UsedStartIdx, int UsedEndIdx ) { local int i; if (BulletMeshComponents.length == 0) { return; } if(FirstIndex >= 0 && FirstIndex < BulletMeshComponents.length) { // after cylinder reset, top-most bullet will be unused BulletMeshComponents[FirstIndex].SetSkeletalMesh( UnusedBulletMeshTemplate ); } else { `warn(self @ "-" @ GetFuncName() @ "- First Index is out of bounds - FirstIndex:" @ FirstIndex @ "BulletMeshComponents.Length:" @ BulletMeshComponents.length); } // set used bullets to used mesh for( i = UsedStartIdx; i > UsedEndIdx; i-=2 ) { if(i >= 0 && i < BulletMeshComponents.length) { BulletMeshComponents[i].SetSkeletalMesh( UsedBulletMeshTemplate ); } } // set the rest of the bullets to unused for( i = UsedEndIdx; i > FirstIndex; i-=2 ) { if (i >= 0 && i < BulletMeshComponents.length) { BulletMeshComponents[i].SetSkeletalMesh( UnusedBulletMeshTemplate ); } } } /** Sets all bullet casing meshes back to unused state */ simulated function ResetBulletMeshes() { local int i; for( i = 0; i < BulletMeshComponents.Length; i+=2 ) { BulletMeshComponents[i].SetSkeletalMesh( UnusedBulletMeshTemplate ); } } /** Sets all bullet casing meshes back to unused state */ simulated function ResetBulletMeshesLeft() { local int i; for( i = 1; i < BulletMeshComponents.Length; i+=2 ) { BulletMeshComponents[i].SetSkeletalMesh( UnusedBulletMeshTemplate ); } } /********************************************************************************************* * State WeaponPuttingDown * Putting down weapon in favor of a new one. * Weapon is transitioning to the Inactive state. *********************************************************************************************/ simulated state WeaponPuttingDown { simulated function EndState(Name NextStateName) { super.EndState( NextStateName ); CheckCylinderRotation( CylinderRotInfo_L, true ); } } /////////////////////////////////////////////////////////////////////////////////////////// // // Trader // /////////////////////////////////////////////////////////////////////////////////////////// /** Adds ammo to (or subtracts ammo from) the trader transaction single that gets created when selling a dual */ native simulated function AddAmmoToSingleOnSell( KFInventoryManager KFIM, int DefaultSingleAmmo, int TraderItemIndex ); /********************************************************************************************* * @name Firing / Projectile ********************************************************************************************* */ /** * @brief Checks if weapon should be auto-reloaded - overwritten to allow gunslinger insta switch * * @param FireModeNum Current fire mode * @return auto reload or not */ simulated function bool ShouldAutoReload( byte FireModeNum ) { return ShouldAutoReloadGunslinger( FireModeNum ); } /** * Toggle between DEFAULT and ALTFIRE */ simulated function AltFireMode() { super.AltFireMode(); PlayIdleAnim(); } /** * @see Weapon::StartFire */ simulated function StartFire(byte FireModeNum) { // These weapons only have a mode toggle, so if we have alt-fire // bound (e.g. gamepad, custom bindings) then perform the toggle if( FireModeNum == ALTFIRE_FIREMODE ) { AltFireMode(); return; } Super.StartFire(FireModeNum); } defaultproperties { InventoryGroup=IG_Primary Begin Object Name=FirstPersonMesh AnimTreeTemplate=AnimTree'CHR_1P_Arms_ARCH.WEP_1stP_Dual_Animtree_Master' End Object bSkipZoomInRotation=true BonesToLockOnEmpty_L=(LW_Bolt) // Animations FireAnim=Shoot_RW LeftFireAnim=Shoot_LW IdleSightedAnims=(Idle_IronOG) FireSightedAnims=(Shoot_IronOG_RW) LeftFireSightedAnims=(Shoot_IronOG_LW) IdleToIronSightAnim=Idle_To_IronOG IronSightToIdleAnim=IronOG_To_Idle EquipAnimIS=Equip_IronOG // IdleSightedAnims=(Idle_Iron) // FireSightedAnims=(Shoot_Iron_RW) // LeftFireSightedAnims=(Shoot_Iron_LW) // IdleToIronSightAnim=Idle_To_Iron // IronSightToIdleAnim=Iron_To_Idle // EquipAnimIS=Equip_Iron IdleSightedAnims_Alt=(Idle_Iron) FireSightedAnim_Alt=Shoot_Iron_RW LeftFireSightedAnim_Alt=Shoot_Iron_LW IdleToIronSightAnim_Alt=Idle_To_Iron IronSightToIdleAnim_Alt=Iron_To_Idle EquipAnimISAlt=Equip_Iron // IdleSightedAnims_Alt=(Idle_IronOG) // FireSightedAnim_Alt=Shoot_IronOG_RW // LeftFireSightedAnim_Alt=Shoot_IronOG_LW // IdleToIronSightAnim_Alt=Idle_To_IronOG // IronSightToIdleAnim_Alt=IronOG_To_Idle // EquipAnimISAlt=Equip_IronOG FireLastAnim=Shoot_RW_Last LeftFireLastAnim=Shoot_LW_Last FireLastSightedAnim=Shoot_IronOG_RW_Last LeftFireLastSightedAnim=Shoot_IronOG_LW_Last FireLastSightedAnim_Alt=Shoot_Iron_RW_Last LeftFireLastSightedAnim_Alt=Shoot_Iron_LW_Last // DEFAULT_FIREMODE FireModeIconPaths[DEFAULT_FIREMODE]=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle' FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit FireInterval(DEFAULT_FIREMODE)=+1.0 InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic' InstantHitMomentum(DEFAULT_FIREMODE)=1.0 PenetrationPower(DEFAULT_FIREMODE)=0.0 PenetrationDamageReductionCurve(DEFAULT_FIREMODE)=(Points=((InVal=0.f,OutVal=0.f),(InVal=1.f, OutVal=1.f))) //ALTFIRE_FIREMODE FireModeIconPaths[ALTFIRE_FIREMODE]=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle' FiringStatesArray(ALTFIRE_FIREMODE)=WeaponFiring WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_InstantHit FireInterval(ALTFIRE_FIREMODE)=+1.0 InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic' InstantHitMomentum(ALTFIRE_FIREMODE)=1.0 PenetrationPower(ALTFIRE_FIREMODE)=0.0 PenetrationDamageReductionCurve(ALTFIRE_FIREMODE)=(Points=((InVal=0.f,OutVal=0.f),(InVal=1.f, OutVal=1.f))) }