2020-12-13 15:01:13 +00:00
//=============================================================================
// KFPawn_ZedHansBase
//=============================================================================
// Base Hans Class. Put here so classes in KFGame could reference
// Hans properties, and if any native code is needed
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
// John "Ramm-Jaeger" Gibson
//=============================================================================
class KFPawn _ZedHansBase extends KFPawn _MonsterBoss
abstract ;
/** Cached AI controller */
var KFAIController _Hans MyHansController ;
/** If true, MkB42s are unholstered and aiming (also see bIsTargeting) */
var repnotify bool bGunsEquipped ;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Battle phases
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/** Info for hans battle phases */
struct HansBattlePhaseInfo
{
/** Whether or not we can do the Frenzy attack this battle phase */
var bool bCanFrenzy ;
/** Whether or not to desire sprinting behavior this battle phase */
var bool bSprintingBehavior ;
/** Info on how often we can toss any grenades in this battle phase */
var float GlobalOffensiveNadePhaseCooldown ;
/** Whether or not we can use a regular nerve gas toss this battle phase */
var bool bCanTossNerveGas ;
/** Info on how often we can toss Nerve Gas Nades in this battle phase */
var float NerveGasTossPhaseCooldown ;
/** Whether or not we can use a nerve gas barrage this battle phase */
var bool bCanBarrageNerveGas ;
/** Info on how often we can barrage Nerve Gas Nades in this battle phase */
var float NerveGasBarragePhaseCooldown ;
/** Whether or not we can use the MKB assault rifle this battle phase */
var bool bCanUseGuns ;
/** Info on how often we can use guns in this battle phase */
var float GunAttackPhaseCooldown ;
/** How long Hans will keep his guns out when he pulls them out in this battle phase */
var float GunAttackLengthPhase ;
/** Whether or not we can use a regular grenade this battle phase */
var bool bCanTossGrenade ;
/** Info on how often we can toss HENades in this battle phase */
var float HENadeTossPhaseCooldown ;
/** Whether or not we can use grenade barrage this battle phase */
var bool bCanBarrageGrenades ;
/** Info on how often we can barrage HENades in this battle phase */
var float HENadeBarragePhaseCooldown ;
/** Whether Hans can move when throwing grenades, per difficulty level */
var array < bool > bCanMoveWhileThrowingGrenades ;
/** Heal threshold (MaxHealth * HealThreshold) per difficulty level */
var array < float > HealThresholds ;
/** Heal amount Health + MaxHealth*(MaxHealth * HealAmount) per difficulty level */
var array < float > HealAmounts ;
/** Shield health per difficulty level */
var array < int > MaxShieldHealth ;
structdefaultproperties
{
bSprintingBehavior = true
GlobalOffensiveNadePhaseCooldown = 8
NerveGasTossPhaseCooldown = 10
NerveGasBarragePhaseCooldown = 20
GunAttackPhaseCooldown = 20
GunAttackLengthPhase = 15
HENadeTossPhaseCooldown = 10
HENadeBarragePhaseCooldown = 20
}
} ;
/** Configuration for the Hans battle phases */
var array < HansBattlePhaseInfo > BattlePhases ;
/** Whether or not we've healed this battle phase */
var bool bHealedThisPhase ;
/** Amount of health healed this phase (cumulative) */
var float AmountHealedThisPhase ;
/** In the mode to hunt players and try and heal */
var repnotify bool bInHuntAndHealMode ;
/** How much to reduce Hans' damage he receives when he is in hunt and heal mode */
var ( ) float HuntAndHealModeDamageReduction ;
/** How much to scale incap powers by when healing */
var float IncapPowerScaleWhenHealing ;
/** Amount of health shield has remaining */
var float ShieldHealth ;
var float ShieldHealthMax ;
var float ShieldHealthScale ;
/** Replicated shield health percentage */
var repnotify byte ShieldHealthPctByte ;
/** Number of times we've bumped into enemies when trying to heal */
var int NumHuntAndHealEnemyBumps ;
/** Last time we bumped into an enemy */
var float LastHuntAndHealEnemyBumpTime ;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Grenade Throwing
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/** Grenade tossing */
var class < KFProj _Grenade > ActiveGrenadeClass ;
/** Grenade tossing */
var class < KFProj _Grenade > ExplosiveGrenadeClass ;
var const class < KFProj _Grenade > SeasonalExplosiveGrenadeClasses [ 5 ] ;
/** Grenade tossing */
var class < KFProj _Grenade > NerveGasGrenadeClass ;
var const class < KFProj _Grenade > SeasonalNerveGasGrenadeClasses [ 5 ] ;
/** Grenade tossing */
var class < KFProj _Grenade > SmokeGrenadeClass ;
var const class < KFProj _Grenade > SeasonalSmokeGrenadeClasses [ 5 ] ;
/** Socket where grenade spawns when Hans throws with his right hand */
var name RightHandSocketName ;
/** Socket where grenade spawns when Hans throws with his left hand */
var name LeftHandSocketName ;
/** Grenade accuracy TODO: Make this based on skill level for AI*/
var vector GrenadeTossSpread ;
/** Waiting to do a smoke grenade barrage as soon as the guns are put away */
var bool bPendingSmokeGrenadeBarrage ;
/** How many grenades have been tossed this barrage */
var int BarrageTossCount ;
/** The current grenade attack is a barrage */
var bool bDoingBarrage ;
/** Info on how often we can tossed any damaging grenades */
var float GlobalOffensiveNadeCooldown ;
var float LastOffensiveNadeTime ;
/** Info on how often we can toss HENades */
var float HENadeTossCooldown ;
var float LastHENadeTossTime ;
/** Info on how often we can barrage HENades */
var float HENadeBarrageCooldown ;
var float LastHENadeBarrageTime ;
/** Info on how often we can toss Nerve Gas Nades */
var float NerveGasTossCooldown ;
var float LastNerveGasTossTime ;
/** Info on how often we can barrage Nerve Gas Nades */
var float NerveGasBarrageCooldown ;
var float LastNerveGasBarrageTime ;
/** Info on how often we can toss Smoke Nades */
var float SmokeTossCooldown ;
var float LastSmokeTossTime ;
replication
{
// Replicated to ALL
if ( bNetDirty )
bGunsEquipped , bInHuntAndHealMode , ShieldHealthPctByte ;
}
simulated event ReplicatedEvent ( name VarName )
{
switch ( VarName )
{
case nameof ( bInHuntAndHealMode ) :
SetHuntAndHealMode ( bInHuntAndHealMode ) ;
break ;
case nameOf ( ShieldHealthPctByte ) :
UpdateShieldColor ( ) ;
break ;
default :
super . ReplicatedEvent ( VarName ) ;
}
}
/** Called from Possessed event when this controller has taken control of a Pawn */
function PossessedBy ( Controller C , bool bVehicleTransition )
{
Super . PossessedBy ( C , bVehicleTransition ) ;
MyHansController = KFAIController _Hans ( C ) ;
SetPhaseCooldowns ( 0 ) ;
ExplosiveGrenadeClass = SeasonalExplosiveGrenadeClasses [ class 'KFGameEngine' . static . GetSeasonalEventID ( ) ] ;
NerveGasGrenadeClass = SeasonalNerveGasGrenadeClasses [ class 'KFGameEngine' . static . GetSeasonalEventID ( ) ] ;
SmokeGrenadeClass = SeasonalSmokeGrenadeClasses [ class 'KFGameEngine' . static . GetSeasonalEventID ( ) ] ;
}
/** Increment Hans to the next battle phase */
function IncrementBattlePhase ( )
{
CurrentBattlePhase ++ ;
bHealedThisPhase = true ;
AmountHealedThisPhase = 0 ;
SetPhaseCooldowns ( CurrentBattlePhase - 1 ) ;
bForceNetUpdate = true ;
}
/** Set the correct phase based cooldown for this battle phase */
function SetPhaseCooldowns ( int BattlePhase )
{
GlobalOffensiveNadeCooldown = BattlePhases [ BattlePhase ] . GlobalOffensiveNadePhaseCooldown ;
HENadeTossCooldown = BattlePhases [ BattlePhase ] . HENadeTossPhaseCooldown ;
HENadeBarrageCooldown = BattlePhases [ BattlePhase ] . HENadeBarragePhaseCooldown ;
NerveGasTossCooldown = BattlePhases [ BattlePhase ] . NerveGasTossPhaseCooldown ;
NerveGasBarrageCooldown = BattlePhases [ BattlePhase ] . NerveGasBarragePhaseCooldown ;
if ( MyHansController != none )
{
MyHansController . ShootingCooldown = BattlePhases [ BattlePhase ] . GunAttackPhaseCooldown ;
MyHansController . MaxGunAttackLength = BattlePhases [ BattlePhase ] . GunAttackLengthPhase ;
// Reset summon flag
MyHansController . bSummonedThisPhase = false ;
}
}
/** Reduce damage when in hunt and heal mode */
function AdjustDamage ( out int InDamage , out vector Momentum , Controller InstigatedBy , vector HitLocation , class < DamageType > DamageType , TraceHitInfo HitInfo , Actor DamageCauser )
{
Super . AdjustDamage ( InDamage , Momentum , InstigatedBy , HitLocation , DamageType , HitInfo , DamageCauser ) ;
// Update shield health before scaling damage
UpdateShieldHealth ( InDamage ) ;
// Reduce damage when in hunt and heal mode
if ( bInHuntAndHealMode && ShieldHealth > 0 )
{
InDamage = Max ( InDamage * HuntAndHealModeDamageReduction , 1 ) ;
}
}
/** Reduce affliction/incap strength when healing */
simulated function AdjustAffliction ( out float AfflictionPower )
{
if ( bInHuntAndHealMode )
{
AfflictionPower *= IncapPowerScaleWhenHealing ;
}
2023-09-21 19:31:11 +00:00
super . AdjustAffliction ( AfflictionPower ) ;
2020-12-13 15:01:13 +00:00
}
/** Updates shield health and shield health percent */
function UpdateShieldHealth ( optional int Damage = - 1 )
{
// If shield has been depleted already, or no damage was taken, don't update
if ( ShieldHealth == 0 || Damage == 0 )
{
return ;
}
// Damage the shield if applicable
if ( ShieldHealth > 0 && Damage > 0 )
{
ShieldHealth = Max ( ShieldHealth - Damage , 0 ) ;
}
// Shield health depleted, break shield
if ( ShieldHealth == 0 )
{
ShieldHealthPctByte = 0 ;
}
else
{
ShieldHealthPctByte = FloatToByte ( fClamp ( ShieldHealth / ShieldHealthMax , 0. f , 1. f ) ) ;
}
// Update color based on percentage of health remaining
UpdateShieldColor ( ) ;
// Need to ensure shield gets broken on dedicated servers
if ( WorldInfo . NetMode == NM _DedicatedServer && bInHuntAndHealMode && ShieldHealthPctByte == 0 )
{
BreakShield ( ) ;
}
}
/** Breaks the shield */
simulated function BreakShield ( )
{
if ( Role == ROLE _Authority )
{
// Cancel any flee in progress
if ( MyHansController != none )
{
MyHansController . CancelFlee ( ) ;
}
// Cancel hunt and heal mode
SetHuntAndHealMode ( false ) ;
// Cancel any active special moves
if ( IsDoingSpecialMove ( ) )
{
EndSpecialMove ( ) ;
}
// Knock down
Knockdown ( Velocity != vect ( 0 , 0 , 0 ) ? - Velocity * 2 : 3 * ( - vector ( Rotation ) * GroundSpeed ) , vect ( 1 , 1 , 1 ) , , , , , Location ) ;
}
}
/** Can we frenzy attack in this phase */
function bool CanFrenzyInThisPhase ( )
{
return BattlePhases [ CurrentBattlePhase - 1 ] . bCanFrenzy ;
}
/** If true Hans will favor sprinting in this phase. Even if it's false he may sprint under certain circumstances, but when it's true he'll try and sprint almost all the time */
function bool DesireSprintingInThisPhase ( )
{
return BattlePhases [ CurrentBattlePhase - 1 ] . bSprintingBehavior ;
}
/** Called by PawnAnimInfo when determining whether an attack with bSpecializedMode TRUE can be performed */
function bool ShouldPlaySpecialMeleeAnims ( )
{
// make all of Han's melee attacks available when he can frenzy
return CanFrenzyInThisPhase ( ) ;
}
/** Only allow blocking when in melee */
function bool CanBlock ( )
{
return ! bGunsEquipped && ! bInHuntAndHealMode && super . CanBlock ( ) ;
}
/** Has the offensive grenade cooldown time elapsed */
function bool OffensiveGrenadeCooldownComplete ( )
{
if ( LastOffensiveNadeTime == 0 || ` TimeSince(LastOffensiveNadeTime) > GlobalOffensiveNadeCooldown )
{
return true ;
}
return false ;
}
/** Can we toss nerve gas in this phase */
function bool CanTossNerveGasInThisPhase ( )
{
if ( BattlePhases [ CurrentBattlePhase - 1 ] . bCanTossNerveGas && ! bInHuntAndHealMode
&& ` TimeSince(LastNerveGasTossTime) > 0 && ` TimeSince ( LastNerveGasTossTime ) > NerveGasTossCooldown
&& OffensiveGrenadeCooldownComplete ( ) )
{
return true ;
}
return false ;
}
/** Can we barrage nerve gas in this phase */
function bool CanBarrageNerveGasInThisPhase ( )
{
if ( BattlePhases [ CurrentBattlePhase - 1 ] . bCanBarrageNerveGas && ! bInHuntAndHealMode
&& ` TimeSince(LastNerveGasBarrageTime) > 0 && ` TimeSince ( LastNerveGasBarrageTime ) > NerveGasBarrageCooldown
&& OffensiveGrenadeCooldownComplete ( ) )
{
return true ;
}
return false ;
}
/** Can we use guns in this phase */
function bool CanUseGunsInThisPhase ( )
{
return BattlePhases [ CurrentBattlePhase - 1 ] . bCanUseGuns ;
}
/** Can we do a standard grenade toss in this phase */
function bool CanTossGrenadeInThisPhase ( )
{
if ( BattlePhases [ CurrentBattlePhase - 1 ] . bCanTossGrenade && ! bInHuntAndHealMode
&& ` TimeSince(LastHENadeTossTime) > 0 && ` TimeSince ( LastHENadeTossTime ) > HENadeTossCooldown
&& OffensiveGrenadeCooldownComplete ( ) )
{
return true ;
}
return false ;
}
/** Can we do a grenade barrage in this phase */
function bool CanGrenadeBarrageInThisPhase ( )
{
if ( BattlePhases [ CurrentBattlePhase - 1 ] . bCanBarrageGrenades && ! bInHuntAndHealMode
&& ` TimeSince(LastHENadeBarrageTime) > 0 && ` TimeSince ( LastHENadeBarrageTime ) > HENadeBarrageCooldown
&& OffensiveGrenadeCooldownComplete ( ) )
{
return true ;
}
return false ;
}
/** Can we do a regular smoke toss in this phase */
function bool CanSmokeTossInThisPhase ( )
{
return bInHuntAndHealMode && ` TimeSince(LastSmokeTossTime) > 0 && ` TimeSince ( LastSmokeTossTime ) > SmokeTossCooldown ;
}
/** Return true if busy throwing grenade(s) or using AICommand_ThrowGrenade */
function bool IsThrowingGrenade ( )
{
return false ;
}
/** Implemented in subclass, put here to be accessible from KFGame.u */
function bool CacheGrenadeThrowLocation ( optional bool bLeftHand )
{
return false ;
}
function SetActiveGrenadeClassGrenade ( )
{
ActiveGrenadeClass = ExplosiveGrenadeClass ;
}
function SetActiveGrenadeClassSmoke ( )
{
ActiveGrenadeClass = SmokeGrenadeClass ;
}
function SetActiveGrenadeClassNerveGas ( )
{
ActiveGrenadeClass = NerveGasGrenadeClass ;
}
/** Returns TRUE if all conditions are met to allow movement while throwing grenades */
simulated function bool CanMoveWhileThrowingGrenades ( )
{
local KFGameReplicationInfo KFGRI ;
// See if this battle phase allows it
KFGRI = KFGameReplicationInfo ( WorldInfo . GRI ) ;
if ( KFGRI != none && BattlePhases [ CurrentBattlePhase - 1 ] . bCanMoveWhileThrowingGrenades [ KFGRI . GetModifiedGameDifficulty ( ) ] )
{
return true ;
}
return bInHuntAndHealMode || LocalIsOnePlayerLeftInTeamGame ( ) ;
}
function PlayDrawGunsDialog ( )
{
` DialogManager.PlayHansDrawGunsDialog( self );
}
function PlayGrenadeDialog ( bool bBarrage )
{
switch ( ActiveGrenadeClass )
{
case ExplosiveGrenadeClass :
` DialogManager.PlayHansNadeDialog( self, bBarrage );
break ;
case SmokeGrenadeClass :
` DialogManager.PlayHansSmokeDialog( self, false );
break ;
case NerveGasGrenadeClass :
` DialogManager.PlayHansGasDialog( self, bBarrage );
break ;
} ;
}
/** Sets variables related to "hunt and heal" mode */
simulated function SetHuntAndHealMode ( bool bOn )
{
local KFGameInfo KFGI ;
local float HealthMod ;
local float HeadHealthMod ;
bInHuntAndHealMode = bOn ;
if ( Role == ROLE _Authority )
{
if ( bInHuntAndHealMode )
{
SetTimer ( 0.25 f , true , nameof ( HuntAndHealBump ) ) ;
// Use health/player count to scale shield health
HealthMod = 1. f ;
KFGI = KFGameInfo ( WorldInfo . Game ) ;
if ( KFGI != none )
{
KFGI . DifficultyInfo . GetAIHealthModifier (
self ,
KFGI . GetModifiedGameDifficulty ( ) ,
KFGI . GetLivingPlayerCount ( ) ,
HealthMod ,
HeadHealthMod ) ;
}
// Initialize shield health
ShieldHealthMax = BattlePhases [ CurrentBattlePhase - 1 ] . MaxShieldHealth [ WorldInfo . Game . GetModifiedGameDifficulty ( ) ] * HealthMod * ShieldHealthScale ;
ShieldHealth = ShieldHealthMax ;
UpdateShieldHealth ( ) ;
// Initialize bump variables
NumHuntAndHealEnemyBumps = 0 ;
LastHuntAndHealEnemyBumpTime = WorldInfo . TimeSeconds ;
// Stumble
if ( IsDoingSpecialMove ( ) )
{
EndSpecialMove ( ) ;
}
DoSpecialMove ( SM _Stumble , , , class 'KFSM_Stumble' . static . PackBodyHitSMFlags ( self , vector ( Rotation ) ) ) ;
// Inform AI of phase change
if ( MyHansController != none )
{
MyHansController . NextBattlePhase ( ) ;
}
// Increment battle phase
IncrementBattlePhase ( ) ;
}
else
{
ClearTimer ( nameof ( HuntAndHealBump ) ) ;
` DialogManager.PlayHansBattlePhaseDialog( self, CurrentBattlePhase );
}
}
if ( ! bOn )
{
bHealedThisPhase = false ;
SetTimer ( 0.1 , false , nameOf ( Timer _ResetShieldHealthPct ) ) ;
}
}
function SetShieldScale ( float InScale )
{
ShieldHealthScale = InScale ;
}
/** Sets shield health pct back to 0 for next phase */
function Timer _ResetShieldHealthPct ( )
{
ShieldHealthPctByte = 0 ;
}
/** Returns the amount that Hans should heal for this phase */
function float GetHealAmountForThisPhase ( )
{
return float ( HealthMax ) * BattlePhases [ CurrentBattlePhase - 2 ] . HealAmounts [ WorldInfo . Game . GetModifiedGameDifficulty ( ) ] ;
}
/** Summons boss minions depending on difficulty and battle phase */
function SummonMinions ( )
{
local int Skill ;
local int DifficultyIndex ;
local KFAIWaveInfo MinionWave ;
local KFAISpawnManager SpawnManager ;
local KFGameInfo MyKFGameInfo ;
MyKFGameInfo = KFGameInfo ( WorldInfo . Game ) ;
if ( MyHansController != none )
{
Skill = MyHansController . Skill ;
}
// Determine which summon squad to spawn by difficulty
if ( Skill == class 'KFGameDifficultyInfo' . static . GetDifficultyValue ( 0 ) ) // Normal
{
DifficultyIndex = 0 ;
}
else if ( Skill <= class 'KFGameDifficultyInfo' . static . GetDifficultyValue ( 1 ) ) // Hard
{
DifficultyIndex = 1 ;
}
else if ( Skill <= class 'KFGameDifficultyInfo' . static . GetDifficultyValue ( 2 ) ) // Suicidal
{
DifficultyIndex = 2 ;
}
else // Hell on Earth
{
DifficultyIndex = 3 ;
}
// Force frustration mode on
MyKFGameInfo . GetAIDirector ( ) . bForceFrustration = true ;
// Spawn our minions
MinionWave = GetWaveInfo ( CurrentBattlePhase , DifficultyIndex ) ;
SpawnManager = MyKFGameInfo . SpawnManager ;
if ( MinionWave != none && SpawnManager != none )
{
SpawnManager . SummonBossMinions ( MinionWave . Squads , GetNumMinionsToSpawn ( ) ) ;
}
}
/** Used to clear DOTs, such as when Hans is healing */
simulated function ClearDOTs ( )
{
DamageOverTimeArray . Length = 0 ;
}
/** If Hans repeatedly bumps into players during his hunt and heal phase, just heal */
simulated event Bump ( Actor Other , PrimitiveComponent OtherComp , Vector HitNormal )
{
local KFPawn KFP ;
if ( ! bGunsEquipped )
{
if ( Role == ROLE _Authority && bInHuntAndHealMode && MyHansController != none && NumHuntAndHealEnemyBumps >= 0 && ! IsDoingSpecialMove ( )
&& Other . GetTeamNum ( ) != GetTeamNum ( ) && MyHansController . FindCommandOfClass ( class 'AICommand_Attack_Grab' ) == none )
{
KFP = KFPawn ( Other ) ;
if ( KFP != none )
{
if ( ` TimeSince(LastHuntAndHealEnemyBumpTime) > 1.f )
{
++ NumHuntAndHealEnemyBumps ;
LastHuntAndHealEnemyBumpTime = WorldInfo . TimeSeconds ;
// If we've bumped into players enough times, just heal off of them
if ( NumHuntAndHealEnemyBumps > 0 )
{
NumHuntAndHealEnemyBumps = - 1 ;
MyHansController . CancelFlee ( false ) ;
MyKFAIC . DoGrabAttack ( KFP ) ;
return ;
}
}
}
}
}
super . Bump ( Other , OtherComp , HitNormal ) ;
}
// Knock other zeds the heck out of the way when I want to attack my enemy!!!
function HuntAndHealBump ( )
{
local KFPawn _Monster KFPM ;
local vector ClosestPoint ;
local float ClosestDist ;
if ( MyKFAIC == none || MyKFAIC . Enemy == none /*|| IsDoingSpecialMove(SM_GrappleAttack)*/
|| VSizeSq ( Location - MyKFAIC . Enemy . Location ) > 250000 ) // 5 meters
{
return ;
}
foreach WorldInfo . AllPawns ( class 'KFPawn_Monster' , KFPM , Location , 300. f )
{
// Heavy bump guys that are between us and our prey!
if ( KFPM != self && KFPM . IsAliveAndWell ( ) )
{
ClosestDist = PointDistToSegment ( KFPM . Location , Location , MyKFAIC . Enemy . Location , ClosestPoint ) ;
if ( ClosestDist < GetCollisionRadius ( ) * 1.5 )
{
MyKFAIC . DoHeavyZedBump ( KFPM , Normal ( KFPM . Location - Location ) ) ;
}
}
}
}
/** returns whether we need to turn to fire at the specified location and angle*/
function bool NeedToTurnEx ( vector targ , optional float Angle = 0.93 )
{
local vector LookDir , AimDir ;
LookDir = Vector ( Rotation ) ;
LookDir . Z = 0 ;
LookDir = Normal ( LookDir ) ;
AimDir = targ - Location ;
AimDir . Z = 0 ;
AimDir = Normal ( AimDir ) ;
return ( ( LookDir Dot AimDir ) < Angle ) ;
}
/** AnimNotify which starts during Hans's AoE melee attack */
simulated function ANIMNOTIFY _AoENerveGas ( ) { }
/** Overridden to prevent blood while invulnerable */
simulated function ApplyBloodDecals ( int HitZoneIndex , vector HitLocation , vector HitDirection , name HitZoneName , name HitBoneName , class < KFDamageType > DmgType , bool bIsDismemberingHit , bool bWasObliterated )
{
if ( ! bInHuntAndHealMode )
{
super . ApplyBloodDecals ( HitZoneIndex , HitLocation , HitDirection , HitZoneName , HitBoneName , DmgType , bIsDismemberingHit , bWasObliterated ) ;
}
// else do nothing, leave no blood because hans is mostly invulnerable
}
/** Updates the color of the shield based on its health */
simulated function UpdateShieldColor ( ) ;
/** Detaches the shield PSC */
simulated function DetachShieldFX ( ) ;
DefaultProperties
{
ShieldHealthScale = 1. f
}