1
0
KF2-Dev-Scripts/KFGameContent/Classes/KFSM_Patriarch_MinigunBarrage.uc
2020-12-13 18:01:13 +03:00

449 lines
12 KiB
Ucode

//=============================================================================
// KFSM_Patriarch_MinigunBarrage
//=============================================================================
// Patriarch's minigun barrage attack
//=============================================================================
// Killing Floor 2
// Copyright (C) 2016 Tripwire Interactive LLC
//=============================================================================
class KFSM_Patriarch_MinigunBarrage extends KFSM_PlaySingleAnim;
/** Cached reference to Patriarch pawn */
var KFPawn_ZedPatriarch MyPatPawn;
/** Cached reference to Patriarch AI controller */
var KFAIController_ZedPatriarch MyPatController;
/** Name of the aim offset profile to use with this special move */
var Name AimOffsetProfileName;
/** Wind Up animation name */
var Name WindUpAnimName;
/** Wind Down animation name */
var Name WindDownAnimName;
/** Alternate fan attack animation names */
var array<Name> FanAnimNames;
/** Set according to special move flags */
var bool bIsFanFire;
/** Rotation rate to use when focus firing */
var rotator FocusFireRotationRate;
/** Minigun sounds */
var AkEvent MinigunLoop;
var AkEvent MinigunLoopEnd;
/** Time to check before firing if an enemy is still visible */
var float VisibilityCheckTime;
/** Set to true if AI command interrupted move early */
var bool bInterrupted;
/** Set to true if fire LOS was obstructed mid-move */
var bool bObstructed;
/** Pack the animation type we'll be using (fan vs. focus fire) */
static function byte PackSMFlags( bool bIsFanFireAttack )
{
return ( bIsFanFireAttack ? 1 : 0 ) + ( Rand(default.FanAnimNames.Length) << 4 );
}
/** Notification called when Special Move starts */
function SpecialMoveStarted( bool bForced, Name PrevMove )
{
local byte Type, Variant;
MyPatPawn = KFPawn_ZedPatriarch( KFPOwner );
// Set anim type by flag
Type = MyPatPawn.SpecialMoveFlags & 15; //0x0f
Variant = MyPatPawn.SpecialMoveFlags >> 4;
// Set the anim according to the type of barrage we'll be doing
if( Type > 0 )
{
bDisableMovement = true;
MyPatPawn.bSprayingFire = true;
bIsFanFire = true;
AnimName = FanAnimNames[Variant];
}
else
{
bDisableMovement = !MyPatPawn.CanMoveWhenMinigunning();
MyPatPawn.bSprayingFire = false;
if( MyPatPawn.Role == ROLE_Authority )
{
MyPatPawn.SetGunTracking( true );
}
AnimName = default.AnimName;
}
Super.SpecialMoveStarted( bForced,PrevMove );
MyPatPawn.SetCloaked( false );
MyPatPawn.SetSprinting( false );
MyPatPawn.ZeroMovementVariables();
MyPatPawn.PlayMinigunWarnDialog();
MyPatController = KFAIController_ZedPatriarch( MyPatPawn.Controller );
PlayWindUpAnimation();
MyPatPawn.SetAimOffsetNodesProfile( AimOffsetProfileName );
MyPatPawn.bEnableAimOffset = true;
if( MyPatPawn.Role == ROLE_Authority && !MyPatPawn.IsHumanControlled() )
{
MyPatPawn.SetTimer( VisibilityCheckTime, false, nameOf(Timer_CheckEnemyLOS), self );
}
bObstructed = false;
bInterrupted = false;
}
/** Returns anim stance based on whether we're allowed to move when minigunning or not */
function EAnimSlotStance GetAnimStance()
{
return bDisableMovement ? EAS_FullBody : EAS_UpperBody;
}
/** Overridden to do nothing */
function PlayAnimation() {}
/** Plays our wind up anim, starts the barrel spin skel controller */
function PlayWindUpAnimation()
{
if( MyPatPawn.bSprayingFire )
{
bUseRootMotion = true;
AnimStance = EAS_FullBody;
EnableRootMotion();
}
else
{
AnimStance = EAS_UpperBody;
}
PlaySpecialMoveAnim( WindUpAnimName, AnimStance, BlendInTime, BlendOutTime, 1.f );
MyPatPawn.SpinMinigunBarrels( true );
// Play our looping minigun sound
MyPatPawn.PostAkEventOnBone( MinigunLoop, 'BarrelSpinner', true, true );
}
/** Make sure we have a clear path to enemy */
function Timer_CheckEnemyLOS()
{
local vector StartTrace, EndTrace;
if( MyPatPawn != none && MyPatPawn.Controller != none )
{
MyPatPawn.Mesh.GetSocketWorldLocationAndRotation( 'MissileCenter', StartTrace );
EndTrace = StartTrace + (Normal(MyPatPawn.Controller.Enemy.Location - StartTrace) * 300.f);
if( !MyPatPawn.FastTrace(EndTrace, StartTrace,, true) )
{
MyPatPawn.DoSpecialMove( SM_HoseWeaponAttack, true,, 128 );
}
}
}
/** Play the wind down if we have to */
function SpecialMoveFlagsUpdated()
{
switch( MyPatPawn.SpecialMoveFlags )
{
// No more valid targets
case 64:
bInterrupted = true;
MyPatPawn.StopBodyAnim( AnimStance, 0.1f );
PlayWindDownAnim();
break;
// Obstructed
case 128:
bObstructed = true;
MyPatPawn.StopBodyAnim( AnimStance, 0.1f );
PlayWindDownAnim();
break;
}
}
/** Plays our fire animation, starts weapon fire */
function PlayFireAnim()
{
if( bInterrupted || bObstructed )
{
return;
}
// Play dialog
MyPatPawn.PlayMinigunAttackDialog();
if( bIsFanFire )
{
MyPatPawn.bDisableTurnInPlace = true;
bUseRootMotion = true;
AnimStance = EAS_FullBody;
EnableRootMotion();
PlaySpecialMoveAnim( AnimName, AnimStance, 0.1f, 0.2f );
}
else
{
MyPatPawn.RotationRate = FocusFireRotationRate;
MyPatPawn.bDisableTurnInPlace = false;
bUseRootMotion = false;
AnimStance = EAS_UpperBody;
DisableRootMotion();
PlaySpecialMoveAnim( AnimName, AnimStance, BlendInTime, BlendOutTime, 1.f );
}
// Zero movement
MyPatPawn.ZeroMovementVariables();
// Start firing
if( MyPatPawn.Role == ROLE_Authority || MyPatPawn.IsLocallyControlled() )
{
MyPatPawn.Weapon.StartFire( 0 );
}
// Set a timer
if( MyPatPawn.Role == ROLE_Authority && !bIsFanFire && !MyPatPawn.IsHumanControlled() )
{
MyPatPawn.SetTimer( 0.1f, true, nameOf(Timer_CheckIfFireAllowed), self );
MyPatPawn.SetTimer( 2.0f, true, nameOf(Timer_SearchForMinigunTargets), self );
}
}
/** Starts and stops minigun fire depending on whether pawn is directly facing enemy */
function Timer_CheckIfFireAllowed()
{
local KFPawn KFP;
local vector PawnDir, Projection, OtherProjection, PawnRot2D;
local float DistSQ;
if( MyPatController.Enemy == none
|| !MyPatController.Enemy.IsAliveAndWell()
|| !MyPatPawn.FastTrace(MyPatController.Enemy.Location, MyPatPawn.Location,, true) )
{
Timer_SearchForMinigunTargets();
}
// If for whatever reason we have no enemy, or enemy is dead, stop firing
if( MyPatController.Enemy == none || !MyPatController.Enemy.IsAliveAndWell() )
{
MyPatPawn.DoSpecialMove( SM_HoseWeaponAttack, true,, 64 );
return;
}
PawnDir = vector( MyPatPawn.Rotation );
Projection = MyPatController.Enemy.Location - MyPatPawn.Location;
DistSQ = VSizeSQ( Projection );
// Search for enemies obstructing us and our enemy
foreach MyPatPawn.WorldInfo.AllPawns( class'KFPawn', KFP )
{
OtherProjection = KFP.Location - MyPatPawn.Location;
if( KFP != MyPatController.Enemy
&& KFP.IsAliveAndWell()
&& KFP.GetTeamNum() != MyPatPawn.GetTeamNum()
&& VSizeSQ(OtherProjection) < DistSQ
&& PawnDir dot Normal(OtherProjection) >= 0.8f
&& MyPatPawn.FastTrace(KFP.Location, MyPatPawn.Location,, true)
&& KFP.CanAITargetThisPawn(MyPatController))
{
MyPatController.ChangeEnemy( KFP, false );
MyPatPawn.SetTimer( 2.0f, true, nameOf(Timer_SearchForMinigunTargets), self );
MyPatPawn.SetGunTracking( true );
if( !MyPatPawn.IsFiring() )
{
MyPatPawn.Weapon.StartFire( 0 );
}
return;
}
}
if( MyPatPawn.Controller != none && MyPatController.Enemy != none )
{
PawnRot2D = PawnDir;
PawnRot2D.Z = 0.f;
if( PawnRot2D dot Normal2D(Projection) >= 0.5f )
{
if( !MyPatPawn.IsFiring() )
{
MyPatPawn.Weapon.StartFire( 0 );
}
return;
}
}
if( MyPatPawn.IsFiring() )
{
MyPatPawn.StopFiring();
}
}
/** Searches for new minigun targets */
function Timer_SearchForMinigunTargets()
{
if( bIsFanFire || MyPatController == none )
{
return;
}
// Try to find a new enemy
if( MyPatController.CheckForEnemiesInFOV(4000.f, 0.25f, 1.f, true, false) != none )
{
MyPatPawn.SetGunTracking( true );
return;
}
// If we have no valid enemy, or no valid LOS to enemy, end barrage
if ( MyPatController.Enemy == none || !MyPatPawn.FastTrace(MyPatController.Enemy.Location, MyPatPawn.Location,, true) )
{
MyPatPawn.DoSpecialMove( SM_HoseWeaponAttack, true,, 64 );
}
}
/** Plays our wind down animation, stops firing, disables barrel spin skel controller */
function PlayWindDownAnim()
{
if( bObstructed )
{
MyPatPawn.StopBodyAnim( AnimStance, 0.33f );
}
// Clear our fire rotation check timer
MyPatPawn.ClearTimer( nameOf(Timer_CheckIfFireAllowed), self );
// Clear our target switching timer
MyPatPawn.ClearTimer( nameOf(Timer_SearchForMinigunTargets), self );
// Sync weapon state
if( MyPatPawn.Weapon != none && !MyPatPawn.Weapon.IsInState('Active') )
{
MyPatPawn.StopFiring();
MyPatPawn.Weapon.GotoState('Active');
}
// Zero movement
MyPatPawn.ZeroMovementVariables();
if( bDisableMovement )
{
bUseRootMotion = true;
AnimStance = EAS_FullBody;
EnableRootMotion();
}
MyPatPawn.SpinMinigunBarrels( false );
MyPatPawn.SetGunTracking( false );
AnimStance = GetAnimStance();
PlaySpecialMoveAnim( WindDownAnimName, AnimStance, BlendInTime, BlendOutTime, 1.f );
// Play our minigun loop end sound
MyPatPawn.PostAkEventOnBone( MinigunLoopEnd, 'BarrelSpinner', true, true );
}
/** Plays subsequent animations in the barrage */
function AnimEndNotify(AnimNodeSequence SeqNode, float PlayedTime, float ExcessTime)
{
switch( DeferredSeqName )
{
case WindUpAnimName:
PlayFireAnim();
break;
case AnimName:
PlayWindDownAnim();
break;
case WindDownAnimName:
KFPOwner.EndSpecialMove();
break;
}
}
function SpecialMoveEnded( Name PrevMove, Name NextMove )
{
if( MyPatPawn != none )
{
if( !bObstructed )
{
MyPatPawn.StartWeaponCooldown();
}
// Stop body anims if this move was interrupted
if( MyPatPawn.IsFiring() )
{
// If still playing an upperbody or fullbody animation allow it to be interrupted
if( KFPOwner.BodyStanceNodes[AnimStance].bIsPlayingCustomAnim )
{
KFPOwner.StopBodyAnim( AnimStance, AbortBlendOutTime );
}
}
// Sync weapon state
if( MyPatPawn.Weapon != none && !MyPatPawn.Weapon.IsInState('Active') )
{
MyPatPawn.StopFiring();
MyPatPawn.Weapon.GotoState('Active');
}
// Play our minigun loop end sound
if( MyPatPawn.bSpinBarrels )
{
MyPatPawn.PostAkEventOnBone( MinigunLoopEnd, 'BarrelSpinner', true, true );
}
MyPatPawn.bEnableAimOffset = false;
MyPatPawn.SetDefaultAimOffsetNodesProfile();
MyPatPawn.ClearTimer( nameOf(Timer_SearchForMinigunTargets), self );
MyPatPawn.ClearTimer( nameOf(Timer_CheckIfFireAllowed), self );
MyPatPawn.ClearTimer( nameOf(Timer_CheckEnemyLOS), self );
MyPatPawn.SetGunTracking( false );
MyPatPawn.SpinMinigunBarrels( false );
MyPatPawn.bSprayingFire = false;
MyPatPawn.RotationRate = MyPatPawn.default.RotationRate;
MyPatPawn.bDisableTurnInPlace = MyPatPawn.default.bDisableTurnInPlace;
MyPatPawn = none;
}
super.SpecialMoveEnded( PrevMove, NextMove );
}
DefaultProperties
{
// SpecialMove
Handle=KFSM_Patriarch_MinigunBarrage
bDisableSteering=false
bDisableMovement=true
bCanBeInterrupted=false
bAllowFireAnims=true
FocusFireRotationRate=(Pitch=30000,Yaw=30000,Roll=30000)
VisibilityCheckTime=1.0f
bShouldDeferToPostTick=true
// Sound
MinigunLoop=AkEvent'WW_ZED_Patriarch.Play_Mini_Gun_Start'
MinigunLoopEnd=AkEvent'WW_ZED_Patriarch.Play_Mini_Gun_Stop'
// Animation
WindUpAnimName=Gun_TO_Load
WindDownAnimName=Gun_TO_Idle
AnimName=Gun_Idle
FanAnimNames(0)=Gun_Shoot_Fan_V1
FanAnimNames(1)=Gun_Shoot_Fan_V3
AimOffsetProfileName=Minigun
BlendInTime=0.15f
BlendOutTime=0.1f
AbortBlendOutTime=0.1f
}