1
0
KF2-Dev-Scripts/KFGameContent/Classes/KFWeap_Eviscerator.uc

490 lines
16 KiB
Ucode
Raw Normal View History

2020-12-13 15:01:13 +00:00
//=============================================================================
// KFWeap_Eviscerator
//=============================================================================
// A hybrid melee weapon that shoots circular saw blades
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
// Andrew Ladenberger and John "Ramm-Jaeger" Gibson
//=============================================================================
class KFWeap_Eviscerator extends KFWeap_MeleeBase;
/** Anim names */
const IdleNoGasAnim = 'Idle_NoGas';
const BlockLoopNoGasAnim = 'Brace_loop_NoGas';
/** BlendNode that's active while weapon has fuel */
var AnimNodeAdditiveBlending AdditiveBlendNode;
/** BlendNode that's active when primary ammo is empty */
var AnimNodeBlendPerBone OutOfBladesBlendNode;
/** Sound to play while weapon has fuel */
var AKEvent IdleMotorSound;
/** Short time from end of FireInterval when blocking can cancel the firing state */
var float BlockInterruptFiringTime;
//------------------------------------------------------------------------------
/**
* Called immediately before gameplay begins.
*/
simulated event PreBeginPlay()
{
Super.PreBeginPlay();
// to prevent exploits the fire interrupt cannot be longer than the block intro
BlockInterruptFiringTime = FMin(BlockInterruptFiringTime, MySkelMesh.GetAnimLength(MeleeBlockStartAnim));
}
/** The SkeletalMeshComponents Animations are being instanced from AnimTreeTemplate
* before PostInitAnimTree. Be sure to never set the Mesh's animations directly through
* the package */
simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
{
Super.PostInitAnimTree(SkelComp);
AdditiveBlendNode = AnimNodeAdditiveBlending(SkelComp.FindAnimNode('MotorAdditiveBlend'));
OutOfBladesBlendNode = AnimNodeBlendPerBone(SkelComp.FindAnimNode('OutOfBladesBlend'));
}
/* OVERRIDE BECAUSE WE DONT NEED TO LOOK AT ALTFIRE_MODE, AS FOR MELEE WEAPONS IS THE BLOCK FIREMODE AND WILL RESULT IN
THE WEAPON ALWAYS GLOWING GREEN
*/
simulated function bool HasAnyAmmo()
{
if ( HasSpareAmmo() || HasAmmo(DEFAULT_FIREMODE) )
{
return true;
}
if (MedicComp != none)
{
return false;
}
if( UsesSecondaryAmmo() && (HasSpareAmmo(HEAVY_ATK_FIREMODE) || HasAmmo(HEAVY_ATK_FIREMODE) ))
{
return true;
}
return false;
}
/*********************************************************************************************
* @name Ammunition
********************************************************************************************* */
/**
* @see Weapon::StartFire
*/
simulated function StartFire(byte FireModeNum)
{
// If we're out of fuel, do a melee 'bash' instead
if( FireModeNum == HEAVY_ATK_FIREMODE && !HasAmmo(FireModeNum) )
{
FireModeNum = BASH_FIREMODE;
}
// skip MeleeBase logic
Super(KFWeapon).StartFire(FireModeNum);
}
/**
* Returns which ammo pool a fire mode should pull from, primary or secondary.
@ param FiringMode the fire mode we want to check against
@ return the ammo pool we should pull from for the input fire mode
*/
simulated function int GetAmmoType(byte FiringMode)
{
//UI looking for altfire
if( FiringMode == HEAVY_ATK_FIREMODE || FiringMode == ALTFIRE_FIREMODE)
{
return 1;
}
else
{
return 0;
}
}
/*********************************************************************************************
* @name Firing / Projectile
********************************************************************************************* */
/** process local player impact for clientside hit detection */
event RecieveClientImpact(byte FiringMode, const out ImpactInfo Impact, optional out float PenetrationValue, optional int ImpactNum)
{
if ( FiringMode == DEFAULT_FIREMODE )
{
super(KFWeapon).RecieveClientImpact(FiringMode, Impact, PenetrationValue);
}
else
{
MeleeAttackHelper.ProcessMeleeHit(FiringMode, Impact);
}
}
/*********************************************************************************************
* @section Animation
********************************************************************************************* */
simulated function HideBlade()
{
// @todo: remove this stub if/when we remove the notifies from the anim
}
simulated function UnHideBlade()
{
// @todo: remove this stub if/when we remove the notifies from the anim
}
/** Check if we're out of blades or fuel and toggle effects accordingly */
simulated function UpdateOutOfAmmoEffects(float BlendTime)
{
if ( AmmoCount[1] > 0 )
{
PlayIdleMotorSound();
if ( AdditiveBlendNode != None )
{
AdditiveBlendNode.SetBlendTarget(1.f, BlendTime);
}
}
else
{
StopIdleMotorSound();
if ( AdditiveBlendNode != None )
{
AdditiveBlendNode.SetBlendTarget(0.f, BlendTime);
}
}
if ( OutOfBladesBlendNode != None )
{
OutOfBladesBlendNode.SetBlendTarget((AmmoCount[0] > 0) ? 0.f : 1.f, BlendTime);
}
}
/** Check to see if sound is already playing before calling SetWeaponAmbientSound */
simulated function PlayIdleMotorSound()
{
local KFPawn P;
P = KFPawn(Instigator);
if ( P != None && P.WeaponAmbientSound != IdleMotorSound )
{
P.SetWeaponAmbientSound(IdleMotorSound);
}
}
/** Check to see if sound is already stopped before calling SetWeaponAmbientSound */
simulated function StopIdleMotorSound()
{
local KFPawn P;
P = KFPawn(Instigator);
if ( P != None && P.WeaponAmbientSound != None )
{
P.SetWeaponAmbientSound(None);
}
}
/*********************************************************************************************
* State Active
* Extended to handle IdleMotorSound
*********************************************************************************************/
simulated state Active
{
simulated function BeginState(name PreviousStateName)
{
Super.BeginState(PreviousStateName);
UpdateOutOfAmmoEffects(0.2f);
}
simulated function PlayIdleAnim()
{
if ( Instigator.IsFirstPerson() )
{
// If we're out of fuel play a special idle with no motor vibration
if ( AmmoCount[1] <= 0 )
{
PlayAnimation(IdleNoGasAnim, 0.0, true, 0.2);
}
else
{
Super.PlayIdleAnim();
}
}
}
}
/*********************************************************************************************
* state Inactive
* Extended to handle IdleMotorSound
*********************************************************************************************/
auto state Inactive
{
simulated function BeginState(name PreviousStateName)
{
Super.BeginState(PreviousStateName);
StopIdleMotorSound();
}
// moved to kfweapon
//simulated function EndState(Name NextStateName)
//{
// Super.EndState(NextStateName);
// UpdateOutOfAmmoEffects(0.0f);
//}
}
/*********************************************************************************************
* State Reloading
* This is the default Reloading State. It's performed on both the client and the server.
*********************************************************************************************/
simulated state Reloading
{
simulated function BeginState(name PreviousStateName)
{
Super.BeginState(PreviousStateName);
// When the player starts reload force unlock the OutOfBlades bones temporarily
if ( OutOfBladesBlendNode != None )
{
OutOfBladesBlendNode.SetBlendTarget(0.f, 0.1f);
}
}
}
/** Called during reload state */
simulated function bool CanOverrideMagReload(byte FireModeNum)
{
return super.CanOverrideMagReload(FireModeNum) || FireModeNum == BLOCK_FIREMODE || FireModeNum == HEAVY_ATK_FIREMODE;
}
/*********************************************************************************************
* State MeleeBlocking
* This is the default Blocking State. It's performed on both the client and the server.
*********************************************************************************************/
simulated state MeleeBlocking
{
// Override to play BlockLoopNoGasAnim
simulated function BlockLoopTimer()
{
if( AmmoCount[1] <= 0 && Instigator.IsLocallyControlled() )
{
PlayAnimation(BlockLoopNoGasAnim, , true);
}
Super.BlockLoopTimer();
}
}
/*********************************************************************************************
* State MeleeSustained
* Derived to prevent Active:BeginState from playing the looping idle sound from UpdateOutOfAmmoEffects after the super changes the state to MeleeSustained
*********************************************************************************************/
simulated state MeleeSustained
{
// This prevents the Active:BeginState from playing the looping idle sound after the super changes the state to MeleeSustained
ignores UpdateOutOfAmmoEffects;
}
/*********************************************************************************************
* Trader
********************************************************************************************/
/** Allows weapon to calculate its own damage for display in trader
* While eviscerator is a melee weapon, it does more damage with its default fire projectile
*/
static simulated function float CalculateTraderWeaponStatDamage()
{
local float CalculatedDamage;
local class<KFDamageType> DamageType;
CalculatedDamage = default.InstantHitDamage[DEFAULT_FIREMODE];
DamageType = class<KFDamageType>(default.InstantHitDamageTypes[DEFAULT_FIREMODE]);
if( DamageType != none && DamageType.default.DoT_Type != DOT_None )
{
CalculatedDamage += (DamageType.default.DoT_Duration / DamageType.default.DoT_Interval) * (CalculatedDamage * DamageType.default.DoT_DamageScale);
}
return CalculatedDamage;
}
/** Returns trader filter index based on weapon type */
static simulated event EFilterTypeUI GetTraderFilter()
{
return FT_Projectile;
}
static simulated event EFilterTypeUI GetAltTraderFilter()
{
return FT_Melee;
}
/*********************************************************************************************
* State WeaponSingleFiring
* Handle special BlockInterruptTimer for this weapon's unusually long FireInterval
*********************************************************************************************/
simulated function BlockInterruptTimer();
simulated state WeaponSingleFiring
{
/** Set cooldown duration */
simulated function BeginState( Name PreviousStateName )
{
local float CheckBlockInterruptTime;
CheckBlockInterruptTime = FireInterval[CurrentFireMode] - BlockInterruptFiringTime;
if ( BlockInterruptFiringTime > 0 && CheckBlockInterruptTime > 0 )
{
SetTimer(CheckBlockInterruptTime, false, nameof(BlockInterruptTimer));
}
Super.BeginState(PreviousStateName);
}
simulated function EndState(Name NextStateName)
{
ClearTimer( nameof(BlockInterruptTimer) );
Super.EndState(NextStateName);
}
/** Check for pending fire */
simulated function BlockInterruptTimer()
{
if ( PendingFire(BLOCK_FIREMODE) && HasAmmo(BLOCK_FIREMODE) )
{
SendToFiringState(BLOCK_FIREMODE);
}
}
/** Check for interrupt near end of state */
simulated function BeginFire(byte FireModeNum)
{
Global.BeginFire(FireModeNum);
if ( FireModeNum == BLOCK_FIREMODE && BlockInterruptFiringTime > 0 )
{
if( HasAmmo(FireModeNum) && !IsTimerActive(nameof(BlockInterruptTimer)) )
{
SendToFiringState(FireModeNum);
}
}
}
}
defaultproperties
{
// Inventory
GroupPriority=100
InventorySize=9
InventoryGroup=IG_Primary
WeaponSelectTexture=Texture2D'ui_weaponselect_tex.UI_WeaponSelect_SawbladeShooter'
SecondaryAmmoTexture=Texture2D'UI_SecondaryAmmo_TEX.GasTank'
// Content
PackageKey="SawBlade"
FirstPersonMeshName="WEP_1P_SawBlade_MESH.Wep_1stP_SawBlade_Rig"
FirstPersonAnimSetNames(0)="WEP_1P_SawBlade_ANIM.WEP_1P_SawBlade_ANIM"
FirstPersonAnimTree="WEP_1P_SawBlade_ANIM.1P_Sawblade_Animtree"
PickupMeshName="WEP_3P_SawBlade_MESH.Wep_SawShooter_Pickup"
AttachmentArchetypeName="WEP_Sawblade_ARCH.Wep_Eviscerator_3P"
MuzzleFlashTemplateName="WEP_Sawblade_ARCH.Wep_Sawblade_MuzzleFlash"
Begin Object Name=MeleeHelper_0
MaxHitRange=180
WorldImpactEffects=KFImpactEffectInfo'FX_Impacts_ARCH.Bladed_melee_impact'
MeleeImpactCamShake=CameraShake'FX_CameraShake_Arch.Melee.Eviscerator'
End Object
// ------------------------------------------------------------------------
// Fire modes
FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFiring
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Projectile
WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Blade_Eviscerator'
InstantHitDamage(DEFAULT_FIREMODE)=300.0
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Slashing_EvisceratorProj'
Spread(DEFAULT_FIREMODE)=0.02
PenetrationPower(DEFAULT_FIREMODE)=4.0
FireInterval(DEFAULT_FIREMODE)=0.95 // 63 RPM
FireOffset=(X=25,Y=5.0,Z=-10)
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Sawblade'
AmmoCost(DEFAULT_FIREMODE) = 1
BlockInterruptFiringTime=0.5
// Saw attack (uses fuel)
FiringStatesArray(HEAVY_ATK_FIREMODE)=MeleeSustained
InstantHitDamage(HEAVY_ATK_FIREMODE)=29
InstantHitDamageTypes(HEAVY_ATK_FIREMODE)=class'KFDT_Slashing_Eviscerator'
FireInterval(HEAVY_ATK_FIREMODE)=+0.12
AmmoCost(HEAVY_ATK_FIREMODE)=1
MeleeSustainedWarmupTime=0.1
// BASH_FIREMODE
FiringStatesArray(BASH_FIREMODE)=MeleeAttackBasic
WeaponFireTypes(BASH_FIREMODE)=EWFT_Custom
InstantHitDamage(BASH_FIREMODE)=90
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Slashing_Eviscerator'
// RELOAD
FiringStatesArray(RELOAD_FIREMODE)="Reloading"
// Blocking
ParryDamageMitigationPercent=0.3
BlockDamageMitigation=0.4
// Fire Effects
WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_SA_SawBlade.Play_WEP_SA_Sawblade_Fire_3P', FirstPersonCue=AkEvent'WW_WEP_SA_SawBlade.Play_WEP_SA_Sawblade_Fire_1P')
WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_SawBlade.Play_WEP_SA_Sawblade_Handling_DryFire'
// Altfire/MeleeSustained
WeaponFireSnd(HEAVY_ATK_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_SA_SawBlade.Play_WEP_SA_Sawblade_Fire_Alt_LoopStart_3P', FirstPersonCue=AkEvent'WW_WEP_SA_SawBlade.Play_WEP_SA_Sawblade_Fire_Alt_LoopStart_1P')
WeaponFireLoopEndSnd(HEAVY_ATK_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_SA_SawBlade.Play_WEP_SA_Sawblade_Fire_Alt_LoopEnd_3P', FirstPersonCue=AkEvent'WW_WEP_SA_SawBlade.Play_WEP_SA_Sawblade_Fire_Alt_LoopEnd_1P')
bLoopingFireAnim(HEAVY_ATK_FIREMODE)=true
bLoopingFireSnd(HEAVY_ATK_FIREMODE)=true
WeaponDryFireSnd(HEAVY_ATK_FIREMODE)=AkEvent'WW_WEP_SA_SawBlade.Play_WEP_SA_Sawblade_Handling_DryFire'
IdleMotorSound=AkEvent'WW_WEP_SA_SawBlade.Play_WEP_SA_Sawblade_Idle_Loop'
// Ammo
bCanBeReloaded=true
bReloadFromMagazine=true
MagazineCapacity[0]=5
SpareAmmoCapacity[0]=25
InitialSpareMags[0]=0
MagazineCapacity[1]=250 // 30 seconds of fuel
AmmoPickupScale[1]=0.2
// Animation
MeleeAttackAnims=(Atk_B)
bHasFireLastAnims=true
QuickWeaponDownRotation=(Pitch=-8192,Yaw=0,Roll=8192)
// Block Sounds
BlockSound=AkEvent'WW_WEP_Bullet_Impacts.Play_Block_MEL_Katana'
ParrySound=AkEvent'WW_WEP_Bullet_Impacts.Play_Block_MEL_Katana'
AssociatedPerkClasses(0)=class'KFPerk_Berserker'
bHasLaserSight=TRUE
ParryStrength=5
// Eviscerator uses its own anim tree with its own specified bones to lock, so leave it alone
BonesToLockOnEmpty.Empty()
WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Heavy_Recoil_SingleShot'
// Weapon Upgrade stat boosts
//WeaponUpgrades[1]=(IncrementDamage=1.1f,IncrementWeight=1)
WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.1f), (Stat=EWUS_Damage1, Scale=1.1f), (Stat=EWUS_Damage2, Scale=1.1f), (Stat=EWUS_Weight, Add=1)))
}