555 lines
16 KiB
Ucode
555 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))
|
|
|
|
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
|
|
}
|