1
0
KF2-Dev-Scripts/KFGameContent/Classes/KFPawn_ZedStalker.uc
2023-09-21 22:31:11 +03:00

562 lines
16 KiB
Ucode

//=============================================================================
// KFPawn_ZedStalker
//=============================================================================
// Stalker
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
//=============================================================================
class KFPawn_ZedStalker extends KFPawn_Monster;
var MaterialInstanceConstant SpottedMaterial;
var AkComponent CloakedAkComponent;
var AkEvent CloakedLoop;
var AkEvent CloakedLoopEnd;
var float CloakPercent;
/** The local player controller viewing this pawn */
var KFPlayerController ViewerPlayer;
/** Cloak speeds */
var float CloakSpeed;
var float DeCloakSpeed;
simulated event PostBeginPlay()
{
super.PostBeginPlay();
SetCloaked( true );
PlayStealthSoundLoop();
}
/**
* Check on various replicated data and act accordingly.
*/
simulated event ReplicatedEvent(name VarName)
{
switch( VarName )
{
case nameof(bIsCloakingSpottedByTeam):
UpdateGameplayMICParams();
break;
case nameof(bIsCloaking):
ClientCloakingStateUpdated();
break;
}
Super.ReplicatedEvent(VarName);
}
/** Toggle cloaking material */
function SetCloaked(bool bNewCloaking)
{
if ( bCanCloak )
{
if( bNewCloaking && !IsCombatCapable() )
{
return;
}
if( MaxHeadChunkGoreWhileAlive == 0 && bIsCloaking != bNewCloaking && IsAliveAndWell() )
{
`DialogManager.PlaySpotCloakDialog( self, bNewCloaking );
}
bIsCloaking = bNewCloaking;
if( WorldInfo.NetMode != NM_DedicatedServer )
{
UpdateGameplayMICParams();
Mesh.SetPerObjectShadows(!bNewCloaking);
ClearBloodDecals();
}
super.SetCloaked( bNewCloaking );
}
}
/**
* bIsCloaking replicated state changed
* Network: Local and Remote Clients
*/
simulated function ClientCloakingStateUpdated()
{
if( bIsCloaking )
{
ClearBloodDecals();
}
UpdateGameplayMICParams();
Mesh.SetPerObjectShadows( !bIsCloaking );
}
/** Handle cloaking materials */
simulated function UpdateGameplayMICParams()
{
local bool bIsSpotted;
super.UpdateGameplayMICParams();
// Cannot cloak after stalker has been gored
if ( !bIsGoreMesh && WorldInfo.NetMode != NM_DedicatedServer )
{
// visible by local player or team (must go after ServerCallOutCloaking)
bIsSpotted = (bIsCloakingSpottedByLP || bIsCloakingSpottedByTeam);
if ( bIsSpotted && bIsCloaking )
{
CharacterMICs[0].SetParent(SpottedMaterial);
}
else if( CharacterMICs[0].Parent == SpottedMaterial )
{
CharacterMICs[0].SetParent(Mesh.SkeletalMesh.Materials[0]);
PlayStealthSoundLoop();
}
}
}
/** Called when SwitchToGoreLOD is successful */
simulated event NotifyGoreMeshActive()
{
super.NotifyGoreMeshActive();
`DialogManager.PlaySpotCloakDialog( self, false );
PlayStealthSoundLoopEnd();
// Restore to uncloaked values
bIsCloaking = false;
bIsCloakingSpottedByLP = false;
bIsCloakingSpottedByTeam = false;
// Set to our solid gore mat (only AI-controlled)
if( PlayerReplicationInfo == none && Mesh.SkeletalMesh.Materials.Length > 2 )
{
CharacterMICs[0].SetParent( Mesh.SkeletalMesh.Materials[2] );
}
}
/** Turns off FX and sounds */
simulated function TerminateEffectsOnDeath()
{
PlayStealthSoundLoopEnd();
super.TerminateEffectsOnDeath();
}
simulated function PlayStealthSoundLoop()
{
if( WorldInfo.NetMode != NM_DedicatedServer && !CloakedAkComponent.IsPlaying(CloakedLoop) )
{
CloakedAkComponent.PlayEvent( CloakedLoop, true, true );
}
}
simulated function PlayStealthSoundLoopEnd()
{
if( WorldInfo.NetMode != NM_DedicatedServer && CloakedAkComponent.IsPlaying(CloakedLoop) )
{
CloakedAkComponent.PlayEvent( CloakedLoopEnd, true, true );
}
}
/** Overridden to support transparency scalar */
simulated event Tick( float DeltaTime )
{
local float MinCloakPct;
super.Tick( DeltaTime );
if( WorldInfo.NetMode != NM_DedicatedServer )
{
if( ViewerPlayer == none )
{
ViewerPlayer = KFPlayerController( GetALocalPlayerController() );
}
MinCloakPct = GetMinCloakPct();
if( !bIsCloaking )
{
if( CloakPercent < 1.0f )
{
CloakPercent = fMin( CloakPercent + DeltaTime*DeCloakSpeed, 1.0f );
CharacterMICs[0].SetScalarParameterValue( 'Transparency', CloakPercent );
}
}
else if( CloakPercent > MinCloakPct )
{
CloakPercent = fMax( CloakPercent - DeltaTime*CloakSpeed, MinCloakPct );
CharacterMICs[0].SetScalarParameterValue( 'Transparency', CloakPercent );
}
}
}
/** Gets the minimum cloaked amount based on the viewer */
simulated protected function float GetMinCloakPct()
{
if( ViewerPlayer != none && (ViewerPlayer.GetTeamNum() == GetTeamNum() || ViewerPlayer.PlayerReplicationInfo.bOnlySpectator) )
{
return 0.5f;
}
return 0.f;
}
/*********************************************************************************************
`* Perk related
********************************************************************************************* */
/**
* Called every 0.5f seconds to check if a cloaked zed has been spotted
* Network: All but dedicated server
*/
simulated event UpdateSpottedStatus()
{
local bool bOldSpottedByLP;
local KFPerk LocalPerk;
local float DistanceSq, Range;
if( WorldInfo.NetMode == NM_DedicatedServer )
{
return;
}
bOldSpottedByLP = bIsCloakingSpottedByLP;
bIsCloakingSpottedByLP = false;
if( !IsHumanControlled() || bIsSprinting )
{
if( ViewerPlayer == none )
{
ViewerPlayer = KFPlayerController( GetALocalPlayerController() );
}
if( ViewerPlayer != none )
{
LocalPerk = ViewerPlayer.GetPerk();
}
if ( ViewerPlayer != none && ViewerPlayer.Pawn != None && ViewerPlayer.Pawn.IsAliveAndWell() && LocalPerk != none &&
LocalPerk.bCanSeeCloakedZeds && `TimeSince( LastRenderTime ) < 1.f )
{
DistanceSq = VSizeSq(ViewerPlayer.Pawn.Location - Location);
Range = LocalPerk.GetCloakDetectionRange();
if ( DistanceSq < Square(Range) )
{
bIsCloakingSpottedByLP = true;
if ( LocalPerk.IsCallOutActive() )
{
// Beware of server spam. This RPC is marked unreliable and UpdateSpottedStatus has it's own cooldown timer
ViewerPlayer.ServerCallOutPawnCloaking(self);
}
}
}
}
// If spotted by team already, there is no point in trying to update the MIC here
if ( !bIsCloakingSpottedByTeam )
{
if ( bIsCloakingSpottedByLP != bOldSpottedByLP )
{
UpdateGameplayMICParams();
}
}
}
/** notification from player with CallOut ability */
function CallOutCloaking( optional KFPlayerController CallOutController )
{
bIsCloakingSpottedByTeam = true;
LastStoredCC = CallOutController;
UpdateGameplayMICParams();
SetTimer(2.f, false, nameof(CallOutCloakingExpired));
}
/** Call-out cloaking ability has timed out */
function CallOutCloakingExpired()
{
bIsCloakingSpottedByTeam = false;
LastStoredCC = none;
UpdateGameplayMICParams();
}
/** Applies the rally buff and spawns a rally effect */
simulated function bool Rally(
KFPawn RallyInstigator,
ParticleSystem RallyEffect,
name EffectBoneName,
vector EffectOffset,
ParticleSystem AltRallyEffect,
name AltEffectBoneNames[2],
vector AltEffectOffset,
optional bool bSkipEffects=false
)
{
local PlayerController PC;
if( WorldInfo.NetMode != NM_DedicatedServer )
{
PC = WorldInfo.GetALocalPlayerController();
// Don't spawn rally effects if cloaking but not spotted
if( bIsCloaking
&& !bIsCloakingSpottedByLP
&& !bIsCloakingSpottedByTeam
&& PC.GetTeamNum() < 255
&& PC.Pawn != none
&& PC.Pawn.IsAliveAndWell() )
{
bSkipEffects = true;
}
}
return super.Rally( RallyInstigator, RallyEffect, EffectBoneName, EffectOffset, AltRallyEffect, AltEffectBoneNames, AltEffectOffset, bSkipEffects );
}
/* PlayDying() is called on server/standalone game when killed
and also on net client when pawn gets bTearOff set to true (and bPlayedDeath is false)
*/
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
{
// Uncloak on death
SetCloaked(false);
bCanCloak = false;
Super.PlayDying(DamageType, HitLoc);
}
/** Disable cloaking when crippled/headless */
function CauseHeadTrauma( float BleedOutTime=5.f )
{
Super.CauseHeadTrauma( BleedOutTime );
if( bIsHeadless && IsAliveAndWell() && !IsDoingSpecialMove() )
{
SetCloaked( false );
}
}
/** Interrupt certain moves when bEmpDisrupted is set */
function OnStackingAfflictionChanged(byte Id)
{
Super.OnStackingAfflictionChanged(Id);
if( Role == ROLE_Authority && IsAliveAndWell() )
{
if ( Id == AF_EMP )
{
SetCloaked( !bEMPPanicked && !bEMPDisrupted );
}
}
}
function PlayHit( float Damage, Controller InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, TraceHitInfo HitInfo )
{
super.PlayHit( Damage, InstigatedBy, HitLocation, damageType, Momentum, HitInfo );
SetCloaked( false );
// SetTimer times are max values pulled from KFPawnAnimInfo::PlayHitReactionAnim
// if last hit caused call to DoPauseAI
if( HitFxInfo.DamageType != none && HitFxInfo.DamageType.default.MeleeHitPower > 0 )
{
SetTimer( 1.f, false, nameof(ReCloakTimer) );
}
else // light hit or DOT
{
SetTimer( 0.35f, false, nameof(ReCloakTimer) );
}
}
simulated function PlayHeadAsplode()
{
if( bIsCloaking )
{
SetCloaked( false );
}
bCanCloak = false;
super.PlayHeadAsplode();
}
simulated function ReCloakTimer()
{
SetCloaked( true );
}
static function bool IsStalkerPawn()
{
return true;
}
/** Returns (hardcoded) dialog event ID for when players kills this zed type */
function int GetKillerDialogID()
{
return 66;//KILL_Female
}
/** Returns (hardcoded) dialog event ID for when players spots this zed type */
function int GetSpotterDialogID()
{
if( bIsCloaking && MaxHeadChunkGoreWhileAlive == 0 )
{
return 135;//SPOTZ_Cloaked
}
return 125;//SPOTZ_Generic
}
/** Returns (hardcoded) dialog event ID for when trader gives advice to player who was killed by this zed type */
static function int GetTraderAdviceID()
{
return 40;//TRAD_AdviceStalker
}
DefaultProperties
{
LocalizationKey=KFPawn_ZedStalker
Begin Object Name=KFPawnSkeletalMeshComponent
// Enabling for bAlwaysFullAnimWeight bones
bUpdateKinematicBonesFromAnimation=true
// Shadows off at spawn since she spawns cloaked
bAllowPerObjectShadows=false
End Object
// ---------------------------------------------
// Stats
XPValues(0)=8
XPValues(1)=10
XPValues(2)=10
XPValues(3)=10
// ---------------------------------------------
// Content
MonsterArchPath="ZED_ARCH.ZED_Stalker_Archetype"
PawnAnimInfo=KFPawnAnimInfo'ZED_Stalker_ANIM.Stalker_AnimGroup'
DifficultySettings=class'KFDifficulty_Stalker'
SpottedMaterial=MaterialInstanceConstant'ZED_Stalker_MAT.ZED_Stalker_Visible_MAT'
// ---------------------------------------------
// Special Moves
Begin Object Name=SpecialMoveHandler_0
SpecialMoveClasses(SM_Evade) =class'KFSM_Evade'
SpecialMoveClasses(SM_Evade_Fear) =class'KFSM_Evade_Fear'
SpecialMoveClasses(SM_Emerge) =class'KFSM_Emerge'
End Object
// for reference: Vulnerability=(default, head, legs, arms, special)
IncapSettings(AF_Stun)= (Vulnerability=(2.0, 2.0, 1.0, 1.0), Cooldown=5.0, Duration=3.0)
IncapSettings(AF_Knockdown)=(Vulnerability=(1.5), Cooldown=1.0)
IncapSettings(AF_Stumble)= (Vulnerability=(1.f), Cooldown=0.5)
IncapSettings(AF_GunHit)= (Vulnerability=(0.75), Cooldown=0.0)
IncapSettings(AF_MeleeHit)= (Vulnerability=(2.0), Cooldown=0.0)
IncapSettings(AF_FirePanic)=(Vulnerability=(3), Cooldown=3.0, Duration=4.0)
IncapSettings(AF_EMP)= (Vulnerability=(2.5), Cooldown=5.0, Duration=5.0)
IncapSettings(AF_Poison)= (Vulnerability=(10.0), Cooldown=7.5, Duration=5.5)
IncapSettings(AF_Microwave)=(Vulnerability=(0.5), Cooldown=20.5, Duration=5.0)
IncapSettings(AF_Freeze)= (Vulnerability=(2.5), Cooldown=1.5, Duration=4.0)
IncapSettings(AF_Snare)= (Vulnerability=(10.0, 10.0, 10.0, 10.0), Cooldown=5.5, Duration=4.0)
IncapSettings(AF_Bleed)= (Vulnerability=(2.0))
IncapSettings(AF_Shrink)= (Vulnerability=(1.0))
ShrinkEffectModifier = 2.0f
ParryResistance=1
// ---------------------------------------------
// Gameplay
bCanGrabAttack=false
bCanMeleeAttack=true
Begin Object Name=MeleeHelper_0
BaseDamage=9.f
MaxHitRange=180.f
MomentumTransfer=25000.f
MyDamageType=class'KFDT_Slashing_ZedWeak'
End Object
Health=75
DoshValue=15
Mass=50.f
bIsStalkerClass=true
// ---------------------------------------------
// Penetration
PenetrationResistance=0.5
// ---------------------------------------------
// Resistance & Vulnerability
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Submachinegun', DamageScale=(0.9))) //0.8
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_AssaultRifle', DamageScale=(1.5))) //2.5
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Shotgun', DamageScale=(1.0))) //0.7
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Handgun', DamageScale=(1.0)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Rifle', DamageScale=(0.85))) //0.6
DamageTypeModifiers.Add((DamageType=class'KFDT_Slashing', DamageScale=(1.0)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Bludgeon', DamageScale=(1.0)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Fire', DamageScale=(0.85))) //0.6
DamageTypeModifiers.Add((DamageType=class'KFDT_Microwave', DamageScale=(0.2)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Explosive', DamageScale=(0.75))) //0.6
DamageTypeModifiers.Add((DamageType=class'KFDT_Piercing', DamageScale=(1.0)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic', DamageScale=(1.0)))
// ---------------------------------------------
// Movement / Physics
GroundSpeed=400.f
SprintSpeed=500.f
PhysRagdollImpulseScale=0.9f
KnockdownImpulseScale=0.9f
// ---------------------------------------------
// AI / Navigation
ControllerClass=class'KFAIController_ZedStalker'
DamageRecoveryTimeHeavy=0.65f
DamageRecoveryTimeMedium=1.0f
ElitePawnClass.Add(class'KFPawn_ZedDAR_EMP')
ElitePawnClass.Add(class'KFPawn_ZedDAR_Laser')
ElitePawnClass.Add(class'KFPawn_ZedDAR_Rocket')
RotationRate=(Pitch=50000,Yaw=45000,Roll=50000)
// ---------------------------------------------
// Cloaking
bIsCloaking=true
bCanCloak=true
bCloakOnMeleeEnd=true
CloakPercent=1.0f
DeCloakSpeed=4.5f
CloakSpeed=4.0f
//CloakDuration=1.2
// ---------------------------------------------
// Audio
CloakedLoop=AkEvent'WW_ZED_Stalker.ZED_Stalker_SFX_Stealth_LP'
CloakedLoopEnd=AkEvent'WW_ZED_Stalker.ZED_Stalker_SFX_Stealth_LP_Stop'
Begin Object Class=AkComponent name=CloakedAkComponent0
BoneName=dummy
bStopWhenOwnerDestroyed=true
bForceOcclusionUpdateInterval=true
OcclusionUpdateInterval=0.4f
End Object
CloakedAkComponent=CloakedAkComponent0
Components.Add( CloakedAkComponent0 )
ZEDCowboyHatAttachName=Hat_Attach
`if(`notdefined(ShippingPC))
DebugRadarTexture=Texture2D'UI_ZEDRadar_TEX.MapIcon_Stalker';
`endif
// Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true
// If we need to reuse it more we'll have to connect map to zed here
MapReplacePawnClass.Add(class'KFPawn_ZedHansClot')
}