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

361 lines
11 KiB
Ucode

//=============================================================================
// KFSM_FleshpoundKing_ChestBeam
//=============================================================================
// Fleshpound king's chest beam attach. Beam particle code from
// KFSM_Matriarch_PlasmaCannon
//=============================================================================
// Killing Floor 2
// Copyright (C) 2017 Tripwire Interactive LLC
// - Matt 'Squirrlz' Farber
//=============================================================================
class KFSM_FleshpoundKing_ChestBeam extends KFSM_PlaySingleAnim;
/** Cached king pawn owner */
var KFPawn_ZedFleshpoundKing KingPawnOwner;
/** Template for the plasma beam */
var const ParticleSystem BeamPSCTemplate;
/** PSC generated from the beam template. Gets recycled in and out of the emitter pool as needed. */
var ParticleSystemComponent BeamPSC;
/** Template for beam hit location FX */
var const ParticleSystem BeamHitPSCTemplate;
/** PSC for the hit location. On when the beam is hitting anything, off when missing everything */
var ParticleSystemComponent BeamHitPSC;
/** AK Event for start of beam charge */
var AkEvent BeamStartSFX;
/** AK Event for end of special move */
var AkEvent BeamEndSFX;
/** Looping sound for beam hit */
var AkEvent BeamHitSFX;
var AkEvent BeamHitStopSFX;
/** Camera shake impact */
var CameraShake BeamHitShake;
/** Socket to attach origin of beam */
var const Name ChestBeamSocketName;
/** How much time to wait before attempting to sweep to a new target */
var const float TimeUntilTargetChange;
/** Damagetype used for beam attack */
var const class<KFDamageType> BeamDamageType;
/** The maximum length the beam can be (used both for FX and damage) */
var const float MaxBeamLength;
/** The width, height, and depth of the beam, used for damage traces */
var const vector BeamExtent;
/** How much time to wait between damage ticks */
var const float DamageInterval;
/** How much damage to deal per damage tick */
var const int DamagePerTick;
/** How much of a momentum impulse to apply each damage tick */
var const float DamageMomentumImpulse;
/** Draws a debug line between the beam's start and end points */
var const bool bDrawDebugBeam;
function SpecialMoveStarted( bool bForced, Name PrevMove )
{
super.SpecialMoveStarted(bForced, PrevMove);
// Attempt a sweep attack if we have multiple targets in front of us
if( KFPOwner.Role == ROLE_Authority )
{
KFPOwner.SetTimer( TimeUntilTargetChange, false, nameOf(Timer_AttemptTargetChange), self );
}
// Use custom FX/lights
KFPOwner.UpdateGameplayMICParams();
//Start KFP beam SFX
KFPOwner.SetWeaponAmbientSound(BeamStartSFX);
KingPawnOwner = KFPawn_ZedFleshpoundKing(KFPOwner);
}
/** Updates beam PSC */
function Tick( float DeltaTime )
{
super.Tick( DeltaTime );
if( KFPOwner.WorldInfo.NetMode != NM_DedicatedServer && BeamPSC != none && BeamPSC.bIsActive )
{
SetBeamTarget();
}
}
function bool IsValidBeamTarget(Actor HitActor)
{
//If we're a pawn or we're a non-resettable static mesh actor
return Pawn(HitActor) != none || (StaticMeshActor(HitActor) != none && !StaticMeshActor(HitActor).bResetCapable) || SkeletalMeshActor(HitActor) != none || StaticMeshCollectionActor(HitActor) != none;
}
function SetBeamTarget()
{
local vector SocketLoc, BeamEnd, HitLocation, HitNormal;
local rotator SocketRot;
local Actor HitActor;
local bool bShouldActivateHit;
KFPOwner.Mesh.GetSocketWorldLocationAndRotation( ChestBeamSocketName, SocketLoc, SocketRot );
BeamEnd = SocketLoc + vector(KFPOwner.Rotation) * MaxBeamLength; // for now use Pawn's rotation, I don't know why but this socket's rotation freaks out during the animation
bShouldActivateHit = false;
foreach KFPOwner.TraceActors(class'Actor', HitActor, HitLocation, HitNormal, BeamEnd, SocketLoc, BeamExtent, , KFPOwner.TRACEFLAG_Bullet)
{
//Beam will be stopped on all valid targets
if (IsValidBeamTarget(HitActor))
{
if (HitActor != none && HitActor.bCanBeDamaged)
{
BeamEnd = HitLocation - vect(0, 0, 64);
}
else
{
BeamEnd = HitLocation;
}
bShouldActivateHit = HitActor != none;
break;
}
}
BeamPSC.SetBeamTargetPoint( 0, BeamEnd, 0 );
//Setup impact sound/fx if valid
if (bShouldActivateHit)
{
//Trigger sound and VFX at the same time, use the more reliable SFX
if (BeamHitPSC == none)
{
BeamHitPSC = KFPOwner.WorldInfo.MyEmitterPool.SpawnEmitter(BeamHitPSCTemplate, BeamEnd);
KingPawnOwner.BeamHitAC.PlayEvent(BeamHitSFX);
}
}
else
{
if (BeamHitPSC != none && BeamHitPSC.bIsActive)
{
BeamHitPSC.DeactivateSystem();
BeamHitPSC = none;
KingPawnOwner.BeamHitAC.PlayEvent(BeamHitStopSFX);
}
}
//Update location of sound/fx
if (BeamHitPSC != none)
{
BeamHitPSC.SetAbsolute(true, true, false);
BeamHitPSC.SetTranslation(BeamEnd);
BeamHitPSC.SetRotation(KFPOwner.Rotation);
}
if( bDrawDebugBeam )
{
KFPOwner.FlushPersistentDebugLines();
KFPOwner.DrawDebugLine( SocketLoc, BeamEnd, 100, 128, 255, true );
}
}
function ToggleBeam( bool bEnable )
{
local ParticleSysParam SourceParam;
if( bEnable )
{
//Startup visual FX
if( BeamPSCTemplate != none)
{
BeamPSC = KFPOwner.WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BeamPSCTemplate, KFPOwner.Mesh, ChestBeamSocketName, true );
if( BeamPSC != none)
{
SourceParam.Name = 'SourceActor';
SourceParam.ParamType = PSPT_Actor;
SourceParam.Actor = KFPOwner;
BeamPSC.InstanceParameters.AddItem( SourceParam );
SetBeamTarget();
}
}
// Start damage timer. We'll do damage on the client too so we affect ragdolls!
KFPOwner.SetTimer( DamageInterval, true, nameOf(Timer_TickDamage), self );
}
else
{
DisableBeamFX();
// Stop dealing damage
KFPOwner.ClearTimer( nameOf(Timer_TickDamage), self );
}
}
/** Turns the beam particle system/sound OFF */
function DisableBeamFX()
{
if( BeamPSC != none && BeamPSC.bIsActive )
{
BeamPSC.DeactivateSystem();
BeamPSC = none;
}
if (BeamHitPSC != none)
{
BeamHitPSC.DeactivateSystem();
BeamHitPSC = none;
}
if (KingPawnOwner.BeamHitAC != none)
{
KingPawnOwner.BeamHitAC.StopEvents();
}
}
function Timer_AttemptTargetChange()
{
local Pawn P, BestTarget;
local KFAIController_ZedFleshpoundKing KingFPController;
local vector PawnDir;
local float DotAngle, BestAngle;
PawnDir = vector( KFPOwner.Rotation );
foreach KFPOwner.WorldInfo.AllPawns( class'Pawn', P )
{
if( P != KFPOwner
&& P != AIOwner.Enemy
&& P.GetTeamNum() != KFPOwner.GetTeamNum()
&& P.IsAliveAndWell()
&& P.CanAITargetThisPawn(KFPOwner.Controller))
{
DotAngle = PawnDir dot Normal( P.Location - KFPOwner.Location );
if( DotAngle < 0.2f )
{
continue;
}
// Choose the target furthest from the center to create the widest sweep
if( BestAngle == 0.f || DotAngle < BestAngle )
{
BestAngle = DotAngle;
BestTarget = P;
}
}
}
// Set our new target if we can
if( BestTarget != none )
{
KingFPController = KFAIController_ZedFleshpoundKing( AIOwner );
if( KingFPController != none )
{
KingFPController.ForceTargetChange( BestTarget );
}
}
}
function Timer_TickDamage()
{
local vector SocketLoc, BeamDir, EndTrace, HitLocation, HitNormal;
local rotator SocketRot;
local Actor HitActor;
local TraceHitInfo HitInfo;
KFPOwner.Mesh.GetSocketWorldLocationAndRotation( ChestBeamSocketName, SocketLoc, SocketRot );
BeamDir = vector( KFPOwner.Rotation ); // for now use Pawn's rotation, I don't know why but this socket's rotation freaks out during the animation
EndTrace = SocketLoc + BeamDir * MaxBeamLength;
foreach KFPOwner.TraceActors(class'Actor', HitActor, HitLocation, HitNormal, EndTrace, SocketLoc, BeamExtent, , KFPOwner.TRACEFLAG_Bullet)
{
//Beam will be stopped on all valid targets
if (IsValidBeamTarget(HitActor))
{
if (HitActor.bCanBeDamaged)
{
HitActor.TakeDamage(DamagePerTick, KFPOwner.Controller, HitLocation, BeamDir*DamageMomentumImpulse, BeamDamageType, HitInfo, KFPOwner);
if (Pawn(HitActor) != none && PlayerController(Pawn(HitActor).Controller) != none)
{
PlayerController(Pawn(HitActor).Controller).ClientPlayCameraShake(BeamHitShake, 1.f, true);
}
}
return;
}
}
}
function SpecialMoveEnded( Name PrevMove, Name NextMove )
{
super.SpecialMoveEnded(PrevMove, NextMove);
ToggleBeam(false);
//Start KFP beam SFX
KFPOwner.SetWeaponAmbientSound(BeamEndSFX);
// Restore FX/lights
if( KFPOwner != none && KFPOwner.IsAliveAndWell() )
{
KFPOwner.UpdateGameplayMICParams();
}
KFPOwner.FlushPersistentDebugLines(); // temp, @todo: remove
}
DefaultProperties
{
// ---------------------------------------------
// SpecialMove
Handle=KFSM_FleshpoundKing_ChestBeam
AnimName=Atk_ChestBeam
AnimStance=EAS_FullBody
bDisableSteering=false
bDisableMovement=true
bDisableTurnInPlace=true
bCanBeInterrupted=false
bUseCustomRotationRate=true
CustomRotationRate=(Pitch=50000, Yaw=25000, Roll=50000)
// ---------------------------------------------
// Effects
BeamPSCTemplate=ParticleSystem'ZED_Fleshpound_King_EMIT.FX_ChestBeam'
BeamHitPSCTemplate=ParticleSystem'ZED_Fleshpound_King_EMIT.FX_ChestBeam_Impact'
ChestBeamSocketName=ChestBeamSocket
BeamStartSFX=AkEvent'ww_zed_fleshpound_2.Play_King_FP_Beam_Start_LP'
BeamEndSFX=AkEvent'ww_zed_fleshpound_2.Play_King_FP_Beam_End'
BeamHitSFX=AkEvent'ww_zed_fleshpound_2.Play_FP_Beam_Hit_LP'
BeamHitStopSFX=AkEvent'ww_zed_fleshpound_2.Stop_FP_Beam_Hit_LP'
Begin Object Class=CameraShake Name=BeamHitShake0
bSingleInstance=true
OscillationDuration=0.35f
RotOscillation={(Pitch=(Amplitude=250.f,Frequency=60.f),
Yaw=(Amplitude=150.f,Frequency=70.f),
Roll=(Amplitude=150.f,Frequency=100.f))}
End Object
BeamHitShake=BeamHitShake0
// ---------------------------------------------
// KFSM_FleshpoundKing_ChestBeam
BeamDamageType=class'KFDT_FleshpoundKing_ChestBeam'
BeamExtent=(X=15.0f, Y=15.0f, Z=15.0f)
MaxBeamLength=2500.0f //1300
DamageInterval=0.1f
DamagePerTick=10 //15 //7
DamageMomentumImpulse=100.0f
TimeUntilTargetChange=0.75f
// ---------------------------------------------
// Debug
bDrawDebugBeam=false
}