288 lines
8.8 KiB
Ucode
288 lines
8.8 KiB
Ucode
//=============================================================================
|
|
// KFWeap_LMG_Stoner63A
|
|
//=============================================================================
|
|
// The Stoner63A rifle
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2017 Tripwire Interactive LLC
|
|
// - Matt "Squirrlz" Farber
|
|
//=============================================================================
|
|
class KFWeap_LMG_Stoner63A extends KFWeap_RifleBase;
|
|
|
|
/** Alternate reload anims (when below the bullet belt threshold */
|
|
const ReloadLowAmmoAnim = 'Reload_Empty_Half';
|
|
const ReloadLowAmmoEliteAnim = 'Reload_Empty_Half_Elite';
|
|
|
|
/** Array of bone names corresponding to bullets in the ammo belt */
|
|
var protected const string AmmoBeltBulletBonePrefix;
|
|
|
|
/** Number of bullets in the ammo belt */
|
|
var protected const int NumAmmoBeltBullets;
|
|
|
|
/** How much ammo we had the last time we updated the ammo belt */
|
|
var transient protected int LastAmmoCount;
|
|
|
|
/** TRUE when set for the first time */
|
|
var transient protected bool bAmmoBeltInitialized;
|
|
|
|
/** Just in case we spawned in with an ammo count that's lower than the threshold */
|
|
simulated event ReplicatedEvent( name VarName )
|
|
{
|
|
super.ReplicatedEvent( VarName );
|
|
|
|
if( !bAmmoBeltInitialized )
|
|
{
|
|
UpdateAmmoBeltBullets();
|
|
}
|
|
}
|
|
|
|
/** Just in case we spawned in with an ammo count that's lower than the threshold */
|
|
simulated event PostBeginPlay()
|
|
{
|
|
super.PostBeginPlay();
|
|
|
|
if( !bAmmoBeltInitialized && Role == ROLE_Authority )
|
|
{
|
|
UpdateAmmoBeltBullets();
|
|
}
|
|
}
|
|
|
|
/** Update ammo belt */
|
|
simulated function ConsumeAmmo( byte FireModeNum )
|
|
{
|
|
super.ConsumeAmmo( FireModeNum );
|
|
|
|
UpdateAmmoBeltBullets();
|
|
}
|
|
|
|
/** Update ammo belt */
|
|
simulated function PerformReload( optional byte FireModeNum )
|
|
{
|
|
super.PerformReload( FireModeNum );
|
|
|
|
UpdateAmmoBeltBullets();
|
|
}
|
|
|
|
/** Notify triggered by reload animations, when ammo belt is grabbed offscreen */
|
|
simulated function ANIMNOTIFY_RestoreAmmoBelt()
|
|
{
|
|
local int AmmoType, PendingAmmoCount;
|
|
|
|
AmmoType = GetAmmoType(0);
|
|
|
|
// This value will sync with the amount actually reloaded in PerformReload()
|
|
PendingAmmoCount = Min( AmmoCount[AmmoType] + SpareAmmoCount[AmmoType], MagazineCapacity[AmmoType] );
|
|
if( PendingAmmoCount < NumAmmoBeltBullets )
|
|
{
|
|
UpdateAmmoBeltBullets( PendingAmmoCount );
|
|
}
|
|
else
|
|
{
|
|
UpdateAmmoBeltBullets( , true );
|
|
}
|
|
}
|
|
|
|
/** Hides/shows bones on the weapon to simulate reaching the end of the ammo belt */
|
|
simulated function UpdateAmmoBeltBullets( optional int ForcedBulletCount=INDEX_NONE, optional bool bShowAll=false )
|
|
{
|
|
local Name BulletBoneName;
|
|
local int NumAmmo, i;
|
|
|
|
if( WorldInfo.NetMode == NM_DedicatedServer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Don't do any bone alterations until the bone array has been filled out
|
|
if( MySkelMesh.LocalAtoms.Length == 0 )
|
|
{
|
|
// Check on the next frame to see if we can init our ammo belt
|
|
if( !IsTimerActive(nameOf(Timer_AttemptAmmoBeltUpdate)) )
|
|
{
|
|
SetTimer( 0.01f, false, nameOf(Timer_AttemptAmmoBeltUpdate) );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// So we don't do this in PostInitAnimTree() or again in Timer_AttemptAmmoBeltUpdate() if we don't have to
|
|
bAmmoBeltInitialized = true;
|
|
|
|
// Don't do anything if ammo hasn't changed
|
|
NumAmmo = ForcedBulletCount != INDEX_NONE ? ForcedBulletCount : AmmoCount[GetAmmoType(0)];
|
|
if( !bShowAll && (LastAmmoCount == NumAmmo || (LastAmmoCount >= NumAmmoBeltBullets && NumAmmo >= NumAmmoBeltBullets)) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Hide or unhide depending on place in array
|
|
for( i = 0; i < NumAmmoBeltBullets; ++i )
|
|
{
|
|
BulletBoneName = Name( AmmoBeltBulletBonePrefix $ (i+1) );
|
|
|
|
// Unhide all bullets if our ammo count is higher than the number of bullets
|
|
if( bShowAll || NumAmmo >= NumAmmoBeltBullets )
|
|
{
|
|
MySkelMesh.UnHideBoneByName( BulletBoneName );
|
|
continue;
|
|
}
|
|
|
|
if( i > NumAmmo-1 )
|
|
{
|
|
MySkelMesh.HideBoneByName( BulletBoneName, PBO_None );
|
|
}
|
|
else
|
|
{
|
|
MySkelMesh.UnHideBoneByName( BulletBoneName );
|
|
}
|
|
}
|
|
|
|
LastAmmoCount = NumAmmo;
|
|
}
|
|
|
|
simulated function Timer_AttemptAmmoBeltUpdate()
|
|
{
|
|
if( !bAmmoBeltInitialized )
|
|
{
|
|
UpdateAmmoBeltBullets();
|
|
}
|
|
}
|
|
|
|
/** Returns animation to play based on reload type and status */
|
|
simulated function name GetReloadAnimName( bool bTacticalReload )
|
|
{
|
|
if( AmmoCount[0] > 0 && AmmoCount[0] < NumAmmoBeltBullets )
|
|
{
|
|
// If we are below the threshold, play our low ammo reload
|
|
return bTacticalReload ? ReloadLowAmmoEliteAnim : ReloadLowAmmoAnim;
|
|
}
|
|
else
|
|
{
|
|
return super.GetReloadAnimName( bTacticalReload );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
simulated state WeaponEquipping
|
|
{
|
|
simulated event BeginState( Name PreviousStateName )
|
|
{
|
|
super.BeginState( PreviousStateName );
|
|
|
|
// Just in case a reload was interrupted after ANIMNOTIFY_RestoreAmmoBelt() was called,
|
|
// set the ammo belt to the current amount of bullets in the magazine on equip
|
|
UpdateAmmoBeltBullets();
|
|
}
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
// Shooting Animations
|
|
FireSightedAnims[0]=Shoot_Iron
|
|
FireSightedAnims[1]=Shoot_Iron2
|
|
FireSightedAnims[2]=Shoot_Iron3
|
|
|
|
// FOV
|
|
MeshFOV=75
|
|
MeshIronSightFOV=35
|
|
PlayerIronSightFOV=70
|
|
|
|
// Depth of field
|
|
DOF_FG_FocalRadius=85
|
|
DOF_FG_MaxNearBlurSize=2.5
|
|
|
|
// Content
|
|
PackageKey="Stoner63a"
|
|
FirstPersonMeshName="WEP_1P_Stoner63A_MESH.Wep_1stP_Stoner63A_Rig"
|
|
FirstPersonAnimSetNames(0)="WEP_1P_Stoner63A_ANIM.Wep_1stP_Stoner63A_Anim"
|
|
PickupMeshName="WEP_3P_Stoner63A_MESH.Wep_Stoner63A_Pickup"
|
|
AttachmentArchetypeName="WEP_Stoner63A_ARCH.Wep_Stoner63A_3P"
|
|
MuzzleFlashTemplateName="WEP_Stoner63A_ARCH.Wep_Stoner63A_MuzzleFlash"
|
|
|
|
// Zooming/Position
|
|
PlayerViewOffset=(X=4.0,Y=8,Z=-4)
|
|
IronSightPosition=(X=8.5,Y=0,Z=0)
|
|
|
|
// Ammo
|
|
MagazineCapacity[0]=75
|
|
SpareAmmoCapacity[0]=500
|
|
InitialSpareMags[0]=1
|
|
bCanBeReloaded=true
|
|
bReloadFromMagazine=true
|
|
|
|
// Recoil
|
|
maxRecoilPitch=120
|
|
minRecoilPitch=70
|
|
maxRecoilYaw=130
|
|
minRecoilYaw=-130
|
|
RecoilRate=0.08
|
|
RecoilMaxYawLimit=500
|
|
RecoilMinYawLimit=65035
|
|
RecoilMaxPitchLimit=900
|
|
RecoilMinPitchLimit=65035
|
|
RecoilISMaxYawLimit=75
|
|
RecoilISMinYawLimit=65460
|
|
RecoilISMaxPitchLimit=375
|
|
RecoilISMinPitchLimit=65460
|
|
RecoilViewRotationScale=0.25
|
|
IronSightMeshFOVCompensationScale=2.3
|
|
HippedRecoilModifier=1.5
|
|
|
|
// Inventory / Grouping
|
|
InventorySize=9
|
|
GroupPriority=100
|
|
WeaponSelectTexture=Texture2D'wep_ui_stoner63a_tex.UI_WeaponSelect_Stoner'
|
|
AssociatedPerkClasses(0)=class'KFPerk_Commando'
|
|
|
|
// DEFAULT_FIREMODE
|
|
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletAuto'
|
|
FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring
|
|
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit
|
|
WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_AssaultRifle'
|
|
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_Stoner63A'
|
|
FireInterval(DEFAULT_FIREMODE)=+0.066 // 900 RPM
|
|
Spread(DEFAULT_FIREMODE)=0.0085
|
|
InstantHitDamage(DEFAULT_FIREMODE)=30.0 //25 //30
|
|
FireOffset=(X=30,Y=4.5,Z=-5)
|
|
|
|
// ALT_FIREMODE
|
|
FireModeIconPaths(ALTFIRE_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletAuto'
|
|
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponFiring
|
|
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None
|
|
|
|
// BASH_FIREMODE
|
|
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_Stoner63A'
|
|
InstantHitDamage(BASH_FIREMODE)=26
|
|
|
|
// Fire Effects
|
|
WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Stoner.Play_WEP_Stoner_Fire_3P_Loop', FirstPersonCue=AkEvent'WW_WEP_Stoner.Play_WEP_Stoner_Fire_1P_Loop')
|
|
WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Stoner.Play_WEP_Stoner_Fire_3P_Single', FirstPersonCue=AkEvent'WW_WEP_Stoner.Play_WEP_Stoner_Fire_1P_Single')
|
|
WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Stoner.Play_WEP_Stoner_Fire_3P_EndLoop', FirstPersonCue=AkEvent'WW_WEP_Stoner.Play_WEP_Stoner_Fire_1P_EndLoop')
|
|
WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_L85A2.Play_WEP_SA_L85A2_Handling_DryFire'
|
|
WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_SA_L85A2.Play_WEP_SA_L85A2_Handling_DryFire'
|
|
EjectedShellForegroundDuration=0.8f
|
|
|
|
// Advanced (High RPM) Fire Effects
|
|
bLoopingFireAnim(DEFAULT_FIREMODE)=true
|
|
bLoopingFireSnd(DEFAULT_FIREMODE)=true
|
|
SingleFireSoundIndex=ALTFIRE_FIREMODE
|
|
|
|
// Attachments
|
|
bHasIronSights=true
|
|
bHasFlashlight=false
|
|
|
|
// Ammo belt
|
|
AmmoBeltBulletBonePrefix="RW_Bullets"
|
|
NumAmmoBeltBullets=14
|
|
LastAmmoCount=-1
|
|
|
|
// Weapon Upgrade stat boosts
|
|
//WeaponUpgrades[1]=(IncrementDamage=1.27f,IncrementWeight=1)
|
|
|
|
WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.27f), (Stat=EWUS_Weight, Add=1)))
|
|
}
|