//============================================================================= // KFWeap_HRG_Energy //============================================================================= //============================================================================= // Killing Floor 2 // Copyright (C) 2021 Tripwire Interactive LLC //============================================================================= class KFWeap_HRG_Energy extends KFWeap_PistolBase; `define HRG_ENERGY_MIC_LED_INDEX_1 0 `define HRG_ENERGY_MIC_SCREEN_INDEX 1 `define HRG_ENERGY_MIC_LED_INDEX_2 2 /********************************************************************************************* @name Optics UI ********************************************************************************************* */ var class OpticsUIClass; var KFGFxWorld_MedicOptics OpticsUI; /** The last updated value for our ammo - Used to know when to update our optics ammo */ var byte StoredPrimaryAmmo; var byte StoredSecondaryAmmo; var transient float AltFireMaxShots; /** Modifier applied to the alt fire animation */ var float SecondaryFireAnimRateModifier; /** Material colors applied to different fire modes */ var LinearColor DefaultFireMaterialColor; var LinearColor AltFireMaterialColor; /** How much recoil the altfire should do */ var protected const float AltFireRecoilScale; simulated event PreBeginPlay() { super.PreBeginPlay(); AltFireMaxShots = MagazineCapacity[DEFAULT_FIREMODE] / AmmoCost[ALTFIRE_FIREMODE]; } simulated function Activate() { super.Activate(); UpdateMaterial(bUseAltFireMode ? ALTFIRE_FIREMODE : DEFAULT_FIREMODE); } simulated function UpdateMaterial(byte FireMode) { local LinearColor MatColor; MatColor = FireMode == DEFAULT_FIREMODE ? DefaultFireMaterialColor : AltFireMaterialColor; if( WeaponMICs.Length > `HRG_ENERGY_MIC_LED_INDEX_1 ) { WeaponMICs[`HRG_ENERGY_MIC_LED_INDEX_1].SetVectorParameterValue('Vector_GlowColor', MatColor); } if (WeaponMICs.Length > `HRG_ENERGY_MIC_SCREEN_INDEX) { WeaponMICs[`HRG_ENERGY_MIC_SCREEN_INDEX].SetVectorParameterValue('Color_override', MatColor); } if( WeaponMICs.Length > `HRG_ENERGY_MIC_LED_INDEX_2 ) { WeaponMICs[`HRG_ENERGY_MIC_LED_INDEX_2].SetVectorParameterValue('Vector_GlowColor', MatColor); } } /** * Check target locking - server-side only * HealAmmo Regen client and server */ simulated event Tick( FLOAT DeltaTime ) { if (Instigator != none && Instigator.weapon == self) { UpdateOpticsUI(); } Super.Tick(DeltaTime); } /********************************************************************************************* @name Optics UI ********************************************************************************************* */ /** Get our optics movie from the inventory once our InvManager is created */ reliable client function ClientWeaponSet(bool bOptionalSet, optional bool bDoNotActivate) { local KFInventoryManager KFIM; super.ClientWeaponSet(bOptionalSet, bDoNotActivate); if (OpticsUI == none) { KFIM = KFInventoryManager(InvManager); if (KFIM != none) { //Create the screen's UI piece OpticsUI = KFGFxWorld_MedicOptics(KFIM.GetOpticsUIMovie(OpticsUIClass)); } } } function ItemRemovedFromInvManager() { local KFInventoryManager KFIM; local KFWeap_MedicBase KFW; Super.ItemRemovedFromInvManager(); if (OpticsUI != none) { KFIM = KFInventoryManager(InvManager); if (KFIM != none) { // @todo future implementation will have optics in base weapon class foreach KFIM.InventoryActors(class'KFWeap_MedicBase', KFW) { // This is not a MedicBase, no need to check against itself if(KFW.OpticsUI.Class == OpticsUI.class) { // A different weapon is still using this optics class return; } } //Create the screen's UI piece KFIM.RemoveOpticsUIMovie(OpticsUI.class); OpticsUI.Close(); OpticsUI = none; } } } /** Unpause our optics movie and reinitialize our ammo when we equip the weapon */ simulated function AttachWeaponTo(SkeletalMeshComponent MeshCpnt, optional Name SocketName) { super.AttachWeaponTo(MeshCpnt, SocketName); if (OpticsUI != none) { OpticsUI.SetPause(false); OpticsUI.ClearLockOn(); UpdateOpticsUI(true); OpticsUI.SetShotPercentCost( AmmoCost[ALTFIRE_FIREMODE]); } } /** Pause the optics movie once we unequip the weapon so it's not playing in the background */ simulated function DetachWeapon() { local Pawn OwnerPawn; super.DetachWeapon(); OwnerPawn = Pawn(Owner); if( OwnerPawn != none && OwnerPawn.Weapon == self ) { if (OpticsUI != none) { OpticsUI.SetPause(); } } } /** * Update our displayed ammo count if it's changed */ simulated function UpdateOpticsUI(optional bool bForceUpdate) { if (OpticsUI != none && OpticsUI.OpticsContainer != none) { if (AmmoCount[DEFAULT_FIREMODE] != StoredPrimaryAmmo || bForceUpdate) { StoredPrimaryAmmo = AmmoCount[DEFAULT_FIREMODE]; OpticsUI.SetPrimaryAmmo(StoredPrimaryAmmo); if(AmmoCount[DEFAULT_FIREMODE] < AmmoCost[ALTFIRE_FIREMODE]) { OpticsUI.SetHealerCharge(0); } else { OpticsUI.SetHealerCharge((AmmoCount[DEFAULT_FIREMODE] / AmmoCost[ALTFIRE_FIREMODE]) / AltFireMaxShots * 100); } } if(OpticsUI.MinPercentPerShot != AmmoCost[ALTFIRE_FIREMODE]) { OpticsUI.SetShotPercentCost( AmmoCost[ALTFIRE_FIREMODE] ); } } } /** Healing charge doesn't count as ammo for purposes of inventory management (e.g. switching) */ simulated function bool HasAnyAmmo() { return HasSpareAmmo() || HasAmmo(DEFAULT_FIREMODE) || HasSpareAmmo(ALTFIRE_FIREMODE) || HasAmmo(ALTFIRE_FIREMODE); } simulated function StartFire(byte FireModeNum) { if (FireModeNum == DEFAULT_FIREMODE && bUseAltFireMode) { if (AmmoCount[FireModeNum] < AmmoCost[ALTFIRE_FIREMODE] && SpareAmmoCount[FireModeNum] > 0) { BeginFire(RELOAD_FIREMODE); return; } } super.StartFire(FireModeNum); } simulated function AltFireMode() { if ( !Instigator.IsLocallyControlled() ) { return; } if ( !bUseAltFireMode && SpareAmmoCount[0] + AmmoCount[0] < AmmoCost[1] ) { return; } super.AltFireMode(); UpdateMaterial(bUseAltFireMode ? ALTFIRE_FIREMODE : DEFAULT_FIREMODE); NotifyAltFireUsage(); } /** Overriden to use WeaponAnimSeqNode */ simulated function PlayWeaponAnimation(name Sequence, float fDesiredDuration, optional bool bLoop, optional SkeletalMeshComponent SkelMesh) { local float DesiredRate; if ( Mesh != none && Instigator != none && WorldInfo.NetMode != NM_DedicatedServer ) { if ( WeaponAnimSeqNode != None ) { if (WeaponAnimSeqNode.AnimSeq == None || WeaponAnimSeqNode.AnimSeq.SequenceName != Sequence) { WeaponAnimSeqNode.SetAnim(Sequence); } if(fDesiredDuration > 0.0 && WeaponAnimSeqNode.AnimSeq.RateScale > 0.0) { DesiredRate = WeaponAnimSeqNode.AnimSeq.SequenceLength / (fDesiredDuration * WeaponAnimSeqNode.AnimSeq.RateScale); WeaponAnimSeqNode.PlayAnim(bLoop, DesiredRate); } else { WeaponAnimSeqNode.PlayAnim(bLoop, DefaultAnimSpeed); } } } } /** * PlayFireEffects Is the root function that handles all of the effects associated with * a weapon. This function creates the 1st person effects. It should only be called * on a locally controlled player. */ simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocation ) { local name WeaponFireAnimName; local KFPerk CurrentPerk; local float TempTweenTime, AdjustedAnimLength; // If we have stopped the looping fire sound to play single fire sounds for zed time // start the looping sound back up again when the time is back above zed time speed if( FireModeNum < bLoopingFireSnd.Length && bLoopingFireSnd[FireModeNum] && !bPlayingLoopingFireSnd ) { StartLoopingFireSound(FireModeNum); } PlayFiringSound(CurrentFireMode); if( Instigator != none ) { // Tell our pawn about any changes in animation speed UpdateWeaponAttachmentAnimRate( GetThirdPersonAnimRate() ); if( Instigator.IsLocallyControlled() ) { if( Instigator.IsFirstPerson() ) { if ( !bPlayingLoopingFireAnim ) { WeaponFireAnimName = GetWeaponFireAnim(FireModeNum); if ( WeaponFireAnimName != '' ) { AdjustedAnimLength = MySkelMesh.GetAnimLength(WeaponFireAnimName); TempTweenTime = FireTweenTime; if (FireModeNum == ALTFIRE_FIREMODE) { AdjustedAnimLength *= SecondaryFireAnimRateModifier; } CurrentPerk = GetPerk(); if( CurrentPerk != none ) { CurrentPerk.ModifyRateOfFire( AdjustedAnimLength, self ); // We need to unlock the slide if we fire from zero ammo while uber ammo is active if( EmptyMagBlendNode != none && BonesToLockOnEmpty.Length > 0 && AmmoCount[GetAmmoType(FireModeNum)] == 0 && CurrentPerk.GetIsUberAmmoActive(self) ) { EmptyMagBlendNode.SetBlendTarget( 0, 0 ); TempTweenTime = 0.f; } } PlayAnimation(WeaponFireAnimName, AdjustedAnimLength,, TempTweenTime); } } // Start muzzle flash effect CauseMuzzleFlash(FireModeNum); } HandleRecoil(); ShakeView(); if (AmmoCount[0] == 0 && ForceReloadTimeOnEmpty > 0) { SetTimer(ForceReloadTimeOnEmpty, false, nameof(ForceReload)); } } } } simulated function ModifyRecoil( out float CurrentRecoilModifier ) { if( CurrentFireMode == ALTFIRE_FIREMODE ) { CurrentRecoilModifier *= AltFireRecoilScale; } super.ModifyRecoil( CurrentRecoilModifier ); } simulated function NotifyAltFireUsage() { local KFPawn_Human KFPH; // Notify 3P to change material. KFPH = KFPawn_Human(Instigator); if (KFPH != none) { KFPH.SetUsingAltFireMode(bUseAltFireMode, true); KFPH.bNetDirty = true; } } simulated state WeaponEquipping { simulated function BeginState(Name PreviousStateName) { super.BeginState(PreviousStateName); // NotifyAltFireUsage(); if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone) { SetTimer(1.0f, false, nameof(NotifyAltFireUsage)); } } } /** Returns animation to play based on reload type and status */ simulated function name GetReloadAnimName( bool bTacticalReload ) { return bTacticalReload ? ReloadNonEmptyMagEliteAnim : ReloadNonEmptyMagAnim; } simulated function ConsumeAmmo( byte FireModeNum ) { super.ConsumeAmmo(FireModeNum); if( bUseAltFireMode && SpareAmmoCount[0] + AmmoCount[0] < AmmoCost[1] ) { bUseAltFireMode = false; UpdateMaterial(DEFAULT_FIREMODE); NotifyAltFireUsage(); } } defaultproperties { // Inventory InventoryGroup=IG_Secondary InventorySize=4 GroupPriority=125 bCanThrow=true bDropOnDeath=true WeaponSelectTexture=Texture2D'WEP_UI_HRG_Energy_TEX.UI_WeaponSelect_HRG_Energy' SecondaryAmmoTexture=Texture2D'UI_SecondaryAmmo_TEX.MedicDarts' AssociatedPerkClasses(0)=class'KFPerk_Gunslinger' // Shooting Animations FireSightedAnims[0]=Shoot_Iron FireSightedAnims[1]=Shoot_Iron2 FireSightedAnims[2]=Shoot_Iron3 // FOV MeshFOV=86 MeshIronSightFOV=77 PlayerIronSightFOV=77 // Depth of field DOF_FG_FocalRadius=40 DOF_FG_MaxNearBlurSize=3.5 // Zooming/Position PlayerViewOffset=(X=29.0,Y=13,Z=-4) //Content PackageKey="HRG_Energy" FirstPersonMeshName="WEP_1P_HRG_Energy_MESH.Wep_1stP_HRG_Energy_Rig" FirstPersonAnimSetNames(0)="WEP_1P_HRG_Energy_ANIM.WEP_1P_HRG_Energy_ANIM" PickupMeshName="wep_3p_HRG_Energy_mesh.Wep_HRG_Energy_Pickup" AttachmentArchetypeName="WEP_HRG_Energy_ARCH.Wep_HRG_Energy_3P" MuzzleFlashTemplateName="WEP_HRG_Energy_ARCH.Wep_HRG_Energy_MuzzleFlash" OpticsUIClass=class'KFGFxWorld_MedicOptics' // Zooming/Position IronSightPosition=(X=15,Y=0,Z=0) // Ammo MagazineCapacity[0]=15 SpareAmmoCapacity[0]=120 //135 InitialSpareMags[0]=2 bCanBeReloaded=true bReloadFromMagazine=true MagazineCapacity[1]=0 bCanRefillSecondaryAmmo=false // Recoil maxRecoilPitch=475 //250 minRecoilPitch=425 //200 maxRecoilYaw=130 //100 minRecoilYaw=-130 //-100 RecoilRate=0.07 RecoilMaxYawLimit=500 RecoilMinYawLimit=65035 RecoilMaxPitchLimit=900 RecoilMinPitchLimit=65035 RecoilISMaxYawLimit=50 RecoilISMinYawLimit=65485 RecoilISMaxPitchLimit=250 RecoilISMinPitchLimit=65485 AltFireRecoilScale=2.0f // DEFAULT_FIREMODE FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle' FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFiring WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_HRG_Energy' FireInterval(DEFAULT_FIREMODE)=+0.175 //342 RPM PenetrationPower(DEFAULT_FIREMODE)=1.0 InstantHitDamage(DEFAULT_FIREMODE)=80 //90.0 //125.0 InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_HRG_Energy_Primary' Spread(DEFAULT_FIREMODE)=0.015 FireOffset=(X=20,Y=4.0,Z=-3) // ALTFIRE_FIREMODE FireModeIconPaths(ALTFIRE_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Electricity' FiringStatesArray(ALTFIRE_FIREMODE)=WeaponFiring WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_InstantHit FireInterval(ALTFIRE_FIREMODE)=+0.705 //85 RPM WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Bullet_HRG_Energy_Secondary' InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_HRG_Energy_Secondary' InstantHitMomentum(ALTFIRE_FIREMODE)=1.0 PenetrationPower(ALTFIRE_FIREMODE)=3.0 InstantHitDamage(ALTFIRE_FIREMODE)=120.0 // 300.0 //475.0 PenetrationDamageReductionCurve(ALTFIRE_FIREMODE)=(Points=((InVal=0.f,OutVal=0.f),(InVal=1.f, OutVal=1.f))) AmmoCost(ALTFIRE_FIREMODE)=3 // BASH_FIREMODE InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_HRG_Energy' InstantHitDamage(BASH_FIREMODE)=26 // Fire Effects WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'ww_wep_hrg_energy.Play_WEP_HRG_Energy_3P_Shoot', FirstPersonCue=AkEvent'ww_wep_hrg_energy.Play_WEP_HRG_Energy_1P_Shoot') WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'ww_wep_hrg_energy.Play_WEP_HRG_Energy_3P_ShootAlt', FirstPersonCue=AkEvent'ww_wep_hrg_energy.Play_WEP_HRG_Energy_1P_ShootAlt') WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_MedicPistol.Play_SA_MedicPistol_Handling_DryFire' WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_SA_MedicDart.Play_WEP_SA_Medic_Dart_DryFire' // Attachments bHasIronSights=true bHasFlashlight=false WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Damage1, Scale=1.15f), (Stat=EWUS_Weight, Add=1))) SecondaryFireAnimRateModifier = 2.0f; DefaultFireMaterialColor=(R=0.90f,G=0.26f,B=0.0f) AltFireMaterialColor=(R=0.7f,G=0.04f,B=0.9f) NumBloodMapMaterials=3 // bForceHandleImpacts=true; }