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

789 lines
21 KiB
Ucode
Raw Normal View History

2020-12-13 15:01:13 +00:00
//=============================================================================
// KFWeap_Healer_Syringe
//=============================================================================
// Weapon class used for the healer
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
// - Andrew "Strago" Ladenberger
//=============================================================================
class KFWeap_Welder extends KFWeapon
hidedropdown;
/** If set, automatically equip the previous weapon when leaving a door trigger */
var() bool bAutoUnequip;
/*********************************************************************************************
@name Firing
********************************************************************************************* */
/** Maximum range to find Door */
var() float WeldingRange;
/** How many points to add to a door's weld integrity per use (use rate determined by FireInterval) */
var() float FastenRate;
/** How many points to subtract from a door's weld integrity per use (use rate determined by FireInterval) */
var() float UnFastenRate;
/** Percent repaired per fire interval */
var() float RepairRate;
/** Give the welder extra range when it has a WeldTarget to avoid the ready animation
activating / deactivating on door that is is currently being damaged */
var const float ExtraWeldingRange;
/** How long (in seconds) it takes to recharge ammo by 1 unit */
var() float AmmoRechargeRate;
/*********************************************************************************************
@name Animations
********************************************************************************************* */
/** animation names */
var name IdleWeldAnim;
var name WeldOpenAnim;
var name WeldCloseAnim;
/** Reference to the door we're pointing at */
var KFWeldableComponent WeldTarget;
/** Store last update to we can prevent idle->ready->idle to quickly */
var float LastTraceHitTime;
/*********************************************************************************************
@name Screen
********************************************************************************************* */
var class<KFGFxWorld_WelderScreen> ScreenUIClass;
var KFGFxWorld_WelderScreen ScreenUI;
simulated event PreBeginPlay()
{
local KFGameEngine KFGEngine;
Super.PreBeginPlay();
KFGEngine = KFGameEngine( Class'KFGameEngine'.static.GetEngine() );
if(KFGEngine != none)
{
SetShownInInventory(KFGEngine.bShowWelderInInv);
}
}
simulated function SetShownInInventory(bool bValue)
{
InventoryGroup= bValue ? IG_Equipment : IG_None ;
bAutoUnequip = !bValue;
}
/** Turn on the UI screen when we equip the healer */
simulated function AttachWeaponTo( SkeletalMeshComponent MeshCpnt, optional Name SocketName )
{
super.AttachWeaponTo( MeshCpnt, SocketName );
if( Instigator != none && Instigator.IsLocallyControlled() )
{
// Create the screen's UI piece
if (ScreenUI == none)
{
ScreenUI = new( self ) ScreenUIClass;
ScreenUI.Init();
ScreenUI.Start(true);
}
if ( ScreenUI != none)
{
ScreenUI.SetPause(false);
ScreenUI.SetCharge( AmmoCount[0] );
ScreenUI.SetIntegrity( 255 );
}
}
}
/** Turn off the UI screen when we unequip the healer */
simulated function DetachWeapon()
{
super.DetachWeapon();
if ( Instigator != none && Instigator.IsLocallyControlled() && ScreenUI != none )
{
ScreenUI.SetPause();
}
}
/** @see Weapon::Tick */
simulated event Tick(float DeltaTime)
{
Super.Tick(DeltaTime);
UpdateScreenUI();
}
simulated event Destroyed()
{
if ( Instigator != none && Instigator.IsLocallyControlled() && ScreenUI != none)
{
ScreenUI.Close();
}
super.Destroyed();
}
/** Only update the screen screen if we have the welder equipped and it's screen values have changed */
simulated function UpdateScreenUI()
{
local float WeldPercentageFloat;
local byte WeldPercentage;
if ( Instigator != none && Instigator.IsLocallyControlled() && Instigator.Weapon == self )
{
if ( ScreenUI != none )
{
// Check if our current ammo reading has changed
if ( ScreenUI.CurrentCharge != AmmoCount[0] )
{
ScreenUI.SetCharge( AmmoCount[0] );
}
if ( WeldTarget != none )
{
// Address rounding errors in UI
WeldPercentageFloat = WeldTarget.GetWeldPct();
if( WeldPercentageFloat < 1.f && WeldPercentageFloat > 0.f )
{
WeldPercentageFloat = 1.f;
}
else if( WeldPercentageFloat > 99.f && WeldPercentageFloat < 100.f )
{
WeldPercentageFloat = 99.f;
}
WeldPercentage = byte( WeldPercentageFloat );
// Check if our weld integrity has changed
if ( WeldPercentage != ScreenUI.IntegrityPercentage )
{
ScreenUI.SetIntegrity( WeldPercentage );
}
}
// Remove our weld value
else if ( ScreenUI.IntegrityPercentage != 255 )
{
ScreenUI.SetIntegrity( 255 );
}
}
}
}
/*********************************************************************************************
* @name Ammunition
*********************************************************************************************/
simulated function bool HasAnyAmmo()
{
return true;
}
simulated function bool HasAmmo( byte FireModeNum, optional int Amount )
{
if ( FireModeNum == DEFAULT_FIREMODE || FireModeNum == ALTFIRE_FIREMODE )
{
if ( AmmoCount[0] >= AmmoCost[FireModeNum])
{
// Requires a valid WeldTarget (see ServerSetWeldTarget)
return ( WeldTarget != None && CanWeldTarget(FireModeNum) );
}
return false;
}
return Super.HasAmmo(FireModeNum, Amount);
}
/** Returns true if weapon should be auto-reloaded */
simulated function bool ShouldAutoReload(byte FireModeNum)
{
return false;
}
/*********************************************************************************************
@name Firing / Projectile
********************************************************************************************* */
/** Unweld with RM/L2 */
simulated function SetIronSights(bool bNewIronSights)
{
if ( !Instigator.IsLocallyControlled() )
{
return;
}
if ( bNewIronSights )
{
StartFire(ALTFIRE_FIREMODE);
}
else
{
StopFire(ALTFIRE_FIREMODE);
}
}
/** Also unweld with MM/R1 */
simulated function AltFireMode()
{
if ( !Instigator.IsLocallyControlled() )
{
return;
}
// StartFire - StopFire called from KFPlayerInput
StartFire(ALTFIRE_FIREMODE);
}
/**
* @see Weapon::StartFire
*/
simulated function StartFire(byte FireModeNum)
{
// Notify server of the weld target we plan to use
if ( FireModeNum == DEFAULT_FIREMODE || FireModeNum == ALTFIRE_FIREMODE )
{
if ( Role < ROLE_Authority )
{
ServerSetWeldTarget(WeldTarget, false);
}
}
Super.StartFire(FireModeNum);
}
/**
* If the weapon isn't an instant hit, or a simple projectile, it should use the type EWFT_Custom. In those cases
* this function will be called. It should be subclassed by the custom weapon.
*/
simulated function CustomFire()
{
local float CurrentFastenRate, CurrentUnfastenRate;
WeldTarget = TraceWeldables();
// Fasten/Unfasten the door
if ( Role == ROLE_Authority )
{
if( !IsTimerActive(nameof(RechargeAmmo)) )
{
SetTimer(AmmoRechargeRate, true, nameof(RechargeAmmo));
}
if(WeldTarget != None)
{
CurrentFastenRate = FastenRate;
CurrentUnfastenRate = UnFastenRate;
GetPerk().ModifyWeldingRate(CurrentFastenRate, CurrentUnfastenRate);
if ( WeldTarget.bIsDestroyed && !WeldTarget.Owner.IsA('KFRepairableActor') )
{
WeldTarget.Repair(RepairRate, KFPawn(Instigator));
}
else if ( CurrentFireMode == DEFAULT_FIREMODE )
{
WeldTarget.Weld(CurrentFastenRate, KFPawn(Instigator));
}
else
{
WeldTarget.Weld(CurrentUnfastenRate, KFPawn(Instigator));
}
}
}
// On the local player check to see if we should stop firing
// It makes sense to do this in ShouldRefire(), but there is no guarantee
// that it will sync on server/client. So, use StopFire() instead.
if ( Instigator.IsLocallyControlled() )
{
if ( WeldTarget == none || !CanWeldTarget() )
{
// TargetDoor is no longer valid
StopFire(CurrentFireMode);
}
}
}
simulated function bool CanWeldTarget( optional int FireModeNum=CurrentFireMode )
{
local KFPerk WelderPerk;
WelderPerk = GetPerk();
if ( FireModeNum == DEFAULT_FIREMODE && !WeldTarget.CanBeWelded() )
{
if( WelderPerk != none && WelderPerk.CanExplosiveWeld() )
{
return WeldTarget.CanBeDemoWelded();
}
return false;
}
else if ( FireModeNum == ALTFIRE_FIREMODE && !WeldTarget.CanBeUnwelded() )
{
return false;
}
return true;
}
/** Ammo regen timer */
function RechargeAmmo()
{
if ( AmmoCount[0] < MagazineCapacity[0] )
{
AmmoCount[0]++;
// If we are holding down the mouse when we regen enough ammo, fire again
if ( AmmoCount[0] == AmmoCost[0] )
{
Refire();
if ( !Instigator.IsLocallyControlled() )
{
ClientRefire();
}
}
}
else
{
ClearTimer(nameof(RechargeAmmo));
}
}
/** If we are holding down the mouse when we regen enough ammo, fire again */
simulated function Refire()
{
local byte i;
// if either of the fire modes are pending, perform them
for( i=0; i<=ALTFIRE_FIREMODE; i++ )
{
if( PendingFire(i) )
{
SendToFiringState(i);
break;
}
}
}
/** RechargeAmmo is called on authority only. When we have enough ammo to fire again
* we want to make sure that the client plays his animations and fire effects */
reliable client function ClientRefire()
{
Refire();
}
/** If we recieve a valid door target after the fire key was already pressed down */
simulated function CheckDelayedStartFire()
{
local bool bNotifyServer;
if ( WeldTarget != None )
{
if( PendingFire(DEFAULT_FIREMODE) )
{
BeginFire(DEFAULT_FIREMODE);
bNotifyServer = true;
}
else if( PendingFire(ALTFIRE_FIREMODE) )
{
BeginFire(ALTFIRE_FIREMODE);
bNotifyServer = true;
}
if ( bNotifyServer && Role < ROLE_Authority )
{
ServerSetWeldTarget(WeldTarget, true);
}
}
}
/*********************************************************************************************
@name Animation
********************************************************************************************* */
/*********************************************************************************************
@name WeldTarget
********************************************************************************************* */
/**
* Find doors within welding range
* Network: Local Player
*/
simulated function bool TickWeldTarget()
{
local KFWeldableComponent PreviousTarget;
// prevent state from changing too often (also a optimization)
if ( `TimeSince(LastTraceHitTime) < 0.2f )
{
return false;
}
PreviousTarget = WeldTarget;
WeldTarget = TraceWeldables();
// refresh idle anim if TargetDoor has changed
if ( PreviousTarget != WeldTarget )
{
return PlayReadyTransition(PreviousTarget);
}
return false;
}
/** Network: All */
simulated function KFWeldableComponent TraceWeldables()
{
local KFWeldableComponent WeldableComponent;
local vector HitLoc, HitNorm, StartTrace, EndTrace, AdjustedAim;
local bool bIsRepairableActor;
local KFDoorActor WeldableDoor;
// define range to use for CalcWeaponFire()
StartTrace = Instigator.GetWeaponStartTraceLocation();
AdjustedAim = vector(GetAdjustedAim(StartTrace));
EndTrace = StartTrace + AdjustedAim * WeldingRange;
// Give the welder extra range when it has a WeldTarget to avoid the ready animation
// activating / deactivating on door that is is currently being damaged
if( WeldTarget != none )
{
EndTrace += AdjustedAim * ExtraWeldingRange;
}
// find weldable (trace against basic cylinder)
foreach GetTraceOwner().TraceActors(class'KFWeldableComponent', WeldableComponent, HitLoc, HitNorm, EndTrace, StartTrace)
{
if (WeldableComponent.Owner == none)
{
// should never happen, right?
continue;
}
// trace against owner for more accuracy
if (Trace(HitLoc, HitNorm, EndTrace, StartTrace, true) != WeldableComponent.Owner)
{
continue;
}
bIsRepairableActor = WeldableComponent.Owner.IsA('KFRepairableActor');
if (!bIsRepairableActor && WeldableComponent.bIsDestroyed )
{
continue;
}
if (bIsRepairableActor && !WeldableComponent.CanBeWelded())
{
continue;
}
LastTraceHitTime = WorldInfo.TimeSeconds;
return WeldableComponent;
}
WeldableDoor = FindRepairableDoor();
return WeldableDoor != none ? WeldableDoor.WeldableComponent : none;
}
/** Try to find a nearby destroyed door. Can't use TraceActors because the door geometry is gone! */
simulated function KFDoorActor FindRepairableDoor()
{
local KFDoorTrigger DoorTrigger;
local KFInterface_Usable UsableTrigger;
local float FacingDot;
local vector Dir2d;
local KFPlayerController KFPC;
// On local player we can detect the interaction message and early out for perf
// On the server this is only called when firing (instead of on tick) so it's not as bad,
// but if needed we could add a LastInteractionMessage var to UpdateInteractionMessages()
if ( Instigator.IsLocallyControlled() )
{
KFPC = KFPlayerController(Instigator.Controller);
if ( KFPC.MyGFxHUD.CurrentInteractionIndex != IMT_RepairDoor )
{
return None;
}
}
UsableTrigger = class'KFPlayerController'.static.GetCurrentUsableActor(Instigator);
DoorTrigger = KFDoorTrigger(UsableTrigger);
// if we didn't find a door that could be welded, maybe there is one that can be repaired?
if ( DoorTrigger != None && DoorTrigger.DoorActor != None && DoorTrigger.DoorActor.bIsDestroyed )
{
// zero Z to give us a 2d dot product
Dir2d = Normal2d(DoorTrigger.DoorActor.Location - Instigator.Location);
FacingDot = vector(Instigator.Rotation) dot (Dir2d);
if ( FacingDot > 0.87 )
{
return DoorTrigger.DoorActor;
}
}
return None;
}
/** Notify server of new WeldTarget for 'HasAmmo' */
reliable server private function ServerSetWeldTarget(KFWeldableComponent NewTarget, bool bDelayedStart)
{
WeldTarget = NewTarget;
if ( bDelayedStart )
{
CheckDelayedStartFire();
}
}
/** Play a transition animation between idle ready states */
simulated function bool PlayReadyTransition(KFWeldableComponent PreviousTarget)
{
local name AnimName;
local float Duration;
if( WeldTarget != None )
{
AnimName = WeldOpenAnim;
// set timer to begin firing if PendingFire is already set
if ( PreviousTarget == None )
{
Duration = MySkelMesh.GetAnimLength(AnimName);
SetTimer(FMax(Duration - 0.2f, 0.01f), false, nameof(CheckDelayedStartFire));
}
}
else if( PreviousTarget != None )
{
AnimName = WeldCloseAnim;
}
if ( AnimName != '' )
{
PlayAnimation(AnimName);
return true;
}
return false;
}
/*********************************************************************************************
* state Inactive
* This state is the default state. It needs to make sure Zooming is reset when entering/leaving
*********************************************************************************************/
auto state Inactive
{
simulated function BeginState(name PreviousStateName)
{
Super.BeginState(PreviousStateName);
WeldTarget = none;
}
}
/*********************************************************************************************
* 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 event Tick(float DeltaTime)
{
// Caution - Super will skip our global, but global will skip super's state function!
Global.Tick(DeltaTime);
if ( Instigator != none && Instigator.IsLocallyControlled() )
{
// local player - find nearbydoors
TickWeldTarget(); // will trace each call, but it's decently fast (zero-extent)
UpdateScreenUI();
if (bAutoUnequip)
{
TickAutoUnequip();
}
}
}
simulated event OnAnimEnd(AnimNodeSequence SeqNode, float PlayedTime, float ExcessTime)
{
local bool bPlayingAnim;
if ( Instigator != none && Instigator.IsLocallyControlled() )
{
// Update target immediately and enable tick (first time)
bPlayingAnim = TickWeldTarget();
// if animation didn't play in UpdateWeldTarget, play idle normally
if ( !bPlayingAnim )
{
PlayIdleAnim();
}
}
}
simulated function PlayIdleAnim()
{
local int IdleIndex;
if ( Instigator != none && Instigator.IsLocallyControlled() )
{
if( WeldTarget != None )
{
PlayAnimation(IdleWeldAnim, 0.0, true, 0.2);
}
else
{
IdleIndex = Rand(IdleAnims.Length);
PlayAnimation(IdleAnims[IdleIndex], 0.0, true, 0.2);
}
}
}
}
/** Automatically equip previous weapon when leaving a door trigger */
simulated function bool TickAutoUnequip()
{
local Trigger_PawnsOnly Trigger;
local KFInventoryManager KFIM;
ForEach Instigator.TouchingActors(class'Trigger_PawnsOnly', Trigger)
{
if(Trigger.IsA('KFDoorTrigger') || Trigger.IsA('KFRepairableActorTrigger') || Trigger.IsA('KFWeldableTrigger'))
{
return FALSE;
}
}
KFIM = KFInventoryManager( Instigator.InvManager );
if ( KFIM != None )
{
KFIM.SwitchToLastWeapon();
return TRUE;
}
return FALSE;
}
/*********************************************************************************************
* state WeaponWelding
* This is the default Firing State. It's performed on both the client and the server.
*********************************************************************************************/
simulated state WeaponWelding extends WeaponFiring
{
simulated function byte GetWeaponStateId()
{
return WEP_Weld;
}
simulated function BeginState(Name PrevStateName)
{
super.BeginState(PrevStateName);
NotifyBeginState();
}
simulated function EndState(Name NextStateName)
{
super.EndState(NextStateName);
NotifyEndState();
}
}
/*********************************************************************************************
* state WeaponSprinting
* When the pawn is sprinting, they need to check if they should auto unequip the welder.
*********************************************************************************************/
simulated state WeaponSprinting
{
simulated event Tick(float DeltaTime)
{
Global.Tick(DeltaTime);
if (Instigator != none && Instigator.IsLocallyControlled())
{
if (bAutoUnequip)
{
TickAutoUnequip();
}
}
}
}
defaultproperties
{
InventoryGroup=IG_None
bCanThrow=false
bDropOnDeath=false
bAutoUnequip=true
PlayerViewOffset=(X=20.0,Y=10,Z=-10)
FireTweenTime=0.2f
WeldingRange=150.f
ExtraWeldingRange=10
FastenRate=68.f
UnFastenRate=-110.f
RepairRate=0.03f //0.05f
IdleWeldAnim=Idle_Weld
WeldOpenAnim=Weld_On
WeldCloseAnim=Weld_Off
// Content
PackageKey="Welder"
FirstPersonMeshName="WEP_1P_Welder_MESH.Wep_1stP_Welder_Rig"
FirstPersonAnimSetNames(0)="WEP_1P_Welder_ANIM.Wep_1st_Welder_Anim"
AttachmentArchetypeName="WEP_Welder_ARCH.Welder_3P"
MuzzleFlashTemplateName="WEP_Welder_ARCH.Wep_Welder_MuzzleFlash"
// Aim Assist
AimCorrectionSize=0.f
bTargetAdhesionEnabled=false
// Ammo
MagazineCapacity[0]=100
SpareAmmoCapacity[0]=0
bInfiniteSpareAmmo=true
AmmoRechargeRate=0.08f
bAllowClientAmmoTracking=false
// Grouping
GroupPriority=5
WeaponSelectTexture=Texture2D'ui_weaponselect_tex.UI_WeaponSelect_Welder'
// Weld
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Electricity'
FiringStatesArray(DEFAULT_FIREMODE)=WeaponWelding
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_Custom
FireInterval(DEFAULT_FIREMODE)=+0.2
AmmoCost(DEFAULT_FIREMODE)=7
// Un-Weld
FireModeIconPaths(ALTFIRE_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Electricity'
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponWelding
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Custom
FireInterval(ALTFIRE_FIREMODE)=+0.2
AmmoCost(ALTFIRE_FIREMODE)=7
// Fire Effects
WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_SA_Welder.Play_WEP_SA_Welder_Fire_Loop_M', FirstPersonCue=AkEvent'WW_WEP_SA_Welder.Play_WEP_SA_Welder_Fire_Loop_S')
WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_SA_Welder.Play_WEP_SA_Welder_Fire_Loop_M', FirstPersonCue=AkEvent'WW_WEP_SA_Welder.Play_WEP_SA_Welder_Fire_Loop_S')
// BASH_FIREMODE
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_Welder'
InstantHitDamage(BASH_FIREMODE)=20
// Advanced (High RPM) Fire Effects
bLoopingFireAnim(DEFAULT_FIREMODE)=true
bLoopingFireAnim(ALTFIRE_FIREMODE)=true
bLoopingFireSnd(DEFAULT_FIREMODE)=true
bLoopingFireSnd(ALTFIRE_FIREMODE)=true
WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_SA_Welder.Stop_WEP_SA_Welder_Fire_Loop_M', FirstPersonCue=AkEvent'WW_WEP_SA_Welder.Stop_WEP_SA_Welder_Fire_Loop_S')
ScreenUIClass=class'KFGFxWorld_WelderScreen'
AssociatedPerkClasses(0)=none
bStorePreviouslyEquipped=false
}