508 lines
14 KiB
Ucode
508 lines
14 KiB
Ucode
|
//=============================================================================
|
||
|
// KFWeap_GrenadeLauncher_CylinderBase
|
||
|
//=============================================================================
|
||
|
// Cylinder based grenade launcher
|
||
|
//=============================================================================
|
||
|
// Killing Floor 2
|
||
|
// Copyright (C) 2017 Tripwire Interactive LLC
|
||
|
//=============================================================================
|
||
|
|
||
|
class KFWeap_GrenadeLauncher_CylinderBase extends KFWeap_GrenadeLauncher_Base
|
||
|
native;
|
||
|
|
||
|
var int CurrentShellSlotReloading;
|
||
|
var bool bReloadingFromEmpty;
|
||
|
var bool bInitialReloadIsOpenOnly;
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Revolver vars
|
||
|
********************************************************************************************* */
|
||
|
|
||
|
// Whether this weapon is a revolver. Enables revolver functionality like rotating cylinder and noticeably-used shells
|
||
|
var bool bRevolver;
|
||
|
|
||
|
// Meshes to indicate which rounds have been fired or not
|
||
|
var array<name> BulletFXSocketNames;
|
||
|
var array<KFBulletSkeletalMeshComponent> BulletMeshComponents;
|
||
|
|
||
|
var array<name> ShellBoneNames;
|
||
|
|
||
|
var name ReloadShellSocketName;
|
||
|
var KFBulletSkeletalMeshComponent ReloadShell;
|
||
|
|
||
|
const CYLINDERSTATE_READY = 0;
|
||
|
const CYLINDERSTATE_PENDING = 1;
|
||
|
const CYLINDERSTATE_ROTATING = 2;
|
||
|
|
||
|
var CylinderRotationInfo CylinderRotInfo;
|
||
|
|
||
|
cpptext
|
||
|
{
|
||
|
// cylinder is incrementally rotated
|
||
|
virtual void TickSpecial(FLOAT DeltaSeconds);
|
||
|
};
|
||
|
|
||
|
// directly sets cylinder rotation, instead of allowing tickspecial to increment rotation
|
||
|
native simulated function SetCylinderRotation(out CylinderRotationInfo RotInfo, float Degrees);
|
||
|
|
||
|
|
||
|
/** Cache Anim Nodes from the tree
|
||
|
* @note: skipped on server because AttachComponent/AttachWeaponTo is not called
|
||
|
*/
|
||
|
simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
|
||
|
{
|
||
|
super.PostInitAnimTree(SkelComp);
|
||
|
|
||
|
if (bRevolver)
|
||
|
{
|
||
|
PostInitAnimTreeRevolver(SkelComp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Ammo
|
||
|
********************************************************************************************* */
|
||
|
|
||
|
// /**
|
||
|
// * @see Weapon::ConsumeAmmo
|
||
|
// */
|
||
|
simulated function ConsumeAmmo(byte FireModeNum)
|
||
|
{
|
||
|
local int BulletIndex;
|
||
|
|
||
|
BulletIndex = MagazineCapacity[DEFAULT_FIREMODE] - AmmoCount[DEFAULT_FIREMODE];
|
||
|
|
||
|
super.ConsumeAmmo(FireModeNum);
|
||
|
|
||
|
// update bullet mesh
|
||
|
if (BulletIndex < BulletMeshComponents.Length)
|
||
|
{
|
||
|
BulletMeshComponents[BulletIndex].SetBulletState(BS_Used);
|
||
|
}
|
||
|
|
||
|
// do revovler ammo before actually consuming ammo
|
||
|
if (bRevolver && Instigator != none && Instigator.IsLocallyControlled())
|
||
|
{
|
||
|
ConsumeAmmoRevolver();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
simulated function PerformReload(optional byte FireModeNum)
|
||
|
{
|
||
|
if (!bInitialReloadIsOpenOnly)
|
||
|
{
|
||
|
bInitialReloadIsOpenOnly = true;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
super.PerformReload(FireModeNum);
|
||
|
BulletMeshComponents[CurrentShellSlotReloading].SetBulletState(BS_Unused);
|
||
|
CurrentShellSlotReloading++;
|
||
|
}
|
||
|
|
||
|
simulated function UpdateShellsBaseOnAmmoCount(optional byte FireModeNum = 0)
|
||
|
{
|
||
|
local int i;
|
||
|
|
||
|
for (i = 0; i < BulletMeshComponents.length; i++)
|
||
|
{
|
||
|
if (BulletMeshComponents[i].GetBulletShellState() != BS_Unused && AmmoCount[FireModeNum] > i)
|
||
|
{
|
||
|
BulletMeshComponents[i].SetBulletState(BS_Unused);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
simulated function GetFirstEmptyShell()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
simulated function SetShellToState(int ShellIndex, byte ShellState)
|
||
|
{
|
||
|
switch (ShellState)
|
||
|
{
|
||
|
case 0:
|
||
|
BulletMeshComponents[ShellIndex].SetBulletState(BS_Unused);
|
||
|
break;
|
||
|
case 1:
|
||
|
BulletMeshComponents[ShellIndex].SetBulletState(BS_Used);
|
||
|
break;
|
||
|
case 2:
|
||
|
BulletMeshComponents[ShellIndex].SetBulletState(BS_Unloaded);
|
||
|
break;
|
||
|
default:
|
||
|
BulletMeshComponents[ShellIndex].SetBulletState(BS_Unused);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* State Reloading
|
||
|
* State the weapon is in when it is being reloaded (current magazine replaced with a new one, related animations and effects played).
|
||
|
*/
|
||
|
simulated state Reloading
|
||
|
{
|
||
|
ignores ForceReload, ShouldAutoReload, AllowSprinting;
|
||
|
|
||
|
simulated function BeginState(name PreviousStateName)
|
||
|
{
|
||
|
super.BeginState(PreviousStateName);
|
||
|
|
||
|
if (bRevolver)
|
||
|
{
|
||
|
ResetCylinder();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Revolver
|
||
|
********************************************************************************************* */
|
||
|
|
||
|
simulated event PostInitAnimTreeRevolver(SkeletalMeshComponent SkelComp)
|
||
|
{
|
||
|
local int i;
|
||
|
|
||
|
CylinderRotInfo.Control = SkelControlSingleBone(SkelComp.FindSkelControl('CylinderControl'));
|
||
|
if (CylinderRotInfo.Control != none)
|
||
|
{
|
||
|
CylinderRotInfo.Control.SetSkelControlActive(true);
|
||
|
}
|
||
|
|
||
|
// attach bullet meshes to correct sockets
|
||
|
for (i = 0; i < BulletMeshComponents.Length; ++i)
|
||
|
{
|
||
|
BulletMeshComponents[i].SetBulletState(BS_Unused);
|
||
|
MySkelMesh.AttachComponentToSocket(BulletMeshComponents[i], BulletFXSocketNames[i]);
|
||
|
}
|
||
|
MySkelMesh.AttachComponentToSocket(ReloadShell, ReloadShellSocketName);
|
||
|
}
|
||
|
|
||
|
simulated function ConsumeAmmoRevolver()
|
||
|
{
|
||
|
CheckCylinderRotation(CylinderRotInfo);
|
||
|
CylinderRotInfo.State = CYLINDERSTATE_PENDING;
|
||
|
}
|
||
|
|
||
|
simulated function CheckCylinderRotation(out CylinderRotationInfo RotInfo, optional bool bResetState)
|
||
|
{
|
||
|
if (RotInfo.State != CYLINDERSTATE_READY)
|
||
|
{
|
||
|
RotateCylinder(RotInfo, true);
|
||
|
|
||
|
if (bResetState)
|
||
|
{
|
||
|
RotInfo.State = CYLINDERSTATE_READY;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
simulated function ANIMNOTIFY_ResetBulletMeshes()
|
||
|
{
|
||
|
local int i;
|
||
|
|
||
|
for (i = 0; i < BulletMeshComponents.length; i++)
|
||
|
{
|
||
|
BulletMeshComponents[i].SetBulletState(i < MagazineCapacity[0] - AmmoCount[0] ? BS_Unloaded : BS_Unused );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Initiate cylinder rotation (rotation occurs in tickspecial in native) */
|
||
|
simulated function ANIMNOTIFY_RotateCylinder()
|
||
|
{
|
||
|
RotateCylinder(CylinderRotInfo);
|
||
|
}
|
||
|
|
||
|
/** Initiates cylinder rotation process */
|
||
|
simulated function RotateCylinder(out CylinderRotationInfo RotInfo, optional bool bInstant)
|
||
|
{
|
||
|
if (bInstant)
|
||
|
{
|
||
|
// don't use timer, just set rotation
|
||
|
|
||
|
if (RotInfo.State == CYLINDERSTATE_PENDING)
|
||
|
{
|
||
|
// if we're pending, we don't have an updated rotation yet, so get a rotation
|
||
|
IncrementCylinderRotation(RotInfo);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// if we're rotating already, we're going to go to a pending state after forcing rotation
|
||
|
RotInfo.State = CYLINDERSTATE_PENDING;
|
||
|
}
|
||
|
|
||
|
SetCylinderRotation(RotInfo, RotInfo.NextDegrees);
|
||
|
RotInfo.Timer = 0.f;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// use timer, rotate over time in native tickspecial
|
||
|
RotInfo.State = CYLINDERSTATE_ROTATING;
|
||
|
IncrementCylinderRotation(RotInfo);
|
||
|
RotInfo.Timer = RotInfo.Time;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
simulated function IncrementCylinderRotation(out CylinderRotationInfo RotInfo)
|
||
|
{
|
||
|
local bool bReloading;
|
||
|
|
||
|
bReloading = ReloadStatus == RS_Reloading;
|
||
|
//if reloading reverse the rotation
|
||
|
RotInfo.PrevDegrees = RotInfo.NextDegrees;
|
||
|
RotInfo.NextDegrees += bReloading ? -RotInfo.Inc : RotInfo.Inc;
|
||
|
}
|
||
|
|
||
|
simulated function ResetCylinderInfo(out CylinderRotationInfo RotInfo)
|
||
|
{
|
||
|
RotInfo.PrevDegrees = 0;
|
||
|
RotInfo.NextDegrees = 0;
|
||
|
RotInfo.State = CYLINDERSTATE_READY;
|
||
|
}
|
||
|
|
||
|
simulated event OnCylinderRotationFinished(out CylinderRotationInfo RotInfo)
|
||
|
{
|
||
|
RotInfo.State = CYLINDERSTATE_READY;
|
||
|
}
|
||
|
|
||
|
/** Resets cylinder orientation to initial state and repositions bullet meshes to line up with their pre-reset locations */
|
||
|
simulated function ResetCylinder()
|
||
|
{
|
||
|
// reset cylinder rotation (skel control needs to be reset to initial state while bullet casings are not on screen)
|
||
|
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] == 0)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Sets all bullet casing meshes back to unused state */
|
||
|
simulated function ResetBulletMeshes()
|
||
|
{
|
||
|
local int i;
|
||
|
return;
|
||
|
for (i = 0; i < BulletMeshComponents.Length; ++i)
|
||
|
{
|
||
|
BulletMeshComponents[i].SetBulletState(BS_Unused);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* 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, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Iron Sights / Zoom
|
||
|
*********************************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Adjust the FOV for the first person weapon and arms.
|
||
|
*/
|
||
|
simulated event SetFOV(float NewFOV)
|
||
|
{
|
||
|
local int i;
|
||
|
|
||
|
super.SetFOV(NewFOV);
|
||
|
|
||
|
for (i = 0; i < BulletMeshComponents.Length; ++i)
|
||
|
{
|
||
|
BulletMeshComponents[i].SetFOV(NewFOV);
|
||
|
}
|
||
|
|
||
|
if (ReloadShell != none)
|
||
|
{
|
||
|
ReloadShell.SetFOV(NewFOV);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Reload
|
||
|
*********************************************************************************************/
|
||
|
|
||
|
simulated function InitializeReload()
|
||
|
{
|
||
|
bInitialReloadIsOpenOnly = false;
|
||
|
RebuildLockedBonesForReload();
|
||
|
super.InitializeReload();
|
||
|
CheckCylinderRotation(CylinderRotInfo, true);
|
||
|
CurrentShellSlotReloading = 0;
|
||
|
}
|
||
|
|
||
|
simulated function RebuildLockedBonesForReload()
|
||
|
{
|
||
|
local int i;
|
||
|
local int NumOfShotsToReload;
|
||
|
|
||
|
if (EmptyMagBlendNode != none)
|
||
|
{
|
||
|
EmptyMagBlendNode.SetBlendTarget(0, 0);
|
||
|
}
|
||
|
|
||
|
BonesToLockOnEmpty.length = 0;
|
||
|
NumOfShotsToReload = MagazineCapacity[0] - AmmoCount[0];
|
||
|
|
||
|
for (i=NumOfShotsToReload; i < ShellBoneNames.length; i++)
|
||
|
{
|
||
|
|
||
|
BonesToLockOnEmpty.AddItem(ShellBoneNames[i]);
|
||
|
}
|
||
|
if (EmptyMagBlendNode != none && BonesToLockOnEmpty.Length > 0)
|
||
|
{
|
||
|
BuildEmptyMagNodeWeightList(EmptyMagBlendNode, BonesToLockOnEmpty);
|
||
|
EmptyMagBlendNode.SetBlendTarget(1, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Returns animation to play based on reload type and status */
|
||
|
|
||
|
simulated function name GetReloadAnimName(bool bTacticalReload)
|
||
|
{
|
||
|
if (!bReloadFromMagazine)
|
||
|
{
|
||
|
switch (ReloadStatus)
|
||
|
{
|
||
|
case RS_OpeningBolt:
|
||
|
ReloadStatus = GetNextReloadStatus();
|
||
|
|
||
|
bReloadingFromEmpty = AmmoCount[0] == 0;
|
||
|
|
||
|
return bReloadingFromEmpty ? 'Reload_Empty_Open' : 'Reload_Half_Open';
|
||
|
|
||
|
case RS_Reloading:
|
||
|
return bReloadingFromEmpty ? 'Reload_Empty_Insert' : 'Reload_Half_Insert';
|
||
|
|
||
|
case RS_ClosingBolt:
|
||
|
return bReloadingFromEmpty ? 'Reload_Empty_Close' : 'Reload_Half_Close';
|
||
|
}
|
||
|
|
||
|
return 'Reload_Half_Insert';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
simulated function UnloadAllBulletMeshes()
|
||
|
{
|
||
|
local int i;
|
||
|
|
||
|
for (i = 0; i < BulletMeshComponents.length; i++)
|
||
|
{
|
||
|
BulletMeshComponents[i].SetBulletState(BS_Unloaded);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
defaultproperties
|
||
|
{
|
||
|
ForceReloadTime=0.0f
|
||
|
|
||
|
// Inventory
|
||
|
InventoryGroup=IG_Primary
|
||
|
GroupPriority=75
|
||
|
InventorySize=6
|
||
|
WeaponSelectTexture=Texture2D'WEP_UI_M79_TEX.UI_WeaponSelect_M79' // //NEED TO REPLACE
|
||
|
|
||
|
// FOV
|
||
|
MeshIronSightFOV=52
|
||
|
PlayerIronSightFOV=73
|
||
|
|
||
|
// Zooming/Position
|
||
|
PlayerViewOffset=(X=19.0,Y=13,Z=-2)
|
||
|
FastZoomOutTime=0.2
|
||
|
|
||
|
// Content
|
||
|
PackageKey="M32_MGL"
|
||
|
FirstPersonMeshName="WEP_1P_M32_MGL_MESH.Wep_1stP_M32_MGL_Rig"
|
||
|
FirstPersonAnimSetNames(0)="WEP_1P_M32_MGL_ANIM.Wep_1stP_M32_MGL_Anim"
|
||
|
PickupMeshName="WEP_3P_M79_MESH.Wep_m79_Pickup" //NEED TO REPLACE
|
||
|
AttachmentArchetypeName="WEP_M32_MGL_ARCH.Wep_M32_MGL_3P"
|
||
|
MuzzleFlashTemplateName="WEP_M79_ARCH.Wep_M79_MuzzleFlash" // Need to replace
|
||
|
|
||
|
// Zooming/Position
|
||
|
IronSightPosition=(X=0,Y=0,Z=0)
|
||
|
|
||
|
// Ammo
|
||
|
MagazineCapacity[0]=6
|
||
|
SpareAmmoCapacity[0]=36
|
||
|
InitialSpareMags[0]=9
|
||
|
AmmoPickupScale[0]=2.0
|
||
|
bCanBeReloaded=true
|
||
|
bReloadFromMagazine=false
|
||
|
|
||
|
// Recoil
|
||
|
maxRecoilPitch=900
|
||
|
minRecoilPitch=775
|
||
|
maxRecoilYaw=500
|
||
|
minRecoilYaw=-500
|
||
|
RecoilRate=0.085
|
||
|
RecoilBlendOutRatio=0.35
|
||
|
RecoilMaxYawLimit=500
|
||
|
RecoilMinYawLimit=65035
|
||
|
RecoilMaxPitchLimit=1500
|
||
|
RecoilMinPitchLimit=64785
|
||
|
RecoilISMaxYawLimit=50
|
||
|
RecoilISMinYawLimit=65485
|
||
|
RecoilISMaxPitchLimit=500
|
||
|
RecoilISMinPitchLimit=65485
|
||
|
RecoilViewRotationScale=0.8
|
||
|
FallingRecoilModifier=1.5
|
||
|
HippedRecoilModifier=1.25
|
||
|
|
||
|
// DEFAULT_FIREMODE
|
||
|
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Grenade'
|
||
|
FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFiring
|
||
|
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile
|
||
|
//WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_HighExplosive_M32'
|
||
|
FireInterval(DEFAULT_FIREMODE)=+0.25
|
||
|
InstantHitDamage(DEFAULT_FIREMODE)=150.0
|
||
|
// InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_M32Impact'
|
||
|
Spread(DEFAULT_FIREMODE)=0.015
|
||
|
FireOffset=(X=23,Y=4.0,Z=-3)
|
||
|
|
||
|
// ALT_FIREMODE
|
||
|
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring
|
||
|
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_None
|
||
|
|
||
|
// BASH_FIREMODE
|
||
|
// InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_M32'
|
||
|
InstantHitDamage(BASH_FIREMODE)=26
|
||
|
|
||
|
// Fire Effects
|
||
|
WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_SA_M79.Play_WEP_SA_M79_Fire_M', FirstPersonCue=AkEvent'WW_WEP_SA_M79.Play_WEP_SA_M79_Fire_S')
|
||
|
|
||
|
//@todo: add akevent when we have it
|
||
|
WeaponDryFireSnd(DEFAULT_FIREMODE)=none
|
||
|
|
||
|
// Animation
|
||
|
bHasFireLastAnims=true
|
||
|
|
||
|
// Attachments
|
||
|
bHasIronSights=true
|
||
|
bHasFlashlight=false
|
||
|
|
||
|
AssociatedPerkClasses(0)=class'KFPerk_Demolitionist'
|
||
|
|
||
|
WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Heavy_Recoil_SingleShot'
|
||
|
|
||
|
// Revolver
|
||
|
bRevolver=true
|
||
|
CylinderRotInfo=(Inc=72.0, Time=0.0875/*about 0.35 in the anim divided by ratescale of 4*/)
|
||
|
|
||
|
ReloadShellSocketName="RW_Shell_Reload"
|
||
|
}
|