1
0
KF2-Dev-Scripts/KFGame/Classes/KFSM_TentacleGrappleBase.uc

370 lines
10 KiB
Ucode
Raw Normal View History

2020-12-13 15:01:13 +00:00
//=============================================================================
// KFSM_TentacleGrappleBase
//=============================================================================
// Tentacle grapple
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
// - Andrew "Strago" Ladenberger
//=============================================================================
class KFSM_TentacleGrappleBase extends KFSM_GrappleCombined
native(SpecialMoves);
/** Time from move start when skel controls are activated */
var float TentacleStartTime;
/** Hit detection settings */
var float MaxRange;
var float MaxClawReach;
var float DetachDistance;
var bool bGrabMissed;
/** Skel controls */
var float CurveExponent;
var float TentacleEndBoneOffset;
var float TentacleBlendOutTime;
var name TentacleEndBone;
var name TentacleStartBone;
/** Air speed when in PHYS_Flying during retract */
var float RetractAirSpeed;
/** True if we've grabbed a pawn */
var bool bGrabbedPawn;
var const array<name> TentacleControlNames;
var transient array<SkelControlSingleBone> TentacleControls;
var const name TentacleStartCtrlName;
var transient SkelControlSingleBone TentacleStartCtrl;
var transient bool bTentacleCtrlStarted;
/** Damage values, set by AI command */
var int TentacleDamage;
var class<KFDamageType> TentacleDmgType;
cpptext
{
virtual void TickSpecialMove(FLOAT DeltaTime);
}
function SpecialMoveStarted(bool bForced, Name PrevMove)
{
local KFPawn_MonsterBoss BossPawn;
// skip default (instant attach) behavior
Super.SpecialMoveStarted(bForced, PrevMove);
bAlignFollowerLookSameDirAsMe = default.bAlignFollowerLookSameDirAsMe;
bAlignFollowerRotation = default.bAlignFollowerRotation;
Follower = KFPOwner.InteractionPawn;
if( Follower != none && !Follower.IsAliveAndWell() )
{
KFPOwner.EndSpecialMove();
return;
}
bAlignPawns = false;
bTentacleCtrlStarted = FALSE;
bGrabMissed = false;
BossPawn = KFPawn_MonsterBoss(KFPOwner);
if (BossPawn != none)
{
// On the server start a timer to check collision
if (KFPOwner.Role == ROLE_Authority)
{
// Stop cloaking
BossPawn.SetCloaked(false);
}
BossPawn.BumpFrequency = 0.f;
BossPawn.PlayGrabDialog();
BossPawn.SetTimer(TentacleStartTime, false, nameof(BeginTentacleControls), Self);
if (Follower != none)
{
DetachDistance = BossPawn.CylinderComponent.CollisionRadius + Follower.CylinderComponent.CollisionRadius + default.DetachDistance;
}
}
}
/** Activates the tentacle skel controls */
function BeginTentacleControls()
{
if ( TentacleControls.Length == 0 )
{
InitializeSkelControls();
}
SetSkelControlsActive(true);
bTentacleCtrlStarted = TRUE;
}
/** Test grapple collision on server */
function CheckGrapple()
{
local vector HitLocation, HitNormal, GrabLocation, GrabDirection;
local Actor HitActor;
if( bGrabMissed )
{
return;
}
if( Follower != none && Follower.IsAliveAndWell() && Follower.CanBeGrabbed(KFPOwner, true) )
{
// Out of reach (MaxRange + additional claw reach)
GrabLocation = Follower.Location + Follower.BaseEyeHeight * vect(0,0,0.75);
GrabDirection = PawnOwner.Location - GrabLocation;
if ( VSizeSq(GrabDirection) < Square(MaxRange + MaxClawReach) )
{
// trace for obstructions
HitActor = PawnOwner.Trace(HitLocation, HitNormal, Follower.Location, PawnOwner.Location, true);
if ( HitActor == None || HitActor == Follower )
{
// Do some damage
DamageFollower( GrabLocation, GrabDirection );
}
// Make sure our follower is still alive after taking damage
if( Follower != none && Follower.IsAliveAndWell() )
{
BeginGrapple();
return;
}
}
}
OnGrabMissed();
}
function DamageFollower( vector GrabLocation, vector GrabDirection )
{
GrabDirection = Normal(GrabDirection);
Follower.TakeDamage( TentacleDamage, KFPOwner.Controller, GrabLocation, GrabDirection, TentacleDmgType,, KFPOwner );
// Do a camera shake, etc
KFPawn_Monster(KFPOwner).MeleeAttackHelper.PlayMeleeHitEffects(Follower, GrabLocation, GrabDirection);
}
function BeginGrapple(optional KFPawn Victim)
{
if ( PawnOwner.Role == ROLE_Authority )
{
// @todo: Server only for now because the alignment code is not
// network safe on simulated proxy. Needs invstigation!
bAlignPawns = default.bAlignPawns;
// replicate attachment
KFPOwner.SpecialMoveFlags = EGS_GrabSuccess;
KFPOwner.ReplicatedSpecialMove.Flags = KFPOwner.SpecialMoveFlags;
// Set our physics to flying for the attract
Follower.SetPhysics( PHYS_Flying );
Follower.AirSpeed = RetractAirSpeed;
KFPawn_MonsterBoss(KFPOwner).PlayGrabbedPlayerDialog( KFPawn_Human(Follower) );
}
// Set up a safety net in case interaction cannot be started
PawnOwner.SetTimer( InteractionStartTimeOut, FALSE, nameof(self.InteractionStartTimedOut), self );
// See if we can start interaction right now. If we can't, keep trying until we can.
CheckReadyToStartInteraction();
}
/** StartInteraction */
function StartInteraction()
{
super.StartInteraction();
bGrabbedPawn = true;
}
event OnGrabMissed()
{
bGrabMissed = true;
if ( PawnOwner.Role == ROLE_Authority )
{
KFPOwner.SpecialMoveFlags = EGS_GrabMiss;
KFPOwner.ReplicatedSpecialMove.Flags = KFPOwner.SpecialMoveFlags;
}
bTentacleCtrlStarted = false;
SetSkelControlsActive(false);
}
/** Toggle attachment */
function SpecialMoveFlagsUpdated()
{
if ( KFPOwner.SpecialMoveFlags == EGS_GrabSuccess )
{
BeginGrapple();
}
else if ( KFPOwner.SpecialMoveFlags == EGS_GrabMiss )
{
OnGrabMissed();
}
}
function InitializeSkelControls()
{
local int i;
local SkelControlSingleBone SkelCtrl;
if ( PawnOwner.WorldInfo.NetMode == NM_DedicatedServer )
{
return;
}
// @note: Could be done with a single (shared) control with a relative
// translation, but this way we can make a nice curve to the target.
for (i = 0; i < TentacleControlNames.length; ++i)
{
SkelCtrl = SkelControlSingleBone(PawnOwner.Mesh.FindSkelControl(TentacleControlNames[i]));
SkelCtrl.BlendInTime = (GrabCheckTime - TentacleStartTime);
SkelCtrl.BlendOutTime = TentacleBlendOutTime;
TentacleControls.AddItem(SkelCtrl);
}
// move the anchor bone outside the abdomen
TentacleStartCtrl = SkelControlSingleBone(PawnOwner.Mesh.FindSkelControl(TentacleStartCtrlName));
TentacleStartCtrl.BlendInTime = 0.2f;
TentacleStartCtrl.BlendOutTime = 0.33f;
}
function SetSkelControlsActive(bool bEnabled)
{
local int i;
if ( PawnOwner.WorldInfo.NetMode == NM_DedicatedServer )
{
return;
}
// bEnabled==TRUE is handled in TickSpecialMove to guarantee a valid target
if ( !bEnabled )
{
for(i = 0; i < TentacleControls.Length; ++i)
{
TentacleControls[i].SetSkelControlActive(false);
}
}
TentacleStartCtrl.SetSkelControlActive(bEnabled);
}
/** If the follower has been reeled in, end the special move */
event DetachGrabbedPawn()
{
local int i;
// bEnabled==TRUE is handled in TickSpecialMove to guarantee a valid target
for(i = 0; i < TentacleControls.Length; ++i)
{
if( TentacleControls[i] != none )
{
TentacleControls[i].BlendOutTime = 0.2f;
}
}
if( TentacleStartCtrl != none )
{
TentacleStartCtrl.BlendOutTime = 0.2f;
}
bTentacleCtrlStarted = false;
SetSkelControlsActive( false );
KFPOwner.EndSpecialMove();
if( Follower != none )
{
Follower.SetPhysics( PHYS_Falling );
Follower.EndSpecialMove();
}
}
/** Skip super, this class does not have a looping anim */
function AnimEndNotify( AnimNodeSequence SeqNode, float PlayedTime, float ExcessTime )
{
Super(KFSpecialMove).AnimEndNotify(SeqNode, PlayedTime, ExcessTime);
}
/** Disable grab interruption */
function NotifyOwnerTakeHit(class<KFDamageType> DamageType, vector HitLoc, vector HitDir, Controller InstigatedBy);
/** Follower has left special move */
function OnFollowerLeavingSpecialMove()
{
super.OnFollowerLeavingSpecialMove();
ResetFollowerPhysics();
}
/** Resets physics values on follower */
function ResetFollowerPhysics()
{
if( Follower != none )
{
Follower.AirSpeed = Follower.default.AirSpeed;
if( Follower.Physics == PHYS_Flying )
{
Follower.SetPhysics( PHYS_Falling );
}
}
}
/** Disable skel controls */
function SpecialMoveEnded(Name PrevMove, Name NextMove)
{
if( PawnOwner != none )
{
PawnOwner.ClearTimer(nameof(CheckGrapple), Self);
PawnOwner.ClearTimer(nameof(BeginTentacleControls), Self);
KFPawn_Monster(PawnOwner).BumpFrequency = KFPawn_Monster(PawnOwner).default.BumpFrequency;
}
// Return follower physics to normal
ResetFollowerPhysics();
SetSkelControlsActive(false);
Super.SpecialMoveEnded(PrevMove, NextMove);
}
event vector GetSourceLocation()
{
return PawnOwner.Location;
}
/** Ignore input */
function SpecialMoveButtonRetriggered() {}
function SpecialMoveButtonReleased() {}
function PlayerReleasedGrapple() {}
DefaultProperties
{
Handle=KFSM_TentacleGrappleBase
FollowerSpecialMove=SM_HansGrappleVictim
GrabStartAnimName=Atk_Tentical_V1
AlignDistance=108.f
AlignFollowerInterpSpeed=22.f
bStopAlignFollowerRotationAtGoal=false
bAlignFollowerZ=true
bAlignLeaderLocation=false
bRetryCollisionCheck=false
AlignSpeedModifier=0.2f
TentacleStartTime=0.83f
MaxRange=600.f
MaxClawReach=50.f
DetachDistance=20.f
RetractAirSpeed=1000.f
TentacleStartBone=FrontTentacle2
TentacleEndBone=FrontTentacle7
TentacleEndBoneOffset=-10
TentacleBlendOutTime=0.33f
CurveExponent=1.25f
}