Advertisement
Treedestroyed

KFPawn_Monster

Dec 2nd, 2018
1,422
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. //=============================================================================
  2. // KFPawn_Monster
  3. //=============================================================================
  4. // Base pawn class for Zeds
  5. //=============================================================================
  6. // Killing Floor 2
  7. // Copyright (C) 2015 Tripwire Interactive LLC
  8. // - Andrew "Strago" Ladenberger
  9. //=============================================================================
  10.  
  11. class KFPawn_Monster extends KFPawn
  12.     abstract
  13.     nativereplication
  14.     native(Pawn)
  15.     dependson(KFPawnAnimInfo, KFPawnVoiceGroup, KFPawnVoiceGroupEventData, KFSpawnVolume, KFHeadShotEffectList);
  16.  
  17. `include(KFGame\KFGameAnalytics.uci);
  18. `include(KFGame\KFMatchStats.uci);
  19.  
  20. /************************************
  21.  * @name    General info for AAR/Stats
  22.  ************************************/
  23.  
  24. var bool bLargeZed;
  25. var bool bVersusZed;
  26.  
  27. /************************************
  28.  * @name    Content
  29.  ************************************/
  30.  
  31.  /** Path for DLO of the MonsterArch */
  32. var const string MonsterArchPath;
  33. /**
  34.  * Default content loaded by this pawn.  Private, use GetCharacterMonsterInfo()
  35.  * NOTE: DO NOT statically reference in defaults as it needs to be loaded dynamically
  36.  */
  37. var private const KFCharacterInfo_Monster CharacterMonsterArch;
  38.  
  39. /** List of variants that this pawn can be spawned as */
  40. var const array<class<KFPawn_Monster> > ElitePawn${1}< ${3} >
  41.  
  42. /** Custom third person camera offsets */
  43. var() ViewOffsetData    ThirdPersonViewOffset;
  44.  
  45. /** Randomized color index from the monster arch if it exists for this zed */
  46. var int RandomColorIdx;
  47.  
  48. /** List of static attachments from the character info */
  49. var array<StaticMeshComponent> StaticAttachList;
  50.  
  51. /*********************************************************************************************
  52.  * @name    Combat
  53. ********************************************************************************************* */
  54.  
  55. /** The chance that this monster pawn will sprint */
  56. var(Combat) float   SprintChance;
  57. var(Combat) bool    bCanGrabAttack;
  58. /** Odds (0-1) of evaluating whether to do a grab attack instead of a basic melee attack */
  59. var(Combat) float   GrabAttackFrequency;
  60. var(Combat) bool    bCanMeleeAttack;
  61. var(Combat) bool    bHasExtraSprintJumpVelocity;
  62.  
  63. /** The amount to scale this Zed's damage based on difficulty */
  64. var(Combat) float   DifficultyDamageMod;
  65.  
  66. /** GameInfo based damage resistance modifier cached at spawn time (0: Ignore, 1: Standard) */
  67. var(Combat) float   GameResistancePct;
  68.  
  69. /** Time until death after head is taken off */
  70. var(Combat) float   HeadlessBleedOutTime;
  71. /** Enables crippled animation and behavior state */
  72. var repnotify bool  bIsHeadless;
  73.  
  74. /** If > 0, clientside head injury gore can be applied while still alive */
  75. var byte MaxHeadChunkGoreWhileAlive;
  76.  
  77. /** Object that manages melee attacks, and stores default damage */
  78. var(Weapon) instanced KFMeleeHelperAI MeleeAttackHelper;
  79.  
  80. var protected   bool    bHasReducedMeleeDamage;
  81.  
  82. /** Total dosh this monster is worth on kill */
  83. var private const   int     DoshValue;
  84. /** XP this monster is worth (per difficulty) */
  85. var private const   float   XPValues[4];
  86. /** List of sockets representing weakpoint zone locations */
  87. var() array<name> WeakSpotSocketNames;
  88.  
  89. /**
  90.  * Information on resistant or vulnerable damage types
  91.  * @todo: This is all static data so we should consider moving to the archetype
  92. */
  93. struct native DamageModifierInfo
  94. {
  95.     /** A damage type to modify damage to this zed when it is received */
  96.     var() class<DamageType> DamageType;
  97.     /** Damage scale for this damage type. Additional array elements (MAX:4) can be used to modify for higher game difficulties */
  98.     var() array<float> DamageScale;
  99.  
  100.     structdefaultproperties
  101.     {
  102.         DamageScale=(1.f)
  103.     }
  104. };
  105.  
  106. /** These damage types cause this zed to receive increased damage */
  107. var  array<DamageModifierInfo> DamageTypeModifiers;
  108. /** Additional damage type modifiers that can override the defaults */
  109. var  array<DamageModifierInfo> LiveDamageTypeModifiers;
  110.  
  111. /** Special move cooldown info */
  112. struct native SpecialMoveCooldownInfo
  113. {
  114.     var float CooldownTime;
  115.     var transient float LastUsedTime;
  116.     var ESpecialMove SMHandle;
  117.  
  118.     /** Used by UI */
  119.     var Texture2D SpecialMoveIcon;
  120.     var int Charges;
  121.  
  122.     var string NameLocalizationKey;
  123.     var bool bShowOnHud;
  124.  
  125.     structdefaultproperties
  126.     {
  127.         SMHandle=SM_None
  128.         bShowOnHud=true
  129.         LastUsedTime=0.f
  130.         SpecialMoveIcon=Texture2D'UI_Widgets.MenuBarWidget_SWF_IF'
  131.         Charges=-1
  132.     }
  133. };
  134.  
  135. /** The amount to scale damage dealt by the fleshpound */
  136. var float ZedBumpDamageScale;
  137.  
  138. var protected bool bShowHealth;
  139.  
  140. /** Set only while inside AdjustDamage when calculating extra head explosion damage*/
  141. var transient bool bCheckingExtraHeadDamage;
  142.  
  143. /** Base human-controlled melee damage */
  144. var(Combat) float HumanBaseMeleeDamage;
  145.  
  146. /** Contains balance settings per game difficulty */
  147. var class<KFMonsterDifficultyInfo> DifficultySettings;
  148.  
  149. /*********************************************************************************************
  150.  * @name    Player-controlled
  151. ********************************************************************************************* */
  152.  
  153. /*
  154.  * Move list classification used by MoveListGamepadScheme
  155.  * Must be aligned firemode
  156.  * -- label w/ default bind for readability
  157.  */
  158. enum EPlayerZedGamepadMove
  159. {
  160.     ZGM_Attack_R2,      // firemode 0 -- R2
  161.     ZGM_Block_R1,       // firemode 1 -- R1
  162.     ZGM_Melee_Square,   // firemode 2 -- Square
  163.     ZGM_Special_R3,     // firemode 3 -- R3
  164.     ZGM_Explosive_Ll,   // firemode 4 -- L1
  165.     ZGM_Attack_L2,      // firemode 5 -- L2
  166.     ZGM_Melee_Triangle, // firemode 6 -- Triangle
  167. };
  168.  
  169. /* For gamepads remap the controls by type (aligned to EPlayerZedGamepadMove) */
  170. var array<ESpecialMove> MoveListGamepadScheme;
  171.  
  172. /** Cooldown times for each special move ability */
  173. var array<SpecialMoveCooldownInfo> SpecialMoveCooldowns;
  174.  
  175. /** Flag set after executing a jump */
  176. var bool bJumped;
  177.  
  178. var transient float LastAttackHumanWarningTime;
  179.  
  180. /*********************************************************************************************
  181.  * @name    Hit Reactions
  182. ********************************************************************************************* */
  183.  
  184. /** Settings for the slowed down incapacitation effect */
  185. const SLOW_SPEED_MOD = 0.8f;
  186.  
  187. /** Resistance value for special move reaction to being parried */
  188. var byte ParryResistance;
  189.  
  190. /** true if we currently incap poisoned */
  191. var repnotify bool  bIsPoisoned;
  192. /** Is microwave panic active? (server only) */
  193. var bool            bMicrowavePanicked;
  194. /** Replicated material parameter */
  195. var repnotify byte  RepInflateMatParam;
  196.  
  197. /** If true, jumping on this pawn triggers ragdoll knockdown */
  198. var bool bKnockdownWhenJumpedOn;
  199.  
  200. /** Cached health before taking damage for status effect resistance */
  201. var transient int OldHealth;
  202.  
  203. /** Server-controlled disable of the gore mesh while alive */
  204. var bool bDisableGoreMeshWhileAlive;
  205.  
  206. /** Whether or not to explode on death (set by server) */
  207. var bool bUseExplosiveDeath;
  208.  
  209. /** Inflate on damage (LERP between none and full inflation based on health) */
  210. var bool bUseDamageInflation;
  211.  
  212. /** Max damage inflation value */
  213. var float ZeroHealthInflation;
  214.  
  215. /** Rate of inflation per second when inflating from damage, up to current intended */
  216. var float DamageInflationRate;
  217.  
  218. /** Rate of deflation per second for damage inflation */
  219. var float DamageDeflationRate;
  220.  
  221. /** Intended inflation. If a pawn is still inflating, this will be higher */
  222. var float IntendedDamageInflationPercent;
  223.  
  224. /** Current inflation percent stored as float */
  225. var float DamageInflationPercent;
  226.  
  227. /** Current replicated damage inflation */
  228. var repnotify byte RepDamageInflateParam;
  229.  
  230. /** Gravity value to use on inflate death */
  231. var float InflateDeathGravity;
  232.  
  233. /** Timer after death until inflation explosion */
  234. var float InflationExplosionTimer;
  235.  
  236. /** Deflate value when affected by bleed */
  237. var repnotify byte RepBleedInflateMatParam;
  238.  
  239. // Base emitter to use for incap effect
  240. var const ParticleSystem BleedIncapFX;
  241.  
  242. // PSC attached to effected pawn when incap is in effect
  243. var ParticleSystemComponent BleedIncapPSC;
  244.  
  245. /*********************************************************************************************
  246.  * @name    Anim Tree Controls
  247. ********************************************************************************************* */
  248.  
  249. /* Plays panicked animation (e.g. On Fire, Poisoned) */
  250. var bool bPlayPanicked;
  251. /* Plays shambling animation (e.g. Headless, EMP) */
  252. var bool bPlayShambling;
  253.  
  254. /** Cached anim nodes */
  255. var KFAnim_RandomScripted WalkBlendList;
  256.  
  257. /*********************************************************************************************
  258.  * @name    Special Abilities
  259. ********************************************************************************************* */
  260.  
  261.  
  262. /** Struct containing block settings, used on a per-difficulty basis */
  263. struct native sBlockInfo
  264. {
  265.     /** Chance of blocking an attack */
  266.     var float Chance;
  267.     /** Time from the start of a block until the zed unblocks again */
  268.     var float Duration;
  269.     /** Maximum number of attacks that can be blocked before the block is broken */
  270.     var float MaxBlocks;
  271.     /** Cooldown duration from the end of the last block */
  272.     var float Cooldown;
  273.     /** How much damage to accumulate (as a percentage of maximum health) before attempting to trigger a block */
  274.     var float DamagedHealthPctToTrigger;
  275.     /** How much to mitigate melee damage when blocking */
  276.     var float MeleeDamageModifier;
  277.     /** How much to mitigate non-melee damage when blocking */
  278.     var float DamageModifier;
  279.     /** How much to scale incap/affliction power by when blocking */
  280.     var float AfflictionModifier;
  281.     /** How much to multiply the chance by when in a solo game */
  282.     var float SoloChanceMultiplier;
  283.  
  284.     structdefaultproperties
  285.     {
  286.         Chance=0.f
  287.         MeleeDamageModifier=1.f
  288.         DamageModifier=1.f
  289.         AfflictionModifier=1.f
  290.     }
  291. };
  292.  
  293. var protected transient sBlockInfo DifficultyBlockSettings;
  294.  
  295. /** Minimum FOV angle attacks must fall in to be blocked */
  296. var protected const float MinBlockFOV;
  297.  
  298. /** Whether this zed is blocking attacks */
  299. var transient bool bIsBlocking;
  300.  
  301. /** Multiplier applied to sprint speed when blocking */
  302. var protected const float BlockSprintSpeedModifier;
  303.  
  304. /** The last time a successful block ended */
  305. var transient float LastBlockTime;
  306.  
  307. var             float   KnockedDownBySonicWaveOdds;
  308. var             bool                bCloakOnMeleeEnd;
  309. var             bool    bIsCloakingSpottedByLP;
  310. var repnotify   bool    bIsCloakingSpottedByTeam;
  311. var             float   LastSpottedStatusUpdate;
  312. /** The most recent controller that made the pawn visible for other players */
  313. var             KFPlayerController  LastStoredCC;
  314. /** Rage flags */
  315. var             bool    bCanRage;
  316. var repnotify   bool    bIsEnraged;
  317.  
  318. /** Particle system component for player alpha rally effect */
  319. var transient   ParticleSystemComponent RallyPSC;
  320.  
  321. /** Particle system components attached to the hands when rallied */
  322. var transient   ParticleSystemComponent RallyHandPSCs[2];
  323.  
  324. /** AI rally buff variables */
  325. struct native sRallyInfo
  326. {
  327.     /** Whether this AI can rally in this difficulty */
  328.     var bool bCanRally;
  329.     /** Whether rally causes this AI to sprint */
  330.     var bool bCauseSprint;
  331.     /** How long the rally buff lasts for */
  332.     var float RallyBuffTime;
  333.     /** How much to modify incoming (taken) damage */
  334.     var float TakenDamageModifier;
  335.     /** How much to modify outgoing (dealt) damage */
  336.     var float DealtDamageModifier;
  337.  
  338.     structdefaultproperties
  339.     {
  340.         bCanRally=true
  341.         bCauseSprint=false
  342.         RallyBuffTime=10.f
  343.         TakenDamageModifier=1.f
  344.         DealtDamageModifier=1.f
  345.     }
  346. };
  347.  
  348. var protected transient sRallyInfo DifficultyRallySettings;
  349.  
  350. /*********************************************************************************************
  351.  * @name    Perks
  352. ********************************************************************************************* */
  353.  
  354. var private     bool    bIsStalker${1}< ${3} >
  355. var private     bool    bIsCrawler${1}< ${3} >
  356. var private     bool    bIsFleshpound${1}< ${3} >
  357. var private     bool    bIsClot${1}< ${3} >
  358. var private     bool    bIsBloat${1}< ${3} >
  359.  
  360. /*********************************************************************************************
  361.  * @name    Movement
  362. ********************************************************************************************* */
  363.  
  364. /** The difficulty adjusted original GroundSpeed for this character */
  365. var transient float NormalGroundSpeed;
  366. /** The difficulty adjusted original SprintSpeed for this character */
  367. var transient float NormalSprintSpeed;
  368. /** The random ground speed modifier that is applied whent this character is initialized */
  369. var transient float InitialGroundSpeedModifier;
  370. /** Time interval for HiddenGroundSpeed check */
  371. var transient float LastAISpeedCheckTime;
  372. /** The last time this Zed was network relevant to someone, or had line of sight to someone */
  373. var transient float LastLOSOrRelevantTime;
  374. /** When true, will attempt to match player speed while in pursuit. Use only if you want to allow upperbody attacks while moving. */
  375. var bool        bMatchEnemySpeed;
  376. /** Match enemy speed while in pursuit if enemy distance <= this value */
  377. var float       MatchEnemySpeedAtDistance;
  378. /** Minimum speed of enemy required for me to begin matching speed */
  379. var float       MinimumEnemySpeedToMatch;
  380. var float       PursuitSpeedScale;
  381. /** Scales distance threshold that enemy is considered reached */
  382. var float       ReachedEnemyThresholdScale;
  383. /** Instead of scaling, will just use this value for reached checks, if value > 0 and using walking physics */
  384. var float       ReachedGoalThresh_Walking;
  385. /** Instead of scaling, will just use this value for reached checks, if value > 0 and using spider physics */
  386. var float       ReachedGoalThresh_Spider;
  387. var float       LastBumpTime;
  388. /** Used as an optimization for zeds and limits how frequently NotifyBump will be called */
  389. var float       BumpFrequency;
  390. /** Set internally if pawn's collision size was modified at start of a jump */
  391. var bool        bRestoreCollisionOnLand;
  392.  
  393. /** Damage type applied to destructible actors when bumped */
  394. var class<KFDamageType> BumpDamageType;
  395.  
  396. /** Damagetype applied to human players when hitting them in mid-air */
  397. var class<KFDamageType> JumpBumpDamageType;
  398.  
  399. /** Footstep shakes activated in footstep sound animnotify */
  400. var protected float FootstepCameraShakeInnerRadius;
  401. var protected float FootstepCameraShakeOuterRadius;
  402. var CameraShake FootstepCameraShake;
  403.  
  404. /** A ground speed this pawn has been set to blend to */
  405. var float DesiredAdjustedGroundSpeed;
  406. /** A sprint speed this pawn has been set to blend to */
  407. var float DesiredAdjustedSprintSpeed;
  408. /** The rate to transition speeds when blending to new movement speeds */
  409. var float SpeedAdjustTransitionRate;
  410.  
  411. var AkComponent SprintAkComponent;
  412. var AkComponent HeadShotAkComponent;
  413. var AkEvent StartSprintingSound;
  414. var AkEvent SprintLoopingSound;
  415. var AkEvent StopSprintingSound;
  416.  
  417. var bool bPlayingSprintLoop;
  418.  
  419. /*********************************************************************************************
  420.  * @name    Dismemberment / Gore
  421. ********************************************************************************************* */
  422.  
  423. /** The explosion effect should only be played once */
  424. var transient bool bPlayedExplosionEffect;
  425.  
  426. /** Information regardint the currently attached gore chunks */
  427. struct native AttachedGoreChunkInfo
  428. {
  429.     var int AttachmentIndex;
  430.     var StaticMeshComponent AttachedComponent;
  431. };
  432.  
  433. /** The currently attached gore chunks */
  434. var transient array<AttachedGoreChunkInfo> AttachedGoreChunks;
  435.  
  436. /** Keeps track of the number of head chunks removed for the ZED */
  437. var transient int NumHeadChunksRemoved;
  438.  
  439. /** Keep track of the head bones that have already been broken */
  440. var transient array<Name> BrokenHeadBones;
  441.  
  442. /** How often to check for napalm infections */
  443. //var protected const float NapalmCheckInterval;
  444.  
  445. /** Last time we checked for a napalm infection upon taking damage */
  446. var transient protected float LastNapalmInfectCheckTime;
  447.  
  448. /** Is there a chanced that we explode when we die? Set by the firebug's Zed shrapnel skill */
  449. var bool bCouldTurnIntoShrapnel;
  450.  
  451. /** Gameplay-facing disable of head destruction */
  452. var bool bDisableHeadless;
  453.  
  454. /*********************************************************************************************
  455.  * @name    Dialog
  456. ********************************************************************************************* */
  457. var transient int       DeadHorseHitStreakAmt;
  458. var transient float     LastDeadHorseHitTime;
  459.  
  460. /*********************************************************************************************
  461.  * @name    Debug
  462. ********************************************************************************************* */
  463.  
  464. var bool                bDebug_DrawOverheadInfo;
  465. var bool                bDebug_DrawSprintingOverheadInfo;
  466. var const bool          bDebug_UseIconForShowingSprintingOverheadInfo;
  467. var bool                bDebug_SpawnedThroughCheat;
  468.  
  469. /*********************************************************************************************
  470.  * @name    Door Navigation
  471. ********************************************************************************************* */
  472.  
  473. var float               DefaultCollisionRadius;
  474.  
  475. var KFTrigger_ChokePoint  CurrentChokePointTrigger;
  476.  
  477. var bool                bReducedZedOnZedPinchPointCollisionStateActive;
  478. var const float         CollisionRadiusForReducedZedOnZedPinchPointCollisionState;
  479.  
  480. /*********************************************************************************************
  481.  * @name    Spawning
  482. ********************************************************************************************* */
  483.  
  484. /** When spawning in a spawn volume, the squad type as to be at least this big (can be bigger
  485. *   if there are other zeds in the spawn squad that are larger). */
  486. var() ESquadType MinSpawnSquadSizeType;
  487.  
  488. /*********************************************************************************************
  489.  * @name    Achievements
  490. ********************************************************************************************* */
  491. var protected const int OnDeathAchievementID;
  492. var protected bool bOnDeathAchivementbDisabled;
  493.  
  494. /*********************************************************************************************
  495. * @name Armor
  496. ********************************************************************************************* */
  497.  
  498. /** Armor component to handle armor damage and detachment. */
  499. var class<KFZedArmorInfo> ArmorInfo${1}< ${3} >
  500. var KFZedArmorInfo ArmorInfo;
  501.  
  502. //Byte array of armor percentages, replicated to clients
  503. var repnotify byte RepArmorPct[3];
  504.  
  505. //Bit field for the status of the armor zones.  1 = attached
  506. var repnotify byte ArmorZoneStatus;
  507. var byte PreviousArmorZoneStatus;
  508.  
  509. //Hit FX overrides for hitting armor
  510. var const int OverrideArmorFXIndex;
  511.  
  512. /*********************************************************************************************
  513.  * @name    Delegates
  514. ********************************************************************************************* */
  515.  
  516. /** Declaration for attach/detach gore chunk delegates */
  517. simulated delegate bool GoreChunkAttachmentCriteria();
  518. simulated delegate bool GoreChunkDetachmentCriteria();
  519.  
  520. replication
  521. {
  522.     // Replicated to ALL
  523.     if (bNetDirty)
  524.         bIsHeadless, bIsPoisoned, bPlayPanicked, bPlayShambling, MaxHeadChunkGoreWhileAlive,
  525.         RepInflateMatParam, RepDamageInflateParam, RepBleedInflateMatParam, bDisableGoreMeshWhileAlive,
  526.         bDisableHeadless, InflateDeathGravity, InflationExplosionTimer, bUseDamageInflation, bUseExplosiveDeath;
  527.     if ( bNetDirty && bCanCloak )
  528.         bIsCloakingSpottedByTeam;
  529.     if ( bNetDirty && bCanRage )
  530.         bIsEnraged;
  531.     if (Role == ROLE_Authority)
  532.         ArmorZoneStatus, RepArmorPct;
  533. }
  534.  
  535. cpptext
  536. {
  537.     // Actor
  538.     INT* GetOptimizedRepList( BYTE* InDefault, FPropertyRetirement* Retire, INT* Ptr, UPackageMap* Map, UActorChannel* Channel );
  539.     virtual void TickSpecial( FLOAT DeltaSeconds );
  540.     virtual void TickAuthoritative( FLOAT DeltaSeconds );
  541.  
  542.     // AI / navigation
  543.     virtual UBOOL IgnoreBlockingBy( const AActor *Other) const;
  544.     virtual void NotifyBump( AActor *Other, UPrimitiveComponent* OtherComp, const FVector &HitNormal );
  545.     virtual FLOAT MaxSpeedModifier();
  546.     virtual void UpdateAISuperSpeed();
  547.     virtual void UpdateAIPursuitSpeed();
  548.     virtual UBOOL IsGlider();
  549.     virtual UBOOL ReachThresholdTest( const FVector &TestPosition, const FVector &Dest, AActor* GoalActor, FLOAT UpThresholdAdjust, FLOAT DownThresholdAdjust, FLOAT ThresholdAdjust );
  550.     virtual void HandleSerpentineMovement(FVector& out_Direction, FLOAT Distance, const FVector& Dest);
  551.     virtual INT ModifyCostForReachSpec(UReachSpec* Spec, INT Cost);
  552.     virtual void NotifyBumpLevel(AActor* Wall, const FVector &HitLocation, const FVector& HitNormal);
  553.     virtual void OnRigidBodyCollision(const FRigidBodyCollisionInfo& MyInfo, const FRigidBodyCollisionInfo& OtherInfo, const FCollisionImpactData& RigidCollisionData);
  554.     virtual UBOOL ShouldTrace(UPrimitiveComponent* Primitive,AActor *SourceActor, DWORD TraceFlags);
  555.     virtual void physicsRotation(FLOAT deltaTime, FVector OldVelocity);
  556. }
  557.  
  558. native function bool SpiderPhysicsWallAdjust( vector HitNormal, actor HitActor );
  559. /** Tells the monster whether or not it should shrink its collision cylinder when going through a choke point */
  560. native function SetChokePointCollision( bool bUseChokeCollision );
  561. /** Checks if we are our current location encroaches any world geometry */
  562. native function bool CheckEncroachingWorldGeometry();
  563.  
  564. /**
  565.  * Check on various replicated data and act accordingly.
  566.  */
  567. simulated event ReplicatedEvent(name VarName)
  568. {
  569.     switch( VarName )
  570.     {
  571.     case nameof(bIsHeadless):
  572.         StopAkEventsOnBone( 'head' );
  573.         // No more auto aiming to this zed
  574.         bCanBeAdheredTo=false;
  575.         bCanBeFrictionedTo=false;
  576.         break;
  577.  
  578.     case nameof(bIsPoisoned):
  579.         AfflictionHandler.ToggleEffects(AF_Poison, bIsPoisoned);
  580.         break;
  581.  
  582.     case nameof(RepInflateMatParam):
  583.         AfflictionHandler.UpdateMaterialParameter(AF_Microwave, GetCurrentInflation());
  584.         break;
  585.  
  586.     case nameof(RepBleedInflateMatParam):
  587.         AfflictionHandler.UpdateMaterialParameter(AF_Bleed, ByteToFloat(RepBleedInflateMatParam));
  588.         UpdateBleedIncapFX();
  589.         break;
  590.  
  591.     case nameof(Controller):
  592.         // Set audio switch based on AI or player-controlled
  593.         SetSwitch( 'Player_Zed', IsHumanControlled() ? 'Player' : 'NotPlayer' );
  594.         break;
  595.  
  596.     case nameof(bEmpDisrupted):
  597.         if( bEmpDisrupted )
  598.         {
  599.             // If this pawn was disrupted, put all of its moves on cooldown
  600.             PutAllMovesOnCooldown();
  601.         }
  602.         break;
  603.  
  604.     case nameof(RepDamageInflateParam):
  605.         HandleDamageInflation();
  606.         break;
  607.  
  608.     case nameof(RepArmorPct):
  609.         if(ArmorInfo != none)
  610.         {
  611.             ArmorInfo.UpdateArmorPieces();
  612.         }
  613.         break;
  614.  
  615.     case nameof(ArmorZoneStatus):
  616.         if (ArmorInfo != none)
  617.         {
  618.             ArmorInfo.UpdateArmorPieces();
  619.         }
  620.         break;
  621.  
  622.     case nameof(bIsEnraged):
  623.         SetEnraged(bIsEnraged);
  624.         break;
  625.     }
  626.  
  627.     super.ReplicatedEvent( VarName );
  628. }
  629.  
  630. /*********************************************************************************************
  631.  * @name    Static
  632. ********************************************************************************************* */
  633.  
  634. // returns the zed's score/dosh value
  635. simulated static function int GetDoshValue()
  636. {
  637.     return default.DoshValue;
  638. }
  639.  
  640. simulated static function float GetXPValue(byte Difficulty)
  641. {
  642.     return default.XPValues[Difficulty];
  643. }
  644.  
  645. /** Gets the actual classes used for spawning. Can be overridden to replace this monster with another */
  646. static event class<KFPawn_Monster> GetAIPawnClassToSpawn()
  647. {
  648.     local WorldInfo WI;
  649.  
  650.     WI = class'WorldInfo'.static.GetWorldInfo();
  651.     if (default.ElitePawnClass.length > 0 && default.DifficultySettings != none && fRand() < default.DifficultySettings.static.GetSpecialSpawnChance(KFGameReplicationInfo(WI.GRI)))
  652.     {
  653.         return default.ElitePawnClass[Rand(default.ElitePawnClass.length)];
  654.     }
  655.  
  656.     return default.class;
  657. }
  658.  
  659. /** Load content archetype when map loads */
  660. native static final function PreloadContent();
  661. /** Called if preload was not called before 1st spawn */
  662. native private function LastChanceLoad();
  663.  
  664. /*********************************************************************************************
  665.  * @name    Constructors, Destructors, and Loading
  666. ********************************************************************************************* */
  667.  
  668. /** Called immediately before gameplay begins */
  669. simulated event PreBeginPlay()
  670. {
  671.     CheckShouldAlwaysBeRelevant(); //these guys could be alive already so this is unreliable
  672.  
  673.     DefaultCollisionRadius = CylinderComponent.default.CollisionRadius;
  674.  
  675.     Super.PreBeginPlay();
  676.  
  677.     // If we don't have an archetype select one
  678.     if ( CharacterArch == None )
  679.     {
  680.         // Preload should have been called already, but if not do it now!
  681.         if ( CharacterMonsterArch == None )
  682.         {
  683.             LastChanceLoad();
  684.         }
  685.  
  686.         if ( CharacterMonsterArch != None )
  687.         {
  688.             SetCharacterArch(CharacterMonsterArch);
  689.         }
  690.     }
  691.  
  692.     if ( CharacterArch == None )
  693.     {
  694.         `warn("Failed to find character info for KFMonsterPawn!");
  695.         Destroy();
  696.     }
  697.     NormalGroundSpeed = default.GroundSpeed;
  698.     NormalSprintSpeed = default.SprintSpeed;
  699.  
  700.     if (ArmorInfoClass != none)
  701.     {
  702.         ArmorInfo = new(self) ArmorInfo${1}< ${3} >
  703.     }
  704. }
  705.  
  706. simulated event CheckShouldAlwaysBeRelevant()
  707. {
  708.     local KFGameReplicationInfo KFGRI;
  709.  
  710.     // Set to always relevant if we're the last few remaining
  711.     KFGRI = KFGameReplicationInfo( WorldInfo.GRI );
  712.     if( KFGRI != none && KFGRI.AIRemaining <= class'KFGameInfo'.static.GetNumAlwaysRelevantZeds() )
  713.     {
  714.         bAlwaysRelevant = true;
  715.     }
  716. }
  717.  
  718. /** Called immediately after gameplay begins */
  719. simulated event PostBeginPlay()
  720. {
  721.     local KFGameReplicationInfo KFGRI;
  722.  
  723.     super.PostBeginPlay();
  724.  
  725.     // Set our (Network: ALL) difficulty-based settings
  726.     KFGRI = KFGameReplicationInfo( WorldInfo.GRI );
  727.     if( KFGRI != none )
  728.     {
  729.         SetRallySettings( DifficultySettings.static.GetRallySettings(self, KFGRI) );
  730.         SetZedTimeSpeedScale( DifficultySettings.static.GetZedTimeSpeedScale(self, KFGRI) );
  731.     }
  732.  
  733.     if( (WorldInfo.NetMode != NM_Client) && IsABoss() )
  734.     {
  735.         `AnalyticsLog(("boss_spawn", None, Class.Name));
  736.     }
  737. }
  738.  
  739. simulated function SetMeshLightingChannels(LightingChannelContainer NewLightingChannels)
  740. {
  741.     local int i;
  742.  
  743.     super.SetMeshLightingChannels(NewLightingChannels);
  744.  
  745.     for (i = 0; i < StaticAttachList.Length; ++i)
  746.     {
  747.         if (StaticAttachList[i] != none)
  748.         {
  749.             StaticAttachList[i].SetLightingChannels(NewLightingChannels);
  750.         }
  751.     }
  752. }
  753.  
  754. //update the portrait when we get a PRI
  755. simulated function NotifyTeamChanged()
  756. {
  757.     if(CharacterArch != none)
  758.     {
  759.         CharacterArch.SetCharacterFromArch( self, KFPlayerReplicationInfo(PlayerReplicationInfo) );
  760.     }
  761. }
  762.  
  763. /** Called from Possessed event when this controller has taken control of a Pawn */
  764. function PossessedBy( Controller C, bool bVehicleTransition )
  765. {
  766.     local string NPCName;
  767.     local KFPlayerReplicationInfo KFPRI;
  768.     local KFGameReplicationInfo KFGRI;
  769.  
  770.     Super.PossessedBy( C, bVehicleTransition );
  771.  
  772.     KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
  773.  
  774.     /** Set MyKFAIC for convenience to avoid casting */
  775.     if( KFAIController(C) != none )
  776.     {
  777.         MyKFAIC = KFAIController( C );
  778.  
  779.         // If AI Zed has spawned during trader time, should be killed immediately.
  780.         if (!(bDebug_SpawnedThroughCheat || WorldInfo.Game.GetStateName() == 'DebugSuspendWave') && KFGRI != none && !KFGRI.bWaveIsActive)
  781.         {
  782.             Suicide();
  783.             return;
  784.         }
  785.     }
  786.  
  787.     bReducedZedOnZedPinchPointCollisionStateActive = false;
  788.     //CollisionRadiusBeforeReducedZedOnZedPinchPointCollisionState = CollisionCylinderReducedPercentForSameTeamCollision;
  789.  
  790.     // Turn off air control for AI because it can mess up landing.
  791. `if(`notdefined(ShippingPC))
  792.     if( IsHumanControlled() || MyKFAIC.bIsSimulatedPlayerController )
  793. `else
  794.     if( IsHumanControlled() )
  795. `endif
  796.     {
  797.         KFPRI = KFPlayerReplicationInfo(C.PlayerReplicationInfo);
  798.         if(KFPRI != none)
  799.         {
  800.             KFPRI.PlayerHealth  = Health;
  801.             KFPRI.PlayerHealthPercent = FloatToByte( float(Health) / float(HealthMax) );
  802.             SetCharacterArch(CharacterMonsterArch, true);
  803.         }
  804.  
  805.         if( Role == ROLE_Authority )
  806.         {
  807.             LastAttackHumanWarningTime = WorldInfo.TimeSeconds;
  808.         }
  809.     }
  810.     else
  811.     {
  812.         AirControl = AIAirControl;
  813.     }
  814.  
  815.     KFGameInfo(WorldInfo.Game).SetMonsterDefaults( self );
  816.  
  817.     if( MyKFAIC != none && MyKFAIC.PlayerReplicationInfo != None )
  818.     {
  819.         NPCName = string(self);
  820.         NPCName = Repl(NPCName,"KFPawn_Zed","",false);
  821.         PlayerReplicationInfo.PlayerName = NPCName;
  822.         MyKFAIC.PlayerReplicationInfo.PlayerName = NPCName;
  823.     }
  824.  
  825.     // Set our (Network: Server) difficulty-based settings
  826.     if( KFGRI != none )
  827.     {
  828.         SetBlockSettings( DifficultySettings.static.GetBlockSettings(self, KFGRI) );
  829.  
  830.         // Set any AI-specific difficulty settings
  831.         if( MyKFAIC != none )
  832.         {
  833.             MyKFAIC.EvadeOnDamageSettings = DifficultySettings.static.GetEvadeOnDamageSettings( self, KFGRI );
  834.         }
  835.     }
  836.  
  837.     if (ArmorInfo != none)
  838.     {
  839.         ArmorInfo.InitArmor();
  840.     }
  841.  
  842.     // Set audio switch based on AI or player-controlled
  843. `if(`notdefined(ShippingPC))
  844.     SetSwitch( 'Player_Zed', (IsHumanControlled() || (MyKFAIC != none && MyKFAIC.bIsSimulatedPlayerController)) ? 'Player' : 'NotPlayer' );
  845. `else
  846.     SetSwitch( 'Player_Zed', IsHumanControlled() ? 'Player' : 'NotPlayer' );
  847. `endif
  848. }
  849.  
  850. /*
  851. simulated event FellOutOfWorld(class<DamageType> dmgType)
  852. {
  853.     local string Msg;
  854.  
  855.     `warn( self$" FELL OUT OF WORLD!" );
  856.     if( Health > 0 )
  857.     {
  858.         msg = WorldInfo.TimeSeconds@self@GetFuncName()@"fell out of world! Current location:"@Location;
  859.         if( MyKFAIC != None )
  860.         {
  861.             msg = msg@"Controller:"@MyKFAIC@"active command:"@MyKFAIC.GetActiveCommand();
  862.             if( MyKFAIC.MoveTarget != none )
  863.             {
  864.                 msg = msg@"MoveTarget:"@MyKFAIC.MoveTarget;
  865.             }
  866.         }
  867.         `warn( msg );
  868.     }
  869.     super.FellOutOfWorld(dmgType);
  870. }*/ //log spam
  871.  
  872. event BaseChange()
  873. {
  874.     if( IsAliveAndWell() && MyKFAIC != none )
  875.     {
  876.         if( MyKFAIC.NotifyBaseChange( Base, Floor ) )
  877.         {
  878.             return;
  879.         }
  880.     }
  881.  
  882.     super.BaseChange();
  883. }
  884.  
  885. simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
  886. {
  887.     Super.PostInitAnimTree(SkelComp);
  888.  
  889.     WalkBlendList = KFAnim_RandomScripted(SkelComp.FindAnimNode('WalkRandomList'));
  890. }
  891.  
  892. /** Initialize GoreHealth (Server only) */
  893. function ApplySpecialZoneHealthMod(float HealthMod)
  894. {
  895.     //Update head
  896.     HitZones[HZI_HEAD].GoreHealth = default.HitZones[HZI_HEAD].GoreHealth * HealthMod;
  897.     HitZones[HZI_HEAD].MaxGoreHealth = HitZones[HZI_HEAD].GoreHealth;
  898. }
  899.  
  900. function SetShieldScale(float InScale)
  901. {
  902.     if (ArmorInfo != none)
  903.     {
  904.         ArmorInfo.SetShieldScale(InScale);
  905.     }
  906. }
  907.  
  908. /** Used by the Versus takeover code to determine if this zed can be taken over */
  909. function bool CanTakeOver()
  910. {
  911.     local KFVersusNoTakeoverVolume KFNTV;
  912.  
  913.     if( bVersusZed || IsDoingSpecialMove() || IsHeadless() || !IsAliveAndWell() )
  914.     {
  915.         return false;
  916.     }
  917.  
  918.     foreach TouchingActors( class'KFVersusNoTakeoverVolume', KFNTV )
  919.     {
  920.         return false;
  921.     }
  922.  
  923.     return true;
  924. }
  925.  
  926. simulated event Tick(float DeltaTime)
  927. {
  928.     super.Tick(DeltaTime);
  929.  
  930.     if (WorldInfo.NetMode != NM_DedicatedServer && IsAliveAndWell())
  931.     {
  932.         if (bIsSprinting && VSizeSQ(Velocity) > 40000.f)
  933.         {
  934.             if (!bPlayingSprintLoop)
  935.             {
  936.                 if (StartSprintingSound != none && !SprintAkComponent.IsPlaying(StartSprintingSound))
  937.                 {
  938.                     SprintAkComponent.PlayEvent(StartSprintingSound, true, true);
  939.                 }
  940.             }
  941.  
  942.             if(SprintLoopingSound != none && !SprintAkComponent.IsPlaying(SprintLoopingSound))
  943.             {
  944.                 SprintAkComponent.PlayEvent(SprintLoopingSound, true, true);
  945.                 bPlayingSprintLoop = true;
  946.             }
  947.         }
  948.         else if (bPlayingSprintLoop && (!bIsSprinting || VSizeSQ(Velocity) <= 40000.f))
  949.         {
  950.             if (SprintLoopingSound != none && SprintAkComponent.IsPlaying(SprintLoopingSound))
  951.             {
  952.                 SprintAkComponent.StopEvents();
  953.                 bPlayingSprintLoop = false;
  954.             }
  955.  
  956.             if (StopSprintingSound != none && !SprintAkComponent.IsPlaying(StopSprintingSound))
  957.             {
  958.                 SprintAkComponent.PlayEvent(StopSprintingSound, true, true);
  959.             }
  960.         }
  961.     }
  962. }
  963.  
  964. /*********************************************************************************************
  965.  * @name    Character Info Methods
  966. ********************************************************************************************* */
  967.  
  968. simulated function SetCharacterArch(KFCharacterInfoBase Info, optional bool bForce)
  969. {
  970.     super.SetCharacterArch(Info, bForce);
  971.     PlayExtraVFX('AlwaysOn');
  972. }
  973.  
  974. native function KFCharacterInfo_Monster GetCharacterMonsterInfo();
  975.  
  976. /** If true, assign custom player controlled skin when available */
  977. simulated event bool UsePlayerControlledZedSkin()
  978. {
  979.     return bVersusZed;
  980. }
  981.  
  982. /*********************************************************************************************
  983.  * @name    Movement Methods
  984. ********************************************************************************************* */
  985.  
  986. function float GetBlockingSprintSpeedModifier()
  987. {
  988.     return BlockSprintSpeedModifier;
  989. }
  990.  
  991. /** Set a desired movement speed adjustment that will blend in over time */
  992. function AdjustMovementSpeed( float SpeedAdjust )
  993. {
  994.     DesiredAdjustedGroundSpeed = default.GroundSpeed * SpeedAdjust * InitialGroundSpeedModifier;
  995.  
  996.     if( IsDoingSpecialMove() )
  997.     {
  998.         DesiredAdjustedSprintSpeed = fMax( default.SprintSpeed * SpeedAdjust * InitialGroundSpeedModifier * SpecialMoves[SpecialMove].GetSprintSpeedModifier(), DesiredAdjustedGroundSpeed );
  999.     }
  1000.     else
  1001.     {
  1002.         DesiredAdjustedSprintSpeed = fMax( default.SprintSpeed * SpeedAdjust * InitialGroundSpeedModifier, DesiredAdjustedGroundSpeed );
  1003.     }
  1004.  
  1005.     NormalGroundSpeed = DesiredAdjustedGroundSpeed;
  1006.     NormalSprintSpeed = DesiredAdjustedSprintSpeed;
  1007. }
  1008.  
  1009. /** Overridden to cause slight camera shakes when walking. */
  1010. simulated event PlayFootStepSound(int FootDown)
  1011. {
  1012.     if( WorldInfo.NetMode == NM_DedicatedServer )
  1013.     {
  1014.         return;
  1015.     }
  1016.  
  1017.     Super.PlayFootStepSound(FootDown);
  1018.  
  1019.     // The Zed has footstep notifies in one or more of his Idle anim sequences, where it kind of shuffles his foot as he shifts his weight.
  1020.     // Changed the IsDoingLatentMoveCheck() to a velocity check, so player-controlled zeds trigger camera shake as well.
  1021.     if( Physics == PHYS_Walking
  1022.         && Base != none
  1023.         && Mesh.RootMotionMode == RMM_Ignore
  1024.         && FootstepCameraShake != none
  1025.         && VSizeSQ(Velocity) >= 10000.f )
  1026.     {
  1027.         class'Camera'.static.PlayWorldCameraShake(FootstepCameraShake, self, Location, FootstepCameraShakeInnerRadius, FootstepCameraShakeOuterRadius, 1.3f, true);
  1028.     }
  1029. }
  1030.  
  1031. event SpiderBumpLevel( vector HitLocation, vector HitNormal, optional actor Wall );
  1032.  
  1033. simulated event Bump( Actor Other, PrimitiveComponent OtherComp, Vector HitNormal )
  1034. {
  1035.     Super.Bump( Other, OtherComp, HitNormal );
  1036.  
  1037.     if( SpecialMove != SM_None )
  1038.     {
  1039.         SpecialMoves[SpecialMove].NotifyBump( Other, HitNormal );
  1040.     }
  1041.  
  1042.     // Check for a midair bump
  1043.     if( Physics == Phys_Falling && JumpBumpDamageType != none && Other.GetTeamNum() != GetTeamNum() && VSizeSq2D(Velocity) > Square(GroundSpeed * 1.1) )
  1044.     {
  1045.         Other.TakeDamage( MeleeAttackHelper.BaseDamage, Controller, Other.Location, Normal(Velocity), JumpBumpDamageType );
  1046.     }
  1047. }
  1048.  
  1049.  
  1050. /** Called from AICommand_MoveToGoal, notifies us that we've bumped another KFPawn */
  1051. function HandleMonsterBump( KFPawn_Monster Other, Vector HitNormal )
  1052. {
  1053.     local KFPlayerController KFPC;
  1054.  
  1055.     if( !Other.IsNapalmInfected() && CanNapalmInfect(KFPC) )
  1056.     {
  1057.         InfectWithNapalm( Other, KFPC );
  1058.     }
  1059. }
  1060.  
  1061. /** Override to handle special berserker functionality */
  1062. event HitWall( vector HitNormal, actor Wall, PrimitiveComponent WallComp )
  1063. {
  1064.     local KFDoorActor Door;
  1065.  
  1066.     if (IsHumanControlled())
  1067.     {
  1068.         if (!Wall.bStatic && IsAliveAndWell())
  1069.         {
  1070.             Door = KFDoorActor(Wall);
  1071.             if (Door != none)
  1072.             {
  1073.                 TryDestroyDoor(Door);
  1074.             }
  1075.         }
  1076.     }
  1077.  
  1078.     `AILog_Ext( GetFuncName()$"() Wall: "$Wall, 'BumpEvent', MyKFAIC );
  1079.     // Call our special notification
  1080.     NotifyCollideWithActor(HitNormal, Wall);
  1081.     Super.HitWall(HitNormal, Wall, WallComp);
  1082. }
  1083.  
  1084. /** Allows pawn to handle door bump events instead of controller */
  1085. function bool HandleAIDoorBump(KFDoorActor Door)
  1086. {
  1087.     return TryDestroyDoor(Door);
  1088. }
  1089.  
  1090. /** Destroy unwelded doors instantly when there are few players remaining if I'm a boss */
  1091. function bool TryDestroyDoor(KFDoorActor Door)
  1092. {
  1093.     if (IsABoss() && Door != none && !Door.bIsDoorOpen && !Door.bIsDestroyed && Door.WeldIntegrity == 0 && CanObliterateDoors())
  1094.     {
  1095.         Door.IncrementHitCount(self);
  1096.         Door.DestroyDoor(Controller);
  1097.         return true;
  1098.     }
  1099.  
  1100.     return false;
  1101. }
  1102.  
  1103. /** Determines if this pawn can plow through doors */
  1104. function bool CanObliterateDoors()
  1105. {
  1106.     // We only want the kool-aid man effect if we're sprinting
  1107.     if (IsABoss() && !bIsSprinting)
  1108.     {
  1109.         return false;
  1110.     }
  1111.  
  1112.     // Only allow door obliteration if there is one player remaining in a multiplayer game
  1113.     return true;
  1114. }
  1115.  
  1116. /** Notification that Zed collided with an actor */
  1117. function bool NotifyCollideWithActor( Vector HitNormal, Actor Other )
  1118. {
  1119.     local KFDestructibleActor KFDA;
  1120.  
  1121.     if( !Other.bStatic && IsHumanControlled() )
  1122.     {
  1123.         KFDA = KFDestructibleActor( Other );
  1124.         if( KFDA != none )
  1125.         {
  1126.             HandleDestructibleBump( KFDA, HitNormal );
  1127.         }
  1128.     }
  1129.  
  1130.     `AILog_Ext( GetFuncName()$"() Other: "$Other, 'BumpEvent', MyKFAIC );
  1131.     return false;
  1132. }
  1133.  
  1134. function HandleDestructibleBump( KFDestructibleActor Destructible, vector HitNormal )
  1135. {
  1136.     Destructible.BumpedByMonster( self, HitNormal );
  1137. }
  1138.  
  1139. simulated event Touch(Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal)
  1140. {
  1141.     if( MyKFAIC != none )
  1142.     {
  1143.         MyKFAIC.Touch( Other, OtherComp, HitLocation, HitNormal);
  1144.     }
  1145.     Super.Touch(Other, OtherComp, HitLocation, HitNormal);
  1146. }
  1147.  
  1148. function SetSprinting( bool bNewSprintStatus )
  1149. {
  1150.     if( MyKFAIC != none )
  1151.     {
  1152.         if( !MyKFAIC.CanSetSprinting(bNewSprintStatus) )
  1153.         {
  1154.             return;
  1155.         }
  1156.     }
  1157.  
  1158.     // Disallow sprinting if blocking
  1159.     if( bIsBlocking && IsHumanControlled() )
  1160.     {
  1161.         if( bIsSprinting )
  1162.         {
  1163.             bNewSprintStatus = false;
  1164.         }
  1165.         else
  1166.         {
  1167.             return;
  1168.         }
  1169.     }
  1170.  
  1171.     super.SetSprinting(bNewSprintStatus);
  1172. }
  1173.  
  1174. /** Haven't seen this happen in a long time - logging it in case it does occur again */
  1175. event StuckOnPawn (Pawn OtherPawn)
  1176. {
  1177.     JumpOffPawn();
  1178.     if( MyKFAIC != none )
  1179.     {
  1180.         `AILog_Ext( self$" StuckOnPawn event at "$Location$" Base: "$Base,, MyKFAIC );
  1181.         MyKFAIC.DumpCommandStack();
  1182.         MyKFAIC.DumpCommandHistory();
  1183.     }
  1184. }
  1185.  
  1186. /** Monster jumped */
  1187. function bool DoJump( bool bUpdating )
  1188. {
  1189.     local vector JumpVelocity;
  1190.     local rotator ViewRotation;
  1191.     local vector ViewDirection;
  1192.  
  1193.     if (bCanJump)
  1194.     {
  1195.         if (IsHumanControlled())
  1196.         {
  1197.             if (IsDoingSpecialMove())
  1198.             {
  1199.                 return false;
  1200.             }
  1201.  
  1202.             ViewRotation = GetViewRotation();
  1203.             ViewDirection = Normal(Vector(ViewRotation));
  1204.  
  1205.             if (bJumpCapable && !bIsCrouched && !bWantsToCrouch && (Physics == PHYS_Walking || Physics == PHYS_Ladder || Physics == PHYS_Spider))
  1206.             {
  1207.                 if (Physics == PHYS_Spider)
  1208.                     `if(`__TW_PATHFINDING_)
  1209.                     Velocity = Velocity + (GetJumpZ() * Floor);
  1210.                 `else
  1211.                     Velocity = GetJumpZ() * Floor;
  1212.                 `endif
  1213.                 else if (Physics == PHYS_Ladder)
  1214.                     Velocity.Z = 0;
  1215.                 else if (bIsWalking)
  1216.                 {
  1217.                     Velocity.Z = default.JumpZ;
  1218.                 }
  1219.                 else
  1220.                 {
  1221.                     if (bIsSprinting && bHasExtraSprintJumpVelocity)
  1222.                     {
  1223.                         JumpVelocity = GetSprintJumpVelocity(ViewDirection);
  1224.                         JumpVelocity.Z = GetJumpZ();
  1225.                         Velocity += JumpVelocity;
  1226.                     }
  1227.                     else
  1228.                     {
  1229.                         Velocity.Z = GetJumpZ();
  1230.                     }
  1231.                 }
  1232.                 if (Base != None && !Base.bWorldGeometry && Base.Velocity.Z > 0.f)
  1233.                 {
  1234.                     Velocity.Z += Base.Velocity.Z;
  1235.                 }
  1236.                 SetPhysics(PHYS_Falling);
  1237.                 bJumped = true;
  1238.                 return true;
  1239.             }
  1240.             return false;
  1241.         }
  1242.         else
  1243.         {
  1244.             return super.DoJump(bUpdating);
  1245.         }
  1246.     }
  1247. }
  1248.  
  1249. /** Returns the Z force used for a jump. Different move/physics states can return different values. */
  1250. simulated function float GetJumpZ()
  1251. {
  1252.     return JumpZ;
  1253. }
  1254.  
  1255. /** Returns the sprint jump velocity, used in modifying jump mechanics while sprinting */
  1256. simulated function vector GetSprintJumpVelocity( vector ViewDirection )
  1257. {
  1258.     return ViewDirection * GetJumpZ() * GetDirectionalJumpScale();
  1259. }
  1260.  
  1261. /** Determines how much a directional jump's velocity is scaled */
  1262. simulated function float GetDirectionalJumpScale()
  1263. {
  1264.     return 1.f;
  1265. }
  1266.  
  1267. /** Overridden to access specific bumping damage types like the fleshpounds enraged hit */
  1268. function class<KFDamageType> GetBumpAttackDamageType();
  1269.  
  1270. /** Overridden to prevent falling damage for Zeds */
  1271. function TakeFallingDamage()
  1272. {
  1273. }
  1274.  
  1275. event Landed( vector HitNormal, Actor FloorActor )
  1276. {
  1277.     local Controller StoredLastHitBy;
  1278.     local int SMIndex;
  1279.  
  1280.     StoredLastHitBy = LastHitBy;
  1281.  
  1282.     if( bRestoreCollisionOnLand )
  1283.     {
  1284.         bRestoreCollisionOnLand = false;
  1285.         SetCollisionSize( default.CylinderComponent.CollisionRadius, default.CylinderComponent.CollisionHeight );
  1286.         FitCollision();
  1287.     }
  1288.  
  1289.     if( IsHumanControlled() )
  1290.     {
  1291.         if( bJumped && IsLocallyControlled() )
  1292.         {
  1293.             SMIndex = SpecialMoveCooldowns.Find( 'SMHandle', SM_Jump );
  1294.             if( SMIndex != INDEX_NONE )
  1295.             {
  1296.                 SpecialMoveCooldowns[SMIndex].LastUsedTime = WorldInfo.TimeSeconds;
  1297.             }
  1298.         }
  1299.  
  1300.         bJumped = false;
  1301.     }
  1302.  
  1303.     super.Landed( HitNormal, FloorActor );
  1304.  
  1305.     LastHitBy = StoredLastHitBy;
  1306. }
  1307.  
  1308. function SetMovementPhysics()
  1309. {
  1310.     // do not change when in special move - special move will reset when it's ready
  1311.     if( Physics == PHYS_None && IsDoingSpecialMove() )
  1312.          return;
  1313.  
  1314.     super.SetMovementPhysics();
  1315. }
  1316.  
  1317. /** Implements bKnockdownWhenJumpedOn */
  1318. function CrushedBy(Pawn OtherPawn)
  1319. {
  1320.     Super.CrushedBy(OtherPawn);
  1321.  
  1322.     if ( bKnockdownWhenJumpedOn
  1323.         // Still alive after crush damage
  1324.         && Health > 0
  1325.         // Not emerging
  1326.         && !IsDoingSpecialMove(SM_Emerge)
  1327.         // Actually above and not a side-swipe
  1328.         && ((OtherPawn.Location.Z - Location.Z) > (OtherPawn.CylinderComponent.CollisionHeight + CylinderCOmponent.CollisionHeight))
  1329.         // Opposing team; player only
  1330.         && !IsHumanControlled()
  1331.         && GetTeamNum() != OtherPawn.GetTeamNum()
  1332.         )
  1333.     {
  1334.         Knockdown(, vect(1,1,1), OtherPawn.Location, 1000, 100 );
  1335.     }
  1336. }
  1337.  
  1338. /** Sets the speed scale this zed will use in zed time */
  1339. simulated function SetZedTimeSpeedScale( float SpeedScale )
  1340. {
  1341.     if( SpeedScale > 0.f )
  1342.     {
  1343.         bMovesFastInZedTime = true;
  1344.         ZedTimeSpeedScale = SpeedScale;
  1345.     }
  1346.     else
  1347.     {
  1348.         bMovesFastInZedTime = false;
  1349.     }
  1350. }
  1351.  
  1352. /*********************************************************************************************
  1353.  * @name    Combat Methods
  1354. ********************************************************************************************* */
  1355.  
  1356. /** Override to handle cloaking */
  1357. native function bool IsValidEnemyTargetFor(const PlayerReplicationInfo PRI, bool bNoPRIisEnemy);
  1358.  
  1359. /** Is test location within charge range? */
  1360. native function bool InChargeRange( const Vector TestLocation );
  1361. /** Is test location within melee range? */
  1362. native function bool InMeleeRange( const Vector TestLocation, optional name AttackTag );
  1363. native function bool InAttackTagRange( const name AttackTag, const Vector TestLocation );
  1364. native function bool InAnyAttackTagRange( const Vector TestLocation, out name outAttackTag );
  1365. /** SERVER ONLY: Gets current enemy, to avoid having to get it from the controller */
  1366. native function KFPawn GetEnemy();
  1367. /** Checks to see if area between CheckLocation and pawn is clear for combat */
  1368. native final function bool IsLocationValidForCombat( KFPawn CheckPawn, const vector CheckLocation );
  1369.  
  1370. /** Used by subclass to implement rage mode */
  1371. simulated function bool SetEnraged(bool bNewEnraged)
  1372. {
  1373.     if (!bCanRage)
  1374.     {
  1375.         return false;
  1376.     }
  1377.  
  1378.     if (Role == ROLE_Authority)
  1379.     {
  1380.         if (bNewEnraged == bIsEnraged)
  1381.         {
  1382.             return false;
  1383.         }
  1384.  
  1385.         bIsEnraged = bNewEnraged;
  1386.  
  1387.         // End blocking on rage
  1388.         if (IsDoingSpecialMove(SM_Block))
  1389.         {
  1390.             EndSpecialMove();
  1391.         }
  1392.  
  1393.         // Sprint right away if we're AI
  1394.         if (!IsHumanControlled())
  1395.         {
  1396.             SetSprinting(bNewEnraged);
  1397.         }
  1398.     }
  1399.  
  1400.     if (WorldInfo.NetMode != NM_DedicatedServer && !IsHumanControlled())
  1401.     {
  1402.         if (bNewEnraged)
  1403.         {
  1404.             PlayExtraVFX('Enraged');
  1405.         }
  1406.         else
  1407.         {
  1408.             StopExtraVFX('Enraged');
  1409.         }
  1410.     }
  1411.  
  1412.     return true;
  1413. }
  1414.  
  1415. /** Used by subclass to check rage mode */
  1416. simulated event bool IsEnraged();
  1417.  
  1418. /** Notify the AI controller that we've taken damage from a friendly */
  1419. function NotifyFriendlyAIDamageTaken( Controller DamagerController, int Damage, Actor DamageCauser, class<KFDamageType> DamageType )
  1420. {
  1421.     if( MyKFAIC != none )
  1422.     {
  1423.         MyKFAIC.NotifyFriendlyAIDamageTaken( DamagerController, Damage, DamageCauser, DamageType );
  1424.     }
  1425. }
  1426.  
  1427. /** Notification called from Pawn animation, by KFAnimNotify_MeleeImpact.
  1428.  *  Network: Server Only
  1429.  */
  1430. simulated function MeleeImpactNotify(KFAnimNotify_MeleeImpact Notify)
  1431. {
  1432.     if ( MeleeAttackHelper != None )
  1433.     {
  1434.         MeleeAttackHelper.bHasAlreadyHit = false; // reset attack
  1435.         MeleeAttackHelper.MeleeImpactNotify(Notify);
  1436.     }
  1437.  
  1438.     // check to see if our enemy was killed
  1439.     if ( MyKFAIC != none && MyKFAIC.Enemy != None && MyKFAIC.Enemy.Health <= 0 )
  1440.     {
  1441.         ClearHeadTrackTarget(Controller.Enemy);
  1442.         // TODO: Below command aborts could be problematic, but this code is pretty old- keep an eye on it
  1443.         MyKFAIC.AbortCommand( None, class'AICommand_Attack_Grab' );
  1444.         MyKFAIC.AbortCommand( None, class'AICommand_Attack_Melee' );
  1445.         // Start up a taunt kill special move (used when enemy killed, as opposed to regular taunts)
  1446.         class'AICommand_TauntEnemy'.static.Taunt( KFAIController( Controller ),, TAUNT_EnemyKilled );
  1447.     }
  1448. }
  1449.  
  1450. /** Called from MeleeHelper when damage is done to another pawn */
  1451. function NotifyMeleeDamageDealt();
  1452.  
  1453. /** Am I within range to perform an attack in the AttackTag category? (see PawnAnimInfo) */
  1454. event bool IsInAttackTagRange( vector TestLocation, name AttackTag )
  1455. {
  1456.     local vector2D MinMaxRange;
  1457.     local float DistSq;
  1458.  
  1459.     if( MyKFAIC == none || PawnAnimInfo == none )
  1460.     {
  1461.         MinMaxRange = PawnAnimInfo.GetAttackRangeByName( AttackTag );
  1462.         DistSq = VSizeSq( TestLocation - Location );
  1463.  
  1464.         if( (DistSq > (MinMaxRange.X * MinMaxRange.X)) && (DistSq < (MinMaxRange.Y * MinMaxRange.Y)) )
  1465.         {
  1466.             return true;
  1467.         }
  1468.     }
  1469.     return false;
  1470. }
  1471.  
  1472. /** Am I within range to perform any attack in my PawnAnimInfo?  */
  1473. event bool IsInAnyAttackTagRange( vector TestLocation, out name outAttackTag )
  1474. {
  1475.     local int idx;
  1476.  
  1477.     if( MyKFAIC == none || PawnAnimInfo == none )
  1478.     {
  1479.         outAttackTag = '';
  1480.         return false;
  1481.     }
  1482.  
  1483.     for( idx = 0; idx < PawnAnimInfo.Attacks.Length; idx++ )
  1484.     {
  1485.         if( PawnAnimInfo.Attacks[idx].Tag == '' || PawnAnimInfo.Attacks[idx].Chance <= 0.f )
  1486.         {
  1487.             continue;
  1488.         }
  1489.         if( IsInAttackTagRange(TestLocation, PawnAnimInfo.Attacks[idx].Tag) )
  1490.         {
  1491.             outAttackTag = PawnAnimInfo.Attacks[idx].Tag;
  1492.             return true;
  1493.         }
  1494.     }
  1495.  
  1496.     outAttackTag = '';
  1497.     return false;
  1498. }
  1499.  
  1500. /**
  1501.  * Network: Local Player
  1502.  *
  1503.  * @param   FireModeNum     fire mode number
  1504.  */
  1505. simulated function StartPlayerZedMove(ESpecialMove Move)
  1506. {
  1507.     local byte SMFlags;
  1508.  
  1509.     if( Move != SM_None )
  1510.     {
  1511.         // Make sure attack is off cooldown
  1512.         if( GetSpecialMoveCooldownTimeRemaining(Move) > 0.f )
  1513.         {
  1514.             return;
  1515.         }
  1516.  
  1517.         // Tell our currently-running special move to retrigger the button press if it was released mid-move
  1518.         if( SpecialMove == Move )
  1519.         {
  1520.             SpecialMoves[SpecialMove].SpecialMoveButtonRetriggered();
  1521.         }
  1522.         else if( CanDoSpecialMove(Move) )
  1523.         {
  1524.             SMFlags = SpecialMoveHandler.SpecialMoveClasses[Move].static.PackFlagsBase(self);
  1525.             DoSpecialMove(Move, true, InteractionPawn, SMFlags);
  1526.             if ( Role < ROLE_Authority && IsDoingSpecialMove(Move) )
  1527.             {
  1528.                 ServerDoSpecialMove(Move, true, InteractionPawn, SMFlags);
  1529.             }
  1530.         }
  1531.     }
  1532. }
  1533.  
  1534. /**
  1535.  * Network: Local Player
  1536.  *
  1537.  * @param   FireModeNum     fire mode number
  1538.  */
  1539. simulated function StopPlayerZedMove(ESpecialMove Move)
  1540. {
  1541.     if( !IsDoingSpecialMove() )
  1542.     {
  1543.         return;
  1544.     }
  1545.  
  1546.     // notify move that button has been released (for held moves)
  1547.     if( SpecialMove == Move )
  1548.     {
  1549.         SpecialMoves[Move].SpecialMoveButtonReleased();
  1550.     }
  1551. }
  1552.  
  1553. /** Called from KFSpecialMove::SpecialMoveEnded */
  1554. simulated function NotifySpecialMoveEnded( KFSpecialMove FinishedMove, ESpecialMove SMHandle )
  1555. {
  1556.     local int SMIndex;
  1557.  
  1558.     if( IsHumanControlled() && IsLocallyControlled() )
  1559.     {
  1560.         SMIndex = SpecialMoveCooldowns.Find( 'SMHandle', SMHandle );
  1561.         if( SMIndex != INDEX_NONE )
  1562.         {
  1563.             SpecialMoveCooldowns[SMIndex].LastUsedTime = WorldInfo.TimeSeconds;
  1564.         }
  1565.     }
  1566. }
  1567.  
  1568. /** Returns cooldown time of a special move */
  1569. function float GetSpecialMoveCooldownPercent( const out SpecialMoveCooldownInfo Cooldown )
  1570. {
  1571.     local float CDTime;
  1572.  
  1573.     if( Cooldown.SMHandle != SM_None)
  1574.     {
  1575.         if( Cooldown.LastUsedTime > 0.f && Cooldown.CooldownTime > 0.f )
  1576.         {
  1577.             CDTime = (!bEmpDisrupted) ? Cooldown.CooldownTime : AfflictionHandler.GetAfflictionDuration(AF_EMP);
  1578.             return fClamp( (WorldInfo.TimeSeconds - Cooldown.LastUsedTime) / CDTime, 0.f, 1.f );
  1579.         }
  1580.     }
  1581.  
  1582.     return 1.f;
  1583. }
  1584.  
  1585. /** Returns amount of time remaining in cooldown */
  1586. function float GetSpecialMoveCooldownTimeRemaining( ESpecialMove SMHandle )
  1587. {
  1588.     local float CDTime;
  1589.     local int i;
  1590.  
  1591.     i = SpecialMoveCooldowns.Find( 'SMHandle', SMHandle );
  1592.     if( i != INDEX_NONE )
  1593.     {
  1594.         if( SpecialMoveCooldowns[i].LastUsedTime > 0.f && SpecialMoveCooldowns[i].CooldownTime > 0.f )
  1595.         {
  1596.             CDTime = (!bEmpDisrupted) ? SpecialMoveCooldowns[i].CooldownTime : AfflictionHandler.GetAfflictionDuration(AF_EMP);
  1597.             return CDTime - fMin( WorldInfo.TimeSeconds - SpecialMoveCooldowns[i].LastUsedTime, CDTime );
  1598.         }
  1599.  
  1600.         // if 1st use or no cooldown set return 0 remaining time
  1601.         return 0.f;
  1602.     }
  1603.  
  1604.     return 100.f; // invalid move
  1605. }
  1606.  
  1607. /** Allows access to the cooldowns array without being able to modify it */
  1608. simulated function array<SpecialMoveCooldownInfo> GetSpecialMoveCooldowns()
  1609. {
  1610.     return SpecialMoveCooldowns;
  1611. }
  1612.  
  1613. /** Puts all moves on this pawn on cooldown */
  1614. function PutAllMovesOnCooldown()
  1615. {
  1616.     local int i;
  1617.  
  1618.     if( IsHumanControlled() && IsLocallyControlled() )
  1619.     {
  1620.         for( i = 0; i < SpecialMoveCooldowns.Length; ++i )
  1621.         {
  1622.             if( SpecialMoveCooldowns[i].SMHandle != SM_None )
  1623.             {
  1624.                 SpecialMoveCooldowns[i].LastUsedTime = WorldInfo.TimeSeconds;
  1625.             }
  1626.         }
  1627.     }
  1628. }
  1629.  
  1630. /** Returns TRUE if we should use adjusted controller sensitivity */
  1631. simulated function bool UseAdjustedControllerSensitivity();
  1632.  
  1633. /** Returns TRUE if this zed can block attacks */
  1634. function bool CanBlock()
  1635. {
  1636.     // Make sure this zed is allowed to block first
  1637.     if( Physics == PHYS_Walking
  1638.             && CanDoSpecialMove(SM_Block)
  1639.             && IsCombatCapable() )
  1640.     {
  1641.         // Check chance
  1642.         if( fRand() > DifficultyBlockSettings.Chance * (WorldInfo.Game.NumPlayers == 1 ? DifficultyBlockSettings.SoloChanceMultiplier : 1.f) )
  1643.         {
  1644.             return false;
  1645.         }
  1646.  
  1647.         // Return cooldown status
  1648.         return `TimeSince(LastBlockTime) >= DifficultyBlockSettings.Cooldown;
  1649.     }
  1650.  
  1651.     return false;
  1652. }
  1653.  
  1654. /** Returns the block settings for the current difficulty */
  1655. simulated function SetBlockSettings( const sBlockInfo NewBlockSettings )
  1656. {
  1657.     DifficultyBlockSettings = NewBlockSettings;
  1658. }
  1659.  
  1660. /** Returns the block settings for the current difficulty */
  1661. simulated function sBlockInfo GetBlockSettings()
  1662. {
  1663.     return DifficultyBlockSettings;
  1664. }
  1665.  
  1666. /** Returns the minimum field of view required to detect a block */
  1667. simulated function float GetMinBlockFOV()
  1668. {
  1669.     return MinBlockFOV;
  1670. }
  1671.  
  1672. /** Reduce affliction power when blocking */
  1673. simulated function AdjustAffliction( out float AfflictionPower )
  1674. {
  1675.     if( Role == ROLE_Authority && bIsBlocking )
  1676.     {
  1677.         AfflictionPower *= DifficultyBlockSettings.AfflictionModifier;
  1678.     }
  1679.  
  1680.     super.AdjustAffliction( AfflictionPower );
  1681. }
  1682.  
  1683. /** Used by subclasses to determine if the boss icon can be rendered */
  1684. function bool ShouldDrawBossIcon()
  1685. {
  1686.     return false;
  1687. }
  1688.  
  1689. /*********************************************************************************************
  1690.  * @name    Damage/Death Methods
  1691. ********************************************************************************************* */
  1692.  
  1693. /*
  1694.  * PlayDying() is called upon death
  1695.  * Network: ALL
  1696.  */
  1697. simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
  1698. {
  1699.     local KFPlayerController KFPC;
  1700.     local string ClassName;
  1701.  
  1702.     Timer_EndRallyBoost();
  1703.  
  1704.     super.PlayDying(DamageType, HitLoc);
  1705.  
  1706.     //Shared functionality for boss death
  1707.     if (IsABoss() && class<KFGameInfo>(WorldInfo.GRI.GameClass).default.bGoToBossCameraOnDeath)
  1708.     {
  1709.         KFPC = KFPlayerController(GetALocalPlayerController());
  1710.         if(KFPC != none)
  1711.         {
  1712.             KFPC.SetBossCamera( self );
  1713.         }
  1714.  
  1715.         //@HSL_BEGIN - JRO - 5/17/2016 - PS4 Activity Feeds
  1716.         ClassName = string(Class.Name);
  1717.         ClassName -= '_Versus';
  1718.         class'GameEngine'.static.GetOnlineSubsystem().PlayerInterfaceEx.PostActivityFeedBossKill(GetLocalizedName(), ClassName, WorldInfo.GetMapName(true));
  1719.         //@HSL_END
  1720.     }
  1721.  
  1722.     if (bUseExplosiveDeath)
  1723.     {
  1724.         PlayExplosiveDeath();
  1725.     }
  1726.  
  1727.     if (bUseDamageInflation)
  1728.     {
  1729.         PlayInflationDeath();
  1730.     }
  1731.  
  1732.     UpdateBleedIncapFX();
  1733.  
  1734.     StopExtraVFX(`NAME_NONE);
  1735. }
  1736.  
  1737. simulated function PlayInflationDeath()
  1738. {
  1739.     local int Idx;
  1740.  
  1741.     for (Idx = 0; Idx < Mesh.PhysicsAssetInstance.Bodies.Length; ++Idx)
  1742.     {
  1743.         Mesh.PhysicsAssetInstance.Bodies[Idx].CustomGravityFactor = InflateDeathGravity;
  1744.  
  1745.         if (InflationExplosionTimer > 0)
  1746.         {
  1747.             SetTimer(InflationExplosionTimer, false, 'InflationExplode');
  1748.         }
  1749.     }
  1750. }
  1751.  
  1752. simulated function PlayExplosiveDeath()
  1753. {
  1754.     local KFGoreManager GoreManager;
  1755.  
  1756.     GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
  1757.     if (GoreManager != none && GoreManager.AllowMutilation())
  1758.     {
  1759.         HandlePartialGoreAndGibs(class'KFDT_Explosive_Sacrifice', Location, vect(0, 0, 0), 'hips', true);
  1760.         GoreManager.SpawnObliterationBloodEffect(self);
  1761.     }
  1762.     else
  1763.     {
  1764.         HandlePartialGoreAndGibs(class'KFDT_Explosive_Sacrifice', Location, vect(0, 0, 0), 'hips', false);
  1765.     }
  1766. }
  1767.  
  1768. simulated function InflationExplode()
  1769. {
  1770.     local int Idx;
  1771.  
  1772.     RepDamageInflateParam = 0.f;
  1773.     bUseDamageInflation = false;
  1774.  
  1775.     for (Idx = 0; Idx < Mesh.PhysicsAssetInstance.Bodies.Length; ++Idx)
  1776.     {
  1777.         Mesh.PhysicsAssetInstance.Bodies[Idx].CustomGravityFactor = 1.0;
  1778.     }
  1779.  
  1780.     PlayExplosiveDeath();
  1781.  
  1782.     if ( WorldInfo.NetMode != NM_DedicatedServer )
  1783.     {
  1784.         UpdateVisualInflation(GetCurrentInflation() * 2.0);
  1785.     }
  1786. }
  1787.  
  1788. State Dying
  1789. {
  1790.     event OnSleepRBPhysics()
  1791.     {
  1792.         local int i;
  1793.  
  1794.         // Shrink shadow cull distance when dead. We don't do this for characters because they
  1795.         // have extra cosmetics and custom head components. Must reattach component so render data gets updated.
  1796.         Mesh.PerObjectShadowCullDistance *= 0.6;
  1797.         ReattachComponent(Mesh);
  1798.  
  1799.         //Reattach monster PACs
  1800.         for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; ++i)
  1801.         {
  1802.             if (ThirdPersonAttachments[i] != None)
  1803.             {
  1804.                 ThirdPersonAttachments[i].PerObjectShadowCullDistance *= 0.6;
  1805.                 ReattachComponent(ThirdPersonAttachments[i]);
  1806.             }
  1807.         }
  1808.  
  1809.         Super.OnSleepRBPhysics();
  1810.     }
  1811.     event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
  1812.     {
  1813.         local KFPawn_Human KFPH;
  1814.         super.TakeDamage( Damage, InstigatedBy, HitLocation, Momentum, DamageType, HitInfo, DamageCauser );
  1815.  
  1816.         if( InstigatedBy != none && InstigatedBy.Pawn != none )
  1817.         {
  1818.             // beat dead horse
  1819.             KFPH = KFPawn_Human( InstigatedBy.Pawn );
  1820.             if( KFPH != none )
  1821.             {
  1822.                 `DialogManager.PlayBeatDeadHorseDialog( KFPH, self );
  1823.             }
  1824.         }
  1825.     }
  1826.  
  1827.     simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
  1828.     {
  1829.         local PlayerController PC;
  1830.         local matrix HeadMatrix;
  1831.         local vector HeadLoc;
  1832.         //local rotator HeadRot;
  1833.  
  1834.         PC = GetALocalPlayerController();
  1835.  
  1836.         if( PC != none && !PC.IsSpectating() && PC.ViewTarget == self )
  1837.         {
  1838.             HeadMatrix = Mesh.GetBoneMatrix( Mesh.MatchRefBone('head') );
  1839.             HeadLoc = MatrixGetOrigin( HeadMatrix );
  1840.             //HeadMatrix = MakeRotationMatrix( rot(0, -16383, 16383) ) * HeadMatrix;
  1841.             //HeadRot = MatrixGetRotator( HeadMatrix );
  1842.             //DrawDebugLine(HeadLoc, HeadLoc + 100*vector(HeadRot), 0, 255, 0);
  1843.  
  1844.             //out_CamLoc = VInterpTo( out_CamLoc, HeadLoc, fDeltaTime, 10.0 );
  1845.             out_CamRot = RInterpTo( out_CamRot, rotator(HeadLoc - out_CamLoc), fDeltaTime, 10.0 );
  1846.  
  1847.             return true;
  1848.         }
  1849.  
  1850.         return Global.CalcCamera( fDeltaTime, out_CamLoc, out_CamRot, out_FOV );
  1851.     }
  1852. };
  1853.  
  1854. /**
  1855.  * Don't allow headshots to be counted for any purpose if we were headless before entering the takedamage state
  1856.  * NOTE: This addresses an issue with low gore where the head is not hidden after a headless event, allowing headshots
  1857.  * to count when they shouldn't.
  1858.  */
  1859. function bool CanCountHeadshots()
  1860. {
  1861.     return !bIsHeadless;
  1862. }
  1863.  
  1864. event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
  1865. {
  1866.     local KFPawn_Human HumanInstigator;
  1867.     local KFAIController_Monster AIMonster;
  1868.     local class<KFDamageType> KFDT;
  1869.     local KFPlayerController KFPC;
  1870.     local KFPerk InstigatorPerk;
  1871.     local KfPlayerReplicationInfo KFPRI;
  1872.     local KFAIController KFAIC;
  1873.     local KFPawn_Monster KFPM;
  1874.     local float NapalmCheckDist;
  1875.  
  1876.     AIMonster = KFAIController_Monster(InstigatedBy);
  1877.     KFDT = class<KFDamageType>(DamageType);
  1878.     KFPC = KFPlayerController(InstigatedBy);
  1879.     if( KFPC != none )
  1880.     {
  1881.         InstigatorPerk = KFPC.GetPerk();
  1882.  
  1883.         KFAIC = KFAIController(Controller);
  1884.  
  1885.         if( KFAIC != none && KFAIC.TimeFirstSawPlayer == 0 )
  1886.         {
  1887.             // If we took damage, count that as "seeing a player" for our purposes
  1888.             KFAIC.TimeFirstSawPlayer = Worldinfo.TimeSeconds;
  1889.         }
  1890.     }
  1891.  
  1892.     if( AIMonster != none && KFDT != none && !KFDT.default.bIgnoreZedOnZedScaling )
  1893.     {
  1894.         // Deal extra damage to other zeds if we have over 100 health
  1895.         if( Health >= 100 )
  1896.         {
  1897.             Damage *= ( 2 - (HealthMax - Health ) / HealthMax );
  1898.         }
  1899.     }
  1900.  
  1901.     if( Damage > 0 && InstigatorPerk != none && KFDT != none )
  1902.     {
  1903.         bCouldTurnIntoShrapnel = InstigatorPerk.CouldBeZedShrapnel( KFDT );
  1904.     }
  1905.  
  1906.     super.TakeDamage( Damage, InstigatedBy, HitLocation, Momentum, DamageType, HitInfo, DamageCauser );
  1907.  
  1908.     if( InstigatedBy != none && InstigatedBy.Pawn != none )
  1909.     {
  1910.         HumanInstigator = KFPawn_Human( InstigatedBy.Pawn );
  1911.         if( HumanInstigator != none )
  1912.         {
  1913.             HumanInstigator.ResetIdleStartTime();
  1914.         }
  1915.     }
  1916.  
  1917.     if( Damage > 0
  1918.         && InstigatedBy != none
  1919.         && DamageCauser != none
  1920.         && KFDT != none
  1921.         && KFDT.default.DoT_Type == DOT_Fire
  1922.         && KFDT != class'KFDT_Fire_Napalm'
  1923.         && WorldInfo.RealTimeSeconds - LastNapalmInfectCheckTime > 0.25f )
  1924.     {
  1925.         if( KFPC != none
  1926.             && KFPC.GetPerk() != none
  1927.             && KFPC.GetPerk().CanSpreadNapalm()
  1928.             && class'KFPerk'.static.IsDamageTypeOnThisPerk(KFDT, class'KFPerk_Firebug') )
  1929.         {
  1930.             NapalmCheckDist = Square( CylinderComponent.CollisionRadius * class'KFPerk_Firebug'.static.GetNapalmCheckCollisionScale() );
  1931.             foreach WorldInfo.AllPawns( class'KFPawn_Monster', KFPM, Location )
  1932.             {
  1933.                 if( KFPM != self && KFPM.IsAliveAndWell() && !KFPM.IsNapalmInfected() )
  1934.                 {
  1935.                     if( VSizeSQ(Location - KFPM.Location) > NapalmCheckDist )
  1936.                     {
  1937.                         continue;
  1938.                     }
  1939.  
  1940.                     InfectWithNapalm( KFPM, KFPC );
  1941.                 }
  1942.             }
  1943.  
  1944.             LastNapalmInfectCheckTime = WorldInfo.RealTimeSeconds;
  1945.  
  1946.             // Make sure we're infected
  1947.             if( !IsNapalmInfected() )
  1948.             {
  1949.                 InfectWithNapalm( self, KFPC );
  1950.             }
  1951.         }
  1952.     }
  1953.  
  1954.     KFPRI = KFPlayerReplicationInfo( PlayerReplicationInfo );
  1955.     if( KFPRI != none )
  1956.     {
  1957.         KFPRI.PlayerHealth = Health;
  1958.         KFPRI.PlayerHealthPercent = FloatToByte( float(Health) / float(HealthMax) );
  1959.     }
  1960.  
  1961.     if (bUseDamageInflation)
  1962.     {
  1963.         IntendedDamageInflationPercent = float(Health) / float(HealthMax);
  1964.     }
  1965. }
  1966.  
  1967. /** Disable falling damage, apply blocking modifier */
  1968. function AdjustDamage(out int InDamage, out vector Momentum, Controller InstigatedBy, vector HitLocation, class<DamageType> DamageType, TraceHitInfo HitInfo, Actor DamageCauser)
  1969. {
  1970.     local KFPlayerController KFPC;
  1971.     local KFPawn_Human KFPH;
  1972.     local float TempDamage;
  1973.     local int HitZoneIdx;
  1974.     local int ExtraHeadDamage;
  1975.     local KFPerk InstigatorPerk;
  1976.     local class<KFDamageType> KFDT;
  1977.     local vector DamageSource;
  1978.  
  1979.     Super.AdjustDamage(InDamage, Momentum, InstigatedBy, HitLocation, DamageType, HitInfo, DamageCauser);
  1980.  
  1981.     if( DamageType.default.bCausedByWorld && ClassIsChildOf(DamageType, class'KFDT_Falling') )
  1982.     {
  1983.         InDamage = 0;
  1984.         return;
  1985.     }
  1986.  
  1987.     // start with damage type resistance/vulnerability
  1988.     InDamage *= GetDamageTypeModifier(DamageType);
  1989.  
  1990.     // Applies rally damage resistance
  1991.     GetRallyBoostResistance( InDamage );
  1992.  
  1993.     // Apply blocking modifier
  1994.     ApplyBlockingDamageModifier( InDamage, InstigatedBy, DamageType );
  1995.  
  1996.     // Cached hit params
  1997.     HitZoneIdx = HitZones.Find('ZoneName', HitInfo.BoneName);
  1998.  
  1999.     // Let the instigator's perk adjust the damage
  2000.     KFPC = KFPlayerController(InstigatedBy);
  2001.     if( KFPC != none )
  2002.     {
  2003.         InstigatorPerk = KFPC.GetPerk();
  2004.         if( InstigatorPerk != none )
  2005.         {
  2006.             InstigatorPerk.ModifyDamageGiven( InDamage, DamageCauser, self, KFPC, class<KFDamageType>(DamageType), HitZoneIdx );
  2007.         }
  2008.  
  2009.         if( KFPC.Pawn != none )
  2010.         {
  2011.             KFPH = KFPawn_Human(KFPC.Pawn);
  2012.             if( KFPH != none )
  2013.             {
  2014.                 TempDamage = InDamage;
  2015.                 TempDamage *= KFPH.GetHealingDamageBoostModifier();
  2016.                 InDamage = FCeil( TempDamage );
  2017.             }
  2018.         }
  2019.     }
  2020.  
  2021.     // NVCHANGE_BEGIN - RLS - Debugging Effects
  2022.     if( WorldInfo.Game != none && KFGameInfo(WorldInfo.Game).bNVAlwaysHeadshot )
  2023.     {
  2024.         HitZoneIdx = HZI_HEAD;
  2025.     }
  2026.     // NVCHANGE_BEGIN - RLS - Debugging Effects
  2027.  
  2028.     // Do extra damage on blowing the head off
  2029.     if( !bCheckingExtraHeadDamage && HitZoneIdx == HZI_Head &&
  2030.         HitZones[HZI_Head].GoreHealth > 0 && InDamage > HitZones[HZI_Head].GoreHealth )
  2031.     {
  2032.         KFDT = class<KFDamageType>(DamageType);
  2033.  
  2034.         // For certain weapons do even more damage on blowing the head off
  2035.         if( KFDT != none )
  2036.         {
  2037.             InDamage *= KFDT.default.HeadDestructionDamageScale;
  2038.         }
  2039.  
  2040.         ExtraHeadDamage = InDamage + HealthMax * 0.25;
  2041.  
  2042.         bCheckingExtraHeadDamage = true;
  2043.         AdjustDamage( ExtraHeadDamage, Momentum, InstigatedBy, HitLocation, DamageType, HitInfo, DamageCauser );
  2044.         bCheckingExtraHeadDamage = false;
  2045.  
  2046.         InDamage += ExtraHeadDamage;
  2047.     }
  2048.  
  2049.     // register damage to divide up money/XP/Score.  This is done here instead of NotifyTakeHit,
  2050.     // so that it's called on the killing blow
  2051.     if( !bCheckingExtraHeadDamage && InstigatedBy != none )
  2052.     {
  2053.         AddTakenDamage( InstigatedBy, FMin(Health, InDamage), DamageCauser, class<KFDamageType>(DamageType) );
  2054.     }
  2055.  
  2056.     // If the head has been dismembered reduce damage to 1 (non-zero so play hit is called).
  2057.     // Since zed is bleeding out already this is not as big of a issue.
  2058.     if ( HitZoneIdx == HZI_HEAD && bIsHeadless )
  2059.     {
  2060.         InDamage = 1;
  2061.     }
  2062.  
  2063.     // Never allow damage to reach zero if coming from opposing team
  2064.     if( InstigatedBy == none || InstigatedBy.GetTeamNum() != GetTeamNum() )
  2065.     {
  2066.         InDamage = Max( InDamage, 1 );
  2067.     }
  2068.  
  2069.     if (ArmorInfo != none)
  2070.     {
  2071.         //If the damage doesn't have a bone hit source, it's likely AoE.  Split over all remaining armor evenly.
  2072.         if (HitInfo.BoneName != '')
  2073.         {
  2074.             if (InstigatedBy != none && InstigatedBy.Pawn != none)
  2075.             {
  2076.                 DamageSource = InstigatedBy.Pawn.Location;
  2077.             }
  2078.             else if (KFWeapon(DamageCauser) != none)
  2079.             {
  2080.                 DamageSource = KFWeapon(DamageCauser).GetMuzzleLoc();
  2081.             }
  2082.             else
  2083.             {
  2084.                 DamageSource = DamageCauser.Location;
  2085.             }
  2086.  
  2087.             ArmorInfo.AdjustBoneDamage(InDamage, HitInfo.BoneName, DamageSource);
  2088.         }
  2089.         else
  2090.         {
  2091.             ArmorInfo.AdjustNonBoneDamage(InDamage);
  2092.         }
  2093.  
  2094.         `log(self @ GetFuncName() @ " After armor adjustment Damage=" $ InDamage @ "Momentum=" $ Momentum @ "Zone=" $ HitInfo.BoneName @ "DamageType=" $ DamageType, bLogTakeDamage);
  2095.     }
  2096.  
  2097.     `log(self @ "Adjusted Monster Damage=" $ InDamage, bLogTakeDamage);
  2098. }
  2099.  
  2100. /** Returns damage multiplier for an incoming damage type @todo: c++?*/
  2101. function float GetDamageTypeModifier(class<DamageType> DT)
  2102. {
  2103.     local int i;
  2104.     local int DifficultyIdx;
  2105.     local float DamageModifier;
  2106.  
  2107.     if ( LiveDamageTypeModifiers.Length > 0 )
  2108.     {
  2109.         AppendLiveDamageTypeModifiers();
  2110.     }
  2111.  
  2112.     // reverse iteration so that most specific damage type can be last on the array
  2113.     for (i = DamageTypeModifiers.Length - 1; i >= 0; --i)
  2114.     {
  2115.         if ( ClassIsChildOf(DT, DamageTypeModifiers[i].DamageType) )
  2116.         {
  2117.             // If specified assign difficulty based value
  2118.             if ( WorldInfo.Game != None && DamageTypeModifiers[i].DamageScale.Length > 1 )
  2119.             {
  2120.                 DifficultyIdx = Min(WorldInfo.Game.GetModifiedGameDifficulty(), DamageTypeModifiers[i].DamageScale.Length);
  2121.                 DamageModifier = DamageTypeModifiers[i].DamageScale[DifficultyIdx];
  2122.             }
  2123.             else
  2124.             {
  2125.                 DamageModifier = DamageTypeModifiers[i].DamageScale[0];
  2126.             }
  2127.  
  2128.             // for resistances only, allow game info to modify (e.g. num players)
  2129.             if ( DamageModifier < 1.f )
  2130.             {
  2131.                 DamageModifier = FMax(Lerp(1.f, DamageModifier, GameResistancePct),  0.f);
  2132.             }
  2133.  
  2134.             `log(self@"Scaling damage taken from"@DT@"by"@DamageModifier, bLogTakeDamage);
  2135.             return DamageModifier;
  2136.         }
  2137.     }
  2138.  
  2139.     return 1.f;
  2140. }
  2141.  
  2142. /** Append (one-time) damage type modifiers from the live balance into the damage modifiers array */
  2143. function AppendLiveDamageTypeModifiers()
  2144. {
  2145.     local int i;
  2146.  
  2147.     for (i = 0; i < LiveDamageTypeModifiers.Length; i++)
  2148.     {
  2149.         if ( LiveDamageTypeModifiers[i].DamageType != None )
  2150.         {
  2151.             DamageTypeModifiers.AddItem(LiveDamageTypeModifiers[i]);
  2152.         }
  2153.         else
  2154.         {
  2155.             break; // finished once we find an empty entry
  2156.         }
  2157.     }
  2158.  
  2159.     LiveDamageTypeModifiers.Length = 0;
  2160. }
  2161.  
  2162. /** Applies the blocking damage modifier to incoming damage */
  2163. function ApplyBlockingDamageModifier( out int Damage, Controller InstigatedBy, class<DamageType> damageType )
  2164. {
  2165.     local vector DamageDirNormal;
  2166.  
  2167.     if( !bIsBlocking || Damage <= 0 || InstigatedBy == none || InstigatedBy.Pawn == none || InstigatedBy.GetTeamNum() == GetTeamNum() )
  2168.     {
  2169.         return;
  2170.     }
  2171.  
  2172.     // Only block attacks that come from the front
  2173.     DamageDirNormal = Normal( InstigatedBy.Pawn.Location - Location );
  2174.     if( DamageDirNormal dot vector(Rotation) > MinBlockFOV )
  2175.     {
  2176.         // Melee damage first
  2177.         if( ClassIsChildOf(damageType, class'KFDT_Bludgeon') || ClassIsChildOf(damageType, class'KFDT_Slashing') )
  2178.         {
  2179.             Damage = int( float(Damage) * DifficultyBlockSettings.MeleeDamageModifier );
  2180.         }
  2181.         else
  2182.         {
  2183.             Damage = int( float(Damage) * DifficultyBlockSettings.DamageModifier );
  2184.         }
  2185.     }
  2186. }
  2187.  
  2188. event bool HealDamage(int Amount, Controller Healer, class<DamageType> DamageType, optional bool bRepairArmor=true, optional bool bMessageHealer=true)
  2189. {
  2190.     `DialogManager.PlaySpotZedHealingDialog( self );
  2191.     Super.HealDamage(Amount, Healer, DamageType);
  2192.     return true;
  2193. }
  2194.  
  2195. /** Is this monster effected by a melee damage debuff? */
  2196. function bool HasReducedMeleeDamage()
  2197. {
  2198.     return bHasReducedMeleeDamage;
  2199. }
  2200.  
  2201. /** sends any notifications to anything that needs to know this pawn has taken damage */
  2202. function NotifyTakeHit(Controller InstigatedBy, vector HitLocation, int Damage, class<DamageType> DamageType, vector Momentum, Actor DamageCauser)
  2203. {
  2204.     local KFPawn_Human KFPH_Instigator;
  2205.  
  2206.     Super.NotifyTakeHit(InstigatedBy, HitLocation, Damage, DamageType, Momentum, DamageCauser);
  2207.  
  2208.     // continuous damage check needs to happen before we set LastPainTime and LastHitBy for current hit
  2209.     if( InstigatedBy != none && InstigatedBy.Pawn != none )
  2210.     {
  2211.         KFPH_Instigator = KFPawn_Human( InstigatedBy.Pawn );
  2212.         if( KFPH_Instigator != none )
  2213.         {
  2214.             `DialogManager.PlayDamageZedContinuousDialog( KFPH_Instigator, self );
  2215.         }
  2216.     }
  2217.  
  2218.     // Allow special move to react to TakeHit/TakeDamage
  2219.     if( SpecialMove != SM_None )
  2220.     {
  2221.         SpecialMoves[SpecialMove].NotifyOwnerTakeHit(class<KFDamageType>(damageType), HitLocation, Normal(Momentum), InstigatedBy);
  2222.     }
  2223.  
  2224.     `AILog_Ext(GetFuncName()$"() Instigator:"$InstigatedBy$" DT: "$DamageType, 'Damage', MyKFAIC);
  2225. }
  2226.  
  2227. function PlayHit(float Damage, Controller InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, TraceHitInfo HitInfo)
  2228. {
  2229.     local KFPawn_Human KFPH_Instigator;
  2230.  
  2231.     super.PlayHit( Damage, InstigatedBy, HitLocation, damageType, Momentum, HitInfo );
  2232.  
  2233.     // play damage zed dialog after Afflictions happen in super
  2234.     if( InstigatedBy != none && InstigatedBy.Pawn != none )
  2235.     {
  2236.         KFPH_Instigator = KFPawn_Human( InstigatedBy.Pawn );
  2237.         if( KFPH_Instigator != none )
  2238.         {
  2239.             `DialogManager.PlayDamagedZedDialog( KFPH_Instigator, self, DamageType );
  2240.         }
  2241.     }
  2242. }
  2243.  
  2244. /** Called before, and in addition to, NotifyTakeHit(), but processes melee specifically (Server only) */
  2245. function NotifyMeleeTakeHit(Controller InstigatedBy, vector HitLocation);
  2246.  
  2247. /** Delayed death from lethal damage */
  2248. function BleedOutTimer()
  2249. {
  2250.     if ( !bPlayedDeath )
  2251.     {
  2252.         `log(GetFuncName() @ "LastHitBy" @ LastHitBy, bLogTakeDamage);
  2253.         Died(LastHitBy, class'KFDT_Bleeding', Location);
  2254.     }
  2255. }
  2256.  
  2257. /** Applies the rally buff and spawns a rally effect */
  2258. simulated function bool Rally(
  2259.                             KFPawn          RallyInstigator,
  2260.                             ParticleSystem  RallyEffect,
  2261.                             name            EffectBoneName,
  2262.                             vector          EffectOffset,
  2263.                             ParticleSystem  AltRallyEffect,
  2264.                             name            AltEffectBoneNames[2],
  2265.                             vector          AltEffectOffset,
  2266.                             optional bool   bSkipEffects=false
  2267.                         )
  2268. {
  2269.     local sRallyInfo RallyInfo;
  2270.     local KFAIController KFAIC;
  2271.     local bool bStartedBoostRally;
  2272.  
  2273.     local Name SocketBoneName;
  2274.     local Name AltSocketBoneName;
  2275.  
  2276.     GetDifficultyRallyInfo( RallyInfo );
  2277.  
  2278.     if( !RallyInfo.bCanRally || !IsAliveAndWell() )
  2279.     {
  2280.         return false;
  2281.     }
  2282.  
  2283.     if( RallyInfo.DealtDamageModifier > 1.f )
  2284.     {
  2285.         if( !IsTimerActive(nameOf(Timer_EndRallyBoost)) )
  2286.         {
  2287.             SetTimer( RallyInfo.RallyBuffTime, false, nameOf(Timer_EndRallyBoost) );
  2288.             bStartedBoostRally = true;
  2289.         }
  2290.     }
  2291.  
  2292.     if( Role == ROLE_Authority && Controller != none && RallyInfo.bCauseSprint )
  2293.     {
  2294.         KFAIC = KFAIController( Controller );
  2295.  
  2296.         KFAIC.SetSprintingDisabled( false );
  2297.         KFAIC.SetCanSprint( true );
  2298.         KFAIC.bDefaultCanSprint = true;
  2299.         KFAIC.bCanSprintWhenDamaged = true;
  2300.         KFAIC.bForceFrustration = true;
  2301.  
  2302.         // Trigger sprint/rage immediately
  2303.         SetSprinting( true );
  2304.         SetEnraged( true );
  2305.  
  2306.         if (KFGameInfo(WorldInfo.Game) != none)
  2307.         {
  2308.             KFGameInfo(WorldInfo.Game).NotifyRally(self);
  2309.         }
  2310.     }
  2311.  
  2312.     if( !bSkipEffects && WorldInfo.NetMode != NM_DedicatedServer )
  2313.     {
  2314.         if( RallyPSC != none )
  2315.         {
  2316.             RallyPSC.DeactivateSystem();
  2317.             DetachComponent( RallyPSC );
  2318.         }
  2319.  
  2320.         RallyPSC = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( RallyEffect, Mesh, EffectBoneName, false, EffectOffset );
  2321.  
  2322.         // Spawn player rally particle systems and attach them
  2323.         if( bStartedBoostRally )
  2324.         {
  2325.             SocketBoneName = Mesh.GetSocketBoneName(AltEffectBoneNames[0]);
  2326.             if (SocketBoneName != '' && SocketBoneName != 'None')
  2327.             {
  2328.                 RallyHandPSCs[0] = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( AltRallyEffect, Mesh, AltEffectBoneNames[0], true, AltEffectOffset );
  2329.             }
  2330.  
  2331.             AltSocketBoneName = Mesh.GetSocketBoneName(AltEffectBoneNames[1]);
  2332.             if (AltSocketBoneName != '' && AltSocketBoneName != 'None')
  2333.             {
  2334.                 RallyHandPSCs[1] = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( AltRallyEffect, Mesh, AltEffectBoneNames[1], true, AltEffectOffset );
  2335.             }
  2336.         }
  2337.     }
  2338.  
  2339.     return true;
  2340. }
  2341.  
  2342. /** Ends the rally boost and cancels effects */
  2343. simulated function Timer_EndRallyBoost()
  2344. {
  2345.     if( RallyHandPSCs[0] != none && RallyHandPSCs[0].bIsActive )
  2346.     {
  2347.         RallyHandPSCs[0].DeactivateSystem();
  2348.     }
  2349.     if( RallyHandPSCs[1] != none && RallyHandPSCs[1].bIsActive )
  2350.     {
  2351.         RallyHandPSCs[1].DeactivateSystem();
  2352.     }
  2353. }
  2354.  
  2355. /** Sets the difficulty-based rally settings for this pawn */
  2356. simulated function SetRallySettings( sRallyInfo NewRallyInfo )
  2357. {
  2358.     DifficultyRallySettings = NewRallyInfo;
  2359. }
  2360.  
  2361. /** Returns the difficulty-based rally settings */
  2362. simulated function GetDifficultyRallyInfo( out sRallyInfo RallyInfo )
  2363. {
  2364.     RallyInfo = DifficultyRallySettings;
  2365. }
  2366.  
  2367. /** Applies the rally damage boost if applicable */
  2368. simulated function int GetRallyBoostDamage( int NewDamage )
  2369. {
  2370.     local sRallyInfo RallyInfo;
  2371.  
  2372.     GetDifficultyRallyInfo( RallyInfo );
  2373.     return NewDamage * ( IsTimerActive(nameOf(Timer_EndRallyBoost)) ? RallyInfo.DealtDamageModifier : 1.f );
  2374. }
  2375.  
  2376. /** Applies the rally damage reduction if applicable */
  2377. simulated function int GetRallyBoostResistance( int NewDamage )
  2378. {
  2379.     local sRallyInfo RallyInfo;
  2380.  
  2381.     GetDifficultyRallyInfo( RallyInfo );
  2382.     return NewDamage * ( IsTimerActive(nameOf(Timer_EndRallyBoost)) ? RallyInfo.TakenDamageModifier : 1.f );
  2383. }
  2384.  
  2385. function bool Died(Controller Killer, class<DamageType> DamageType, vector HitLocation)
  2386. {
  2387.     local KFPlayerController KFPC;
  2388.     local KFPerk InstigatorPerk;
  2389.  
  2390.     if ( super.Died(Killer, damageType, HitLocation) )
  2391.     {
  2392.         if( Killer != none && Killer.Pawn != none && KFPawn_Human(Killer.Pawn) != none )
  2393.         {
  2394.             `DialogManager.PlayKilledZedDialog( KFPawn_Human(Killer.Pawn), self, DamageType, IsDoingSpecialMove(SM_Knockdown) || IsDoingSpecialMove(SM_RecoverFromRagdoll) );
  2395.         }
  2396.  
  2397.         if( bCouldTurnIntoShrapnel )
  2398.         {
  2399.             if( Killer != none )
  2400.             {
  2401.                 KFPC = KFPlayerController(Killer);
  2402.                 if( KFPC != none )
  2403.                 {
  2404.                     InstigatorPerk = KFPC.GetPerk();
  2405.                     if( InstigatorPerk != none && InstigatorPerk.ShouldShrapnel() )
  2406.                     {
  2407.                         ShrapnelExplode( Killer, InstigatorPerk );
  2408.                     }
  2409.                 }
  2410.             }
  2411.         }
  2412.  
  2413.         OnZedDied(Killer);
  2414.  
  2415.         return true;
  2416.     }
  2417.  
  2418.     return false;
  2419. }
  2420.  
  2421. /** Handle any subclassed functionality for when a zed dies */
  2422. function OnZedDied(Controller Killer);
  2423.  
  2424. /** Handle this pawn being destroyed
  2425. */
  2426. simulated event Destroyed()
  2427. {
  2428.     // Kill any affliction sounds/effects when this pawn is destroyed
  2429.     AfflictionHandler.Shutdown();
  2430.     super.Destroyed();
  2431. }
  2432.  
  2433. event OnRigidBodyLinearConstraintViolated(name StretchedBoneName)
  2434. {
  2435.     local KFGoreManager GoreManager;
  2436.  
  2437.     `log("Linear constraint violated, hiding bone " @ StretchedBoneName);
  2438.  
  2439.     GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
  2440.     if( GoreManager != none && GoreManager.AllowMutilation() )
  2441.     {
  2442.         if( !bIsGoreMesh )
  2443.         {
  2444.             SwitchToGoreMesh();
  2445.         }
  2446.  
  2447.         if( bIsGoreMesh )
  2448.         {
  2449.             GoreManager.CrushBone( self, StretchedBoneName );
  2450.             return;
  2451.         }
  2452.     }
  2453.  
  2454.     // no gore fallback
  2455.     mesh.HideBoneByName(StretchedBoneName, PBO_Term);
  2456. }
  2457.  
  2458. protected simulated function ResetHealthVisibilty()
  2459. {
  2460.     bShowHealth = false;
  2461. }
  2462.  
  2463. simulated function bool CanShowHealth()
  2464. {
  2465.     return bShowHealth;
  2466. }
  2467.  
  2468. simulated function bool IsNapalmInfected()
  2469. {
  2470.     return DamageOverTimeArray.Find('DamageType', class'KFDT_Fire_Napalm') != INDEX_NONE;
  2471. }
  2472.  
  2473. function bool CanNapalmInfect( out KFPlayerController NapalmInstigator )
  2474. {
  2475.     local int DoTIndex;
  2476.     local KFPlayerController KFPC;
  2477.     local KFPerk InstigatorPerk;
  2478.  
  2479.     if( DamageOverTimeArray.Length > 0 )
  2480.     {
  2481.         DoTIndex = DamageOverTimeArray.Find( 'DoT_Type', class'KFDT_Fire'.default.DoT_Type );
  2482.         if( DoTIndex != INDEX_NONE && DamageOverTimeArray[DoTIndex].InstigatedBy != none )
  2483.         {
  2484.             KFPC = KFPlayerController( DamageOverTimeArray[DoTIndex].InstigatedBy );
  2485.             if( KFPC != none )
  2486.             {
  2487.                 InstigatorPerk = KFPC.GetPerk();
  2488.                 if( InstigatorPerk != none && InstigatorPerk.CanSpreadNapalm() )
  2489.                 {
  2490.                     NapalmInstigator = KFPC;
  2491.                     return true;
  2492.                 }
  2493.             }
  2494.         }
  2495.     }
  2496.  
  2497.     NapalmInstigator = none;
  2498.     return false;
  2499. }
  2500.  
  2501. function InfectWithNapalm( KFPawn_Monster KFPM, KFPlayerController KFPC )
  2502. {
  2503.     if( KFPC != none )
  2504.     {
  2505.         KFPM.TakeDamage( class'KFPerk_Firebug'.static.GetNapalmDamage(),
  2506.                          KFPC,
  2507.                          vect(0,0,0),
  2508.                          vect(0,0,0),
  2509.                          class'KFDT_Fire_Napalm',,
  2510.                          KFPC );
  2511.     }
  2512. }
  2513.  
  2514. /**
  2515.  * @brief Spawns a radioactive cloud that hurts other Zeds
  2516.  *
  2517.  * @param Killer The monster's killer (that had the shrapnel skill enabled)
  2518.  */
  2519. function ShrapnelExplode( Controller Killer, KFPerk InstigatorPerk )
  2520. {
  2521.     local KFExplosionActorReplicated ExploActor;
  2522.     local Actor InstigatorActor;
  2523.     local GameExplosion ExplosionTemplate;
  2524.  
  2525.     if ( Role < ROLE_Authority )
  2526.     {
  2527.         return;
  2528.     }
  2529.  
  2530.     InstigatorActor = Killer.Pawn != none ? Killer.Pawn : Killer;
  2531.  
  2532.     // explode using the given template
  2533.     ExploActor = Spawn(class'KFExplosionActorReplicated', InstigatorActor,, Location,,, true);
  2534.     if( ExploActor != None )
  2535.     {
  2536.         ExploActor.InstigatorController = Killer;
  2537.  
  2538.         if( Killer.Pawn != none )
  2539.         {
  2540.             ExploActor.Instigator = Killer.Pawn;
  2541.         }
  2542.  
  2543.         if( InstigatorPerk != none )
  2544.         {
  2545.             ExplosionTemplate = InstigatorPerk.GetExplosionTemplate();
  2546.             ExploActor.Explode( ExplosionTemplate );
  2547.         }
  2548.     }
  2549. }
  2550.  
  2551. /*********************************************************************************************
  2552.  * @name    Injuries & Status Effects
  2553. ********************************************************************************************* */
  2554.  
  2555. /** Called to set any visual inflation that needs to occur */
  2556. simulated function UpdateVisualInflation(float InflationAmount)
  2557. {
  2558.     local MaterialInstanceConstant MIC;
  2559.     local int i, j;
  2560.  
  2561.     //Turn off inflation entirely if we're in gore mode
  2562.     if (bIsGoreMesh)
  2563.     {
  2564.         InflationAmount = 0.f;
  2565.     }
  2566.  
  2567.     //Base character MICs
  2568.     foreach CharacterMICs(MIC)
  2569.     {
  2570.         MIC.SetScalarParameterValue('Scalar_Inflate', InflationAmount);
  2571.     }
  2572.  
  2573.     //Update MICs in any active PACs
  2574.     for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; ++i)
  2575.     {
  2576.         if (ThirdPersonAttachments[i] != none)
  2577.         {
  2578.             for (j = 0; j < ThirdPersonAttachments[i].Materials.Length; ++j)
  2579.             {
  2580.                 MIC = MaterialInstanceConstant(ThirdPersonAttachments[i].GetMaterial(j));
  2581.                 if (MIC != none)
  2582.                 {
  2583.                     MIC.SetScalarParameterValue('Scalar_Inflate', InflationAmount);
  2584.                 }
  2585.             }
  2586.         }
  2587.     }
  2588.  
  2589.     //Update MICs in any active static attachments
  2590.     for (i = 0; i < StaticAttachList.Length; ++i)
  2591.     {
  2592.         if (StaticAttachList[i] != none)
  2593.         {
  2594.             for (j = 0; j < StaticAttachList[i].Materials.Length; ++j)
  2595.             {
  2596.                 MIC = MaterialInstanceConstant(StaticAttachList[i].GetMaterial(j));
  2597.                 if (MIC != none)
  2598.                 {
  2599.                     MIC.SetScalarParameterValue('Scalar_Inflate', InflationAmount);
  2600.                 }
  2601.             }
  2602.         }
  2603.     }
  2604. }
  2605.  
  2606. /** Called on server when pawn should has been crippled (e.g. Headless) */
  2607. function CauseHeadTrauma(float BleedOutTime=5.f)
  2608. {
  2609.     if ( !bIsHeadless && !bPlayedDeath && !bDisableHeadless )
  2610.     {
  2611.         if( MyKFAIC != none && KFGameInfo(WorldInfo.Game) != none && MyKFAIC.TimeFirstSawPlayer >= 0 )
  2612.         {
  2613.             KFGameInfo(WorldInfo.Game).GameConductor.HandleZedKill( FMax(`TimeSince(MyKFAIC.TimeFirstSawPlayer),0.0));
  2614.             // Set this so we know we already logged a kill for our pawn
  2615.             MyKFAIC.TimeFirstSawPlayer = -1;
  2616.         }
  2617.  
  2618.         bPlayShambling = true;
  2619.         bIsHeadless = true;
  2620.  
  2621.         if( MyKFAIC != none )
  2622.         {
  2623.             MyKFAIC.SetSprintingDisabled(true);
  2624.         }
  2625.  
  2626.         // No more auto aiming to this zed
  2627.         bCanBeAdheredTo=false;
  2628.         bCanBeFrictionedTo=false;
  2629.  
  2630.         StopAkEventsOnBone( 'head' );
  2631.  
  2632.         // insti-kill while doing a root motion SM (uninterruptable)
  2633.         if ( IsDoingSpecialMove() && Mesh.RootMotionMode == RMM_Accel )
  2634.         {
  2635.             Died(LastHitBy, class'DamageType', Location);
  2636.         }
  2637.         // initiate ragdoll knockdown while sprinting
  2638.         else if ( bIsSprinting && FRand() < 0.25f )
  2639.         {
  2640.             Knockdown(Velocity, vect(1,1,1));
  2641.         }
  2642.  
  2643.         // initiate the "headless wander" AICommand
  2644.         if( IsAliveAndWell() && MyKFAIC != none )
  2645.         {
  2646.             // Only allow headless wander if were doing an SM that allows a wander interupt
  2647.             // otherwise wait until the end of the move
  2648.             if ( SpecialMove == SM_None || !SpecialMoves[SpecialMove].bCanOnlyWanderAtEnd )
  2649.             {
  2650.                 MyKFAIC.DoHeadlessWander();
  2651.             }
  2652.         }
  2653.  
  2654.         if ( BleedOutTime > 0 )
  2655.         {
  2656.             SetTimer(BleedOutTime, false, nameof(BleedOutTimer));
  2657.         }
  2658.     }
  2659. }
  2660.  
  2661. /**
  2662. *  SERVER ONLY - Update any AI or animation behaviors based on state
  2663. */
  2664. function OnStackingAfflictionChanged(byte Id)
  2665. {
  2666.     local bool bShouldBeWandering, bWasWandering;
  2667.  
  2668.     bWasWandering = (bPlayShambling || bPlayPanicked);
  2669.  
  2670.     bPlayShambling = bEmpPanicked || bIsHeadless;
  2671.     bPlayPanicked = bFirePanicked || bIsPoisoned || bMicrowavePanicked;
  2672.  
  2673.     bShouldBeWandering = (bPlayShambling || bPlayPanicked);
  2674.  
  2675.     if( !bWasWandering && bShouldBeWandering )
  2676.     {
  2677.         CausePanicWander();
  2678.     }
  2679.     else if( bWasWandering )
  2680.     {
  2681.         EndPanicWander();
  2682.     }
  2683. }
  2684.  
  2685. /** Called on server when pawn should do EMP Wandering */
  2686. function CausePanicWander()
  2687. {
  2688.     if ( !bIsHeadless && !bPlayedDeath )
  2689.     {
  2690.         // if bCanOnlyWanderAtEnd is true, do not begin to wander until the current special move has ended
  2691.         if ( SpecialMove != SM_None && SpecialMoves[SpecialMove].bCanOnlyWanderAtEnd )
  2692.         {
  2693.             return;
  2694.         }
  2695.  
  2696.         if( MyKFAIC != none )
  2697.         {
  2698.             MyKFAIC.SetSprintingDisabled(true);
  2699.         }
  2700.  
  2701.         // initiate the "EMP wander" AICommand
  2702.         if( IsAliveAndWell() && MyKFAIC != none )
  2703.         {
  2704.             MyKFAIC.DoPanicWander();
  2705.         }
  2706.     }
  2707. }
  2708.  
  2709. /** Called on server when pawn should stop doing EMP Wandering */
  2710. function EndPanicWander()
  2711. {
  2712.     if( MyKFAIC != none )
  2713.     {
  2714.         MyKFAIC.SetSprintingDisabled(false);
  2715.     }
  2716.  
  2717.     // stop the "EMP wander" AICommand
  2718.     if( IsAliveAndWell() && MyKFAIC != none )
  2719.     {
  2720.         MyKFAIC.EndPanicWander();
  2721.     }
  2722. }
  2723.  
  2724. /** Returns true if a stacking incap is set that will cause a zed to wander */
  2725. simulated function bool ShouldBeWandering()
  2726. {
  2727.     return bPlayShambling || bPlayPanicked;
  2728. }
  2729.  
  2730. /** Returns true if a stacking incap is set that will cause a zed to wander */
  2731. simulated function bool IsHeadless()
  2732. {
  2733.     return bIsHeadless;
  2734. }
  2735.  
  2736. /** Returns true if a stacking incap is set that will prevent or interrupt a
  2737. * zed from using its special abilities (Ex Husk Flamethrower) */
  2738. simulated function bool IsImpaired()
  2739. {
  2740.     return bPlayPanicked || bEmpDisrupted || (bPlayShambling && !bIsHeadless);
  2741. }
  2742.  
  2743. /** Reapply active gameplay related MIC params (e.g. when switching to the gore mesh) */
  2744. simulated function UpdateGameplayMICParams()
  2745. {
  2746.     if ( AfflictionHandler != None )
  2747.     {
  2748.         AfflictionHandler.ToggleEffects(AF_Poison, bIsPoisoned);
  2749.  
  2750.         // Turn off inflated if we are using the gore mic
  2751.         if( bIsGoreMesh )
  2752.         {
  2753.             AfflictionHandler.UpdateMaterialParameter(AF_Microwave, 0.f);
  2754.         }
  2755.     }
  2756.     super.UpdateGameplayMICParams();
  2757. }
  2758.  
  2759. /** Called when a melee attack has been parried by another pawn */
  2760. function bool NotifyAttackParried(Pawn InstigatedBy, byte InParryStrength)
  2761. {
  2762.     if ( InParryStrength < ParryResistance  )
  2763.     {
  2764.         return FALSE; // resisted
  2765.     }
  2766.  
  2767.     return Super.NotifyAttackParried(InstigatedBy, InParryStrength);
  2768. }
  2769.  
  2770. /*********************************************************************************************
  2771.  * @name    Special Moves
  2772. ********************************************************************************************* */
  2773.  
  2774. /** Cloaking & Spotted */
  2775. function SetCloaked(bool bNewCloaking)
  2776. {
  2777.     if( bNewCloaking )
  2778.     {
  2779.         ClearBloodDecals();
  2780.     }
  2781. }
  2782.  
  2783. function CallOutCloaking( optional KFPlayerController CallOutController );
  2784.  
  2785. /** Notification when a melee special move ends */
  2786. function MeleeSpecialMoveEnded();
  2787.  
  2788. /** Cause a physics knockdown (e.g. SM_Emerge) */
  2789. function ANIMNOTIFY_Knockdown()
  2790. {
  2791.     // notify emerge anim to end with a ragdoll knockdown
  2792.     if ( SpecialMove == SM_Emerge )
  2793.     {
  2794.         //Knockdown(Velocity, vect(1,1,1));
  2795.         KFSM_Emerge(SpecialMoves[SM_Emerge]).bDoKnockdown = true;
  2796.     }
  2797. }
  2798.  
  2799. /** Called from melee special move when it's interrupted */
  2800. function NotifyAnimInterrupt( optional AnimNodeSequence SeqNode );
  2801.  
  2802. /** Sends message to local players using ClientMessage */
  2803. function Msg( string MsgTxt )
  2804. {
  2805.     MessagePlayer( MsgTxt );
  2806. }
  2807.  
  2808. function float GetCircleStrafeDuration()
  2809. {
  2810.     return 0.f;
  2811. }
  2812.  
  2813. function float GetStepBackInCombatOdds()
  2814. {
  2815.     return 0.12f;
  2816. }
  2817.  
  2818. function Pawn GetPawnToLookAt( optional bool bRequireLOS )
  2819. {
  2820.     local Pawn P;
  2821.     local array<Pawn> InterestingPawns;
  2822.  
  2823.     for( P = WorldInfo.PawnList; P != none; P = P.NextPawn )
  2824.     {
  2825.         if( P != self && P.IsAliveAndWell() && ( !bRequireLOS || LineOfSightTo( P ) ) )
  2826.         {
  2827.             InterestingPawns.AddItem( P );
  2828.         }
  2829.     }
  2830.  
  2831.     if( InterestingPawns.Length > 0 )
  2832.     {
  2833.         return InterestingPawns[ Rand(InterestingPawns.Length) ];
  2834.     }
  2835.  
  2836.     return None;
  2837. }
  2838.  
  2839. /*********************************************************************************************
  2840.  * @name    HeadTracking
  2841. ********************************************************************************************* */
  2842.  
  2843. simulated function bool LookAtPawn( optional Pawn P, optional float Strength=0.5f )
  2844. {
  2845.     if( IK_Look_Head == none )
  2846.     {
  2847.         IK_Look_Head        = SkelControlLookAt(Mesh.FindSkelControl('HeadLook'));
  2848.         //IK_Look_Spine     = SkelControlLookAt(Mesh.FindSkelControl('SpineLook'));
  2849.     }
  2850.  
  2851.     if( IK_Look_Head != none )
  2852.     {
  2853.         if( P == none )
  2854.         {
  2855.             P = GetPawnToLookAt( true );
  2856.         }
  2857.  
  2858.         if( P != none )
  2859.         {
  2860.         //  if( IK_Look_Head.CanLookAtPoint( P.Location + ( vect(0,0,1) * P.BaseEyeHeight ) ) )
  2861.         //  {
  2862.                 bIsHeadTrackingActive = true;
  2863.                 `AILog_Ext( GetFuncName()@P, 'HeadTracking', MyKFAIC );
  2864.                 SetHeadTrackTarget( P, vect(0,0,1) * P.BaseEyeHeight, Strength, false );
  2865.                 return true;
  2866.         //  }
  2867.         }
  2868.     }
  2869.     return false;
  2870. }
  2871.  
  2872. simulated function StopLookingAtPawn( optional Pawn P )
  2873. {
  2874.     if( bCanHeadTrack && bIsHeadTrackingActive && MyLookAtInfo.LookAtTarget != none )
  2875.     {
  2876.         if( P == none )
  2877.         {
  2878.             P = Pawn( MyLookAtInfo.LookAtTarget );
  2879.         }
  2880.         if( P != none )
  2881.         {
  2882.             `AILog_Ext( GetFuncName()@P, 'HeadTracking', MyKFAIC );
  2883.             ClearHeadTrackTarget( P );
  2884.         }
  2885.     }
  2886. }
  2887.  
  2888. /*********************************************************************************************
  2889.  * @name    Effects / Gore
  2890. ********************************************************************************************* */
  2891.  
  2892. /**
  2893.  *  Performs the actual gore LOD switch, and optionally switches physics asset upon death
  2894.  *
  2895.  *  @param   GoreLODIndex        LOD index for the gore LOD
  2896.  */
  2897. native final simulated function bool SwitchToGoreMesh();
  2898.  
  2899. /** Called when SwitchToGoreMesh is successful */
  2900. simulated event NotifyGoreMeshActive()
  2901. {
  2902.     // Need to reset material params since our MIC was reassigned to the gore mesh
  2903.     UpdateGameplayMICParams();
  2904.  
  2905.     if ( SpecialMove != SM_None )
  2906.     {
  2907.         SpecialMoves[SpecialMove].OnGoreMeshSwap();
  2908.     }
  2909. }
  2910.  
  2911. /** Returns n closest bones to the TestLocation that belong to the physics asset of the mesh */
  2912. simulated function GetClosestHitBones(int NumBones, vector TestLocation, out array<name> OutHitBoneList)
  2913. {
  2914.     local int i;
  2915.     local array<name> SearchList;
  2916.  
  2917.     // Add bones in the physics asset to the search list
  2918.     for( i=0; i<mesh.PhysicsAsset.BodySetup.length; i++ )
  2919.     {
  2920.         SearchList.AddItem(mesh.PhysicsAsset.BodySetup[i].BoneName);
  2921.     }
  2922.  
  2923.     mesh.FindClosestBones(TestLocation, NumBones, OutHitBoneList, SearchList);
  2924. }
  2925.  
  2926. simulated event RigidBodyCollision( PrimitiveComponent HitComponent, PrimitiveComponent OtherComponent,
  2927.                     const out CollisionImpactData RigidCollisionData, int ContactIndex )
  2928. {
  2929.     local int i;
  2930.     local KFGoreManager GoreManager;
  2931.     local RigidBodyContactInfo ContactInfo;
  2932.  
  2933.     GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
  2934.  
  2935.     if( GoreManager != none && `TimeSince(LastGibCollisionTime) > GoreManager.GetTimeBetweenGibBloodSplats() )
  2936.     {
  2937.         LastGibCollisionTime = WorldInfo.TimeSeconds;;
  2938.  
  2939.         if ( OtherComponent != none && OtherComponent.Owner != none && !OtherComponent.Owner.IsA('KFPawn') ) // Skip pawn-on-pawn collisions
  2940.         {
  2941.             SoundGroupArch.PlayRigidBodyCollisionSound( self, RigidCollisionData.ContactInfos[ContactIndex].ContactPosition );
  2942.  
  2943.             for( i=0; i<RigidCollisionData.ContactInfos.length; i++ )
  2944.             {
  2945.                 ContactInfo = RigidCollisionData.ContactInfos[i];
  2946.                 GoreManager.LeaveAPersistentBloodSplat(ContactInfo.ContactPosition, -ContactInfo.ContactNormal);
  2947.                 //DrawDebugCoordinateSystem(ContactInfo.ContactPosition, rotator(-ContactInfo.ContactNormal), 10, true);
  2948.             }
  2949.         }
  2950.     }
  2951. }
  2952.  
  2953. /** plays clientside hit effects using the data in HitFxInfo */
  2954. simulated function PlayTakeHitEffects( vector HitDirection, vector HitLocation, optional bool bUseHitImpulse = true)
  2955. {
  2956.     local class<KFDamageType> DmgType;
  2957.     local name HitZoneName, HitBoneName;
  2958.     local int HitZoneIndex;
  2959.  
  2960.     // NVCHANGE_BEGIN - RLS - Debugging Effects
  2961.     if( WorldInfo.Game != none && KFGameInfo(WorldInfo.Game).bNVAlwaysHeadshot )
  2962.     {
  2963.         HitFxInfo.HitBoneIndex = HZI_HEAD;
  2964.     }
  2965.     // NVCHANGE_BEGIN - RLS - Debugging Effects
  2966.  
  2967.     // Cached hit params
  2968.     DmgType = HitFxInfo.DamageType;
  2969.  
  2970.     HitZoneIndex = HitFxInfo.HitBoneIndex;
  2971.     if ( HitZoneIndex != 255 && HitZoneIndex <= HitZones.Length)    // INDEX_None -> 255 after byte conversion
  2972.     {
  2973.         HitZoneName = HitZones[HitZoneIndex].ZoneName;
  2974.         HitBoneName = HitZones[HitZoneIndex].BoneName;
  2975.     }
  2976.  
  2977.     if( DmgType != none )
  2978.     {
  2979.         // If TornOff hasn't been called yet on client, PlayDying now before hit reactions
  2980.         if ( bTearOff && !bPlayedDeath )
  2981.         {
  2982.             PlayDying(HitDamageType,TakeHitLocation);
  2983.         }
  2984.  
  2985.         if ( bPlayedDeath )
  2986.         {
  2987.             PlayDeadHitEffects(HitLocation, HitDirection, HitZoneIndex, HitZoneName, HitBoneName, DmgType, bUseHitImpulse);
  2988.         }
  2989.         else
  2990.         {
  2991.             PlayLivingHitEffects(HitLocation, HitDirection, HitZoneIndex, HitZoneName, HitBoneName, DmgType, bUseHitImpulse);
  2992.         }
  2993.     }
  2994.  
  2995.     if( !bPlayedDeath )
  2996.     {
  2997.         bShowHealth = true;
  2998.         SetTimer( 2.f, false, nameOf(ResetHealthVisibilty) );
  2999.     }
  3000.  
  3001.     Super.PlayTakeHitEffects( HitDirection, HitLocation, bUseHitImpulse );
  3002. }
  3003.  
  3004. /** Plays hit effects on dead zeds, this includes dismemberment and obliteration */
  3005. simulated function PlayDeadHitEffects(vector HitLocation, vector HitDirection, int HitZoneIndex, name HitZoneName, name HitBoneName, class<KFDamageType> DmgType, optional bool bUseHitImpulse)
  3006. {
  3007.     local class<KFProj_PinningBullet> PinProjectile${1}< ${3} >
  3008.     local KFPawn DeadPawn;
  3009.     local KFGoreManager GoreManager;
  3010.     local bool bIsDismemberingHit;
  3011.     local bool bWasObliterated;
  3012.  
  3013.     // If ragdoll and gore is not allowed for dead bodies, check the time of death
  3014.     // to see when the pawn died, and skip if he has been dead for a while
  3015.     if( bAllowRagdollAndGoreOnDeadBodies || `TimeSince(TimeOfDeath) <= 3.f )
  3016.     {
  3017.         // If this zone is 'injured' try to dismember it
  3018.         if ( (InjuredHitZones & (1 << HitZoneIndex)) > 0 && !HitZones[HitZoneIndex].bPlayedInjury )
  3019.         {
  3020.             bIsDismemberingHit = PlayDismemberment(HitZoneIndex, DmgType, HitDirection );
  3021.  
  3022.             // If there was no dismemberment, explode head instead
  3023.             if  ( !bIsDismemberingHit && (InjuredHitZones & (1 << HZI_Head)) > 0 )
  3024.             {
  3025.                 PlayHeadAsplode();
  3026.                 // Set bIsDismemberingHit to true to add an impulse to the neck
  3027.                 bIsDismemberingHit = true;
  3028.             }
  3029.         }
  3030.  
  3031.         GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
  3032.         // If the gore manager doesn't allow mutilation, then we don't allow anything other than basic effects.
  3033.         if(GoreManager.AllowMutilation() && HitFxInfo.bObliterated && `TimeSince(TimeOfDeath) < 0.25f && !bUseDamageInflation)
  3034.         {
  3035.             bWasObliterated = true;
  3036.             bIsDismemberingHit = true;
  3037.             HandlePartialGoreAndGibs(DmgType, HitLocation, HitDirection, HitBoneName, true);
  3038.  
  3039.             GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
  3040.             GoreManager.SpawnObliterationBloodEffect(self);
  3041.         }
  3042.         else
  3043.         {
  3044.             // Handle damage types from projectiles that can pin zeds to walls
  3045.             PinProjectileClass = DmgType.static.GetPinProjectileClass();
  3046.             if( PinProjectileClass != none )
  3047.             {
  3048.                DeadPawn = self;
  3049.                PinProjectileClass.static.CreatePin(DeadPawn, HitLocation, HitDirection, HitBoneName);
  3050.             }
  3051.  
  3052.             HandlePartialGoreAndGibs(DmgType, HitLocation, HitDirection, HitBoneName, false);
  3053.         }
  3054.  
  3055.         // Apply an impulse to attached limbs and ragdoll
  3056.         HandleRagdollImpulseEffects( HitLocation, HitDirection, HitZoneName, HitBoneName, DmgType, bIsDismemberingHit, bUseHitImpulse );
  3057.     }
  3058.  
  3059.     // Play blood effects. Apply more blood if this was a dismembering hit
  3060.     ApplyBloodDecals(HitZoneIndex, HitLocation, HitDirection, HitZoneName, HitBoneName, DmgType, bIsDismemberingHit, bWasObliterated);
  3061. }
  3062.  
  3063. /** Apply impulse to the dead ragdolls bones  */
  3064. simulated function HandleRagdollImpulseEffects( vector HitLocation, vector HitDirection, name HitZoneName, name HitBoneName, class<KFDamageType> DmgType, bool bIsDismemberingHit, optional bool bUseHitImpulse )
  3065. {
  3066.     local vector ImpulseDir, ParentImpulseDir;
  3067.     local float ImpulseScale, ParentImpulseScale;
  3068.     local name RBBoneName;
  3069.     local name HitBoneParentName;
  3070.  
  3071.     // Allow damage type to modify the impulse for a dismembering hit
  3072.     ImpulseDir = HitDirection;
  3073.     ParentImpulseDir = HitDirection;
  3074.     ImpulseScale = 1.f;
  3075.     ParentImpulseScale = 1.f;
  3076.  
  3077.     if( bIsDismemberingHit )
  3078.     {
  3079.         DmgType.static.ModifyDismembermentHitImpulse(self, HitZoneName, ImpulseDir, HitDirection, ParentImpulseDir, ImpulseScale, ParentImpulseScale);
  3080.     }
  3081.  
  3082.     if( HitBoneName != '')
  3083.     {
  3084.         // Get the rigidbody bone that matches the hit bone name
  3085.         RBBoneName = GetRBBoneFromBoneName(HitBoneName);
  3086.     }
  3087.  
  3088.     // Apply impulse to hit bone. This can be for an alive zed (on death shot) or a dismembered limb
  3089.     // In case of explosives, this applies an impulse to the body and not the gibs.
  3090.     // Impulse to the gibs is handled separately in CauseGibsAndApplyImpulse()
  3091.     if (bUseHitImpulse)
  3092.     {
  3093.         ApplyRagdollImpulse(DmgType, HitLocation, ImpulseDir, RBBoneName, ImpulseScale);
  3094.  
  3095.         // Apply another impulse to parent bone when a limb gets dismembered for the first time. Skip if parent is the root bone.
  3096.         if (bIsDismemberingHit && ParentImpulseScale > 0)
  3097.         {
  3098.             HitBoneParentName = mesh.GetParentBone(HitBoneName);
  3099.  
  3100.             // Add additional force if we blew the head off if needed
  3101.             if ((HitBoneName == 'head' || HitBoneParentName == 'neck') && DmgType != none)
  3102.             {
  3103.                 ParentImpulseScale *= DmgType.default.HeadDestructionImpulseForceScale;
  3104.             }
  3105.  
  3106.             // Grab the rigid body bone name so we know which one to apply RB force to
  3107.             HitBoneParentName = GetRBBoneFromBoneName(HitBoneParentName);
  3108.  
  3109.             // Do not apply an additional impulse to the parent bone on for a dismembering shot if it'st he same as oru RB bone
  3110.             if (RBBoneName != HitBoneParentName && Mesh.PhysicsAsset.FindBodyIndex(HitBoneParentName) != INDEX_NONE)
  3111.             {
  3112.                 ApplyRagdollImpulse(DmgType, HitLocation, ParentImpulseDir, HitBoneParentName, ParentImpulseScale);
  3113.             }
  3114.         }
  3115.     }
  3116. }
  3117.  
  3118. /** Hit effects for a living zed */
  3119. simulated function PlayLivingHitEffects(vector HitLocation, vector HitDirection, int HitZoneIndex, name HitZoneName, name HitBoneName, class<KFDamageType> DmgType, optional bool bUseHitImpulse)
  3120. {
  3121.     if ( !TryPlayHitReactionAnim(HitDirection, DmgType, HitZoneIndex)
  3122.         && PawnAnimInfo.bCanPlayPhysicsHitReactions
  3123.         && ActorEffectIsRelevant(HitFXInstigator, false) )
  3124.     {
  3125.         // Play Physics Body Impact.
  3126.         PlayPhysicsBodyImpact(HitLocation, HitDirection, DmgType, HitBoneName);
  3127.     }
  3128.  
  3129.     // NVCHANGE_BEGIN - RLS - Debugging Effects
  3130.     if( WorldInfo.Game != none && KFGameInfo(WorldInfo.Game).bNVAlwaysHeadshot )
  3131.     {
  3132.         HitZoneIndex = HZI_HEAD;
  3133.     }
  3134.     // NVCHANGE_BEGIN - RLS - Debugging Effects
  3135.  
  3136.     if( HitZoneIndex == HZI_Head )
  3137.     {
  3138.         // If head is dismembered (InitPartialKinematics was called), do impulse
  3139.         if ( bUseHitImpulse && Mesh.PhysicsWeight == 1.f && bIsHeadless )
  3140.         {
  3141.             ApplyRagdollImpulse(DmgType, HitLocation, HitDirection, HeadBoneName, 1.f);
  3142.         }
  3143.  
  3144.         ApplyHeadChunkGore(DmgType, HitLocation, HitDirection);
  3145.     }
  3146.  
  3147.     // Play blood effects. Apply more blood if this was a dismembering hit
  3148.     ApplyBloodDecals(HitZoneIndex, HitLocation, HitDirection, HitZoneName, HitBoneName, DmgType, false, false);
  3149. }
  3150.  
  3151. simulated function PlayDyingSound()
  3152. {
  3153.     if( ClassIsChildOf(HitDamageType, class'KFDT_Bleeding') )
  3154.     {
  3155.         SoundGroupArch.PlayBleedoutDyingSound( self );
  3156.     }
  3157.     else if( bHasBrokenConstraints && !HasMouth() )
  3158.     {
  3159.         // if we have no head or mouth...
  3160.         SoundGroupArch.PlayMouthlessDyingSound( self );
  3161.     }
  3162.     else
  3163.     {
  3164.         super.PlayDyingSound();
  3165.     }
  3166. }
  3167.  
  3168. simulated function PlayHitZoneGoreSounds( name HitBoneName, vector HitLocation )
  3169. {
  3170.     if( SoundGroupArch.ShouldPlayCleaveSound(HitBoneName) )
  3171.     {
  3172.         SoundGroupArch.PlayCleaveSound( self, HitLocation );
  3173.     }
  3174.     else
  3175.     {
  3176.         SoundGroupArch.PlayDismembermentSounds( self, HitLocation );
  3177.     }
  3178. }
  3179.  
  3180. /**
  3181.  * Return true if we've already broken the input head bone
  3182.  *
  3183.  * @param   BoneName    the head bone to check to see if we've already broken it.
  3184.  */
  3185. simulated function bool HeadBoneAlreadyBroken(name BoneName)
  3186. {
  3187.     if( BrokenHeadBones.Find(BoneName) != INDEX_NONE )
  3188.     {
  3189.         return true;
  3190.     }
  3191.  
  3192.     return false;
  3193. }
  3194.  
  3195. /**
  3196.  * Return true if we've already broken the input head bone
  3197.  *
  3198.  * @param   BoneName    the head bone to add to the broken head bone array.
  3199.  */
  3200. simulated function AddBrokenHeadBone(name BoneName)
  3201. {
  3202.     if( BrokenHeadBones.Find(BoneName) == INDEX_NONE )
  3203.     {
  3204.         BrokenHeadBones.AddItem(BoneName);
  3205.     }
  3206.  
  3207.     return;
  3208. }
  3209.  
  3210. /**
  3211.  * Return true if this pawn is ok with having the input head bone be broken
  3212.  *
  3213.  * @param   BoneName    the head bone to check to see if we all it to break.
  3214.  */
  3215. simulated function bool ShouldAllowHeadBoneToBreak(name BoneName)
  3216. {
  3217.     if( !bPlayedDeath && !bIsHeadless && !bTearOff )
  3218.     {
  3219.         if ( NumHeadChunksRemoved >= MaxHeadChunkGoreWhileAlive )
  3220.         {
  3221.             return false;
  3222.         }
  3223.  
  3224.         // Don't allow both sides of the front of the face (both eyes) to be blown
  3225.         // off, or both sides of the back of the head and the jaw while the zed is alive and not headless
  3226.         if( (BoneName == 'Gore_FrontL' && BrokenHeadBones.Find('Gore_FrontR') != INDEX_NONE)
  3227.             || (BoneName == 'Gore_FrontR' && BrokenHeadBones.Find('Gore_FrontL') != INDEX_NONE)
  3228.             || (BoneName == 'Gore_BackL' && BrokenHeadBones.Find('Gore_Jaw') != INDEX_NONE && BrokenHeadBones.Find('Gore_BackR') != INDEX_NONE)
  3229.             || (BoneName == 'Gore_BackR' && BrokenHeadBones.Find('Gore_Jaw') != INDEX_NONE && BrokenHeadBones.Find('Gore_BackL') != INDEX_NONE)
  3230.             || (BoneName == 'Gore_Jaw' && BrokenHeadBones.Find('Gore_BackR') != INDEX_NONE && BrokenHeadBones.Find('Gore_BackL') != INDEX_NONE) )
  3231.         {
  3232.             return false;
  3233.         }
  3234.     }
  3235.  
  3236.     return true;
  3237. }
  3238.  
  3239. /** Update metadata required by gore chunk attachments */
  3240. simulated function UpdateGoreChunkData(name BoneName)
  3241. {
  3242.     if( BoneName == 'Gore_FrontL' ||
  3243.         BoneName == 'Gore_FrontR' ||
  3244.         BoneName == 'Gore_BackL'  ||
  3245.         BoneName == 'Gore_BackR'  /*||
  3246.         BoneName == 'Gore_Jaw'*/ )
  3247.     {
  3248.         NumHeadChunksRemoved++;
  3249.     }
  3250. }
  3251.  
  3252. /** Implementation of delegate function used to determine whether to attach the skull gore chunk.
  3253.     The skull gore chunk is used only when one or two head chunks are removed */
  3254. simulated function bool ShouldAttachSkullChunk()
  3255. {
  3256.     return NumHeadChunksRemoved < 3;
  3257. }
  3258.  
  3259. /** Implementation of delegate function used to determine whether to detach the skull gore chunk.
  3260.     If attached, the skull gore chunk is detached when more than 2 head chunks are removed */
  3261. simulated function bool ShouldDetachSkullChunk()
  3262. {
  3263.     return NumHeadChunksRemoved >= 3 && (bPlayedDeath || bIsHeadless || bTearOff);
  3264. }
  3265.  
  3266. /** Handles the gore chunks to be attached to the pawn body when a limb is dismembered or gibbed */
  3267. simulated function HandleGoreChunkAttachments(name DismemberedBone)
  3268. {
  3269.     local int AttachmentIndex, ChunkRef;
  3270.     local StaticMeshComponent AttachComp;
  3271.     local KFCharacterInfo_Monster MonsterInfo;
  3272.     local AttachedGoreChunkInfo ChunkInfo;
  3273.     local KFGoreChunkAttachmentInfo CurrentChunk;
  3274.  
  3275.     // Update information that is required by the attachment/detachment code
  3276.     UpdateGoreChunkData(DismemberedBone);
  3277.  
  3278.     MonsterInfo = GetCharacterMonsterInfo();
  3279.  
  3280.     if( MonsterInfo != none )
  3281.     {
  3282.         for( AttachmentIndex = 0; AttachmentIndex < MonsterInfo.GoreChunkAttachments.length; AttachmentIndex++ )
  3283.         {
  3284.             CurrentChunk = MonsterInfo.GoreChunkAttachments[AttachmentIndex];
  3285.  
  3286.             if( CurrentChunk.DismemberedBoneList.Find(DismemberedBone) != INDEX_NONE )
  3287.             {
  3288.                 // Search for it in the currently attached chunks list
  3289.                 ChunkRef = AttachedGoreChunks.Find('AttachmentIndex', AttachmentIndex);
  3290.  
  3291.                 if( CurrentChunk.ShouldAttachGoreChunk(self) && ChunkRef == INDEX_NONE )
  3292.                 {
  3293.                     // Create and intialize the attachment component
  3294.                     AttachComp = new(self) class'StaticMeshComponent';
  3295.                     AttachComp.SetStaticMesh(CurrentChunk.StaticMesh);
  3296.                     AttachComp.SetLightingChannels(PawnLightingChannel);
  3297.                     AttachComp.CastShadow=true;
  3298.                     AttachComp.bCastDynamicShadow=true;
  3299.                     AttachComp.bAllowPerObjectShadowBatching=true;
  3300.  
  3301.                     //Big head mode specific fix.  Increase cull distance for this situation
  3302.                     if (CurrentChunk.SocketName == 'Head_Attach' && CurrentHeadScale > 1.f)
  3303.                     {
  3304.                         AttachComp.SetCullDistance(1000 * CurrentHeadScale);
  3305.                     }
  3306.                     else
  3307.                     {
  3308.                         AttachComp.SetCullDistance(1000);
  3309.                     }
  3310.  
  3311.                     AttachComp.SetShadowParent(mesh);
  3312.                     AttachComp.bAllowApproximateOcclusion=True;
  3313.                     AttachComp.SetTraceBlocking(False,False);
  3314.                     AttachComp.SetActorCollision(False,False);
  3315.  
  3316.                     // Attach chunk to socket in the pawn mesh
  3317.                     mesh.AttachComponentToSocket(AttachComp, CurrentChunk.SocketName);
  3318.  
  3319.                     // Add it to the list of currently active gore attachments
  3320.                     ChunkInfo.AttachmentIndex = AttachmentIndex;
  3321.                     ChunkInfo.AttachedComponent = AttachComp;
  3322.                     AttachedGoreChunks.AddItem(ChunkInfo);
  3323.                 }
  3324.                 else if( CurrentChunk.ShouldDetachGoreChunk(self) && ChunkRef != INDEX_NONE )
  3325.                 {
  3326.                     AttachComp = AttachedGoreChunks[ChunkRef].AttachedComponent;
  3327.  
  3328.                     // Detach the chunk from the mesh
  3329.                     mesh.DetachComponent(AttachComp);
  3330.  
  3331.                     // Remove it from the list of currently active gore attachments
  3332.                     AttachedGoreChunks.Remove(ChunkRef, 1);
  3333.                 }
  3334.             }
  3335.         }
  3336.     }
  3337. }
  3338.  
  3339. /** Calculate type of hit reaction animation from damage info */
  3340. simulated function bool TryPlayHitReactionAnim(vector HitDirection, class<KFDamageType> damageType, byte HitZoneIdx)
  3341. {
  3342.     local EPawnOctant AnimDir;
  3343.     local EHitReactionAnimType HitReactionType;
  3344.     local EHitZoneBodyPart BodyPart;
  3345.     local bool bOnlyAdditiveHits;
  3346.  
  3347.     if( damageType == none || ActorTimeSince(NextHitReactionAnim_ActorTime) < 0 )
  3348.     {
  3349.         return false;
  3350.     }
  3351.  
  3352.     // cannot play hit reaction anims during special moves
  3353.     if ( IsDoingSpecialMove() )
  3354.     {
  3355.         bOnlyAdditiveHits = true;
  3356.         if ( !SpecialMoves[SpecialMove].bAllowHitReactions )
  3357.         {
  3358.             return false;
  3359.         }
  3360.     }
  3361.  
  3362.     // Set the limb used for incap effects
  3363.     BodyPart = (HitZoneIdx != 255 && HitZoneIdx < HitZones.Length) ? HitZones[HitZoneIdx].Limb : BP_Torso;
  3364.  
  3365.     HitReactionType = HIT_Light;
  3366.     // If we're moving (e.g. DoPauseAI wasn't called because of incap cooldown) don't try hard/medium
  3367.     // This is not 100% reliable, but worst case we get a reasonable upper body blend while moving
  3368.     bOnlyAdditiveHits = bOnlyAdditiveHits || VSizeSq(Velocity) > 50.f;
  3369.  
  3370.     if ( !bOnlyAdditiveHits )
  3371.     {
  3372.         HitReactionType = EHitReactionAnimType(AfflictionHandler.GetPredictedHitReaction(DamageType, BodyPart));
  3373.     }
  3374.  
  3375.     switch (BodyPart)
  3376.     {
  3377.     case BP_LeftArm:
  3378.         AnimDir = CalcOctagonRegion(Rotation, -HitDirection);
  3379.         if ( AnimDir == DIR_Forward || AnimDir == DIR_ForwardLeft || AnimDir == DIR_ForwardRight )
  3380.             AnimDir = DIR_ForwardLeft;
  3381.         else
  3382.             AnimDir = DIR_BackwardLeft;
  3383.         break;
  3384.     case BP_RightArm:
  3385.         AnimDir = CalcOctagonRegion(Rotation, -HitDirection);
  3386.         if ( AnimDir == DIR_Forward || AnimDir == DIR_ForwardLeft || AnimDir == DIR_ForwardRight )
  3387.             AnimDir = DIR_ForwardRight;
  3388.         else
  3389.             AnimDir = DIR_BackwardRight;
  3390.         break;
  3391.     default:
  3392.         AnimDir = CalcOctagonRegion(Rotation, -HitDirection);
  3393.         break;
  3394.     }
  3395.  
  3396.     // Play animation
  3397.     return PawnAnimInfo.PlayHitReactionAnim(self, HitReactionType, AnimDir);
  3398. }
  3399.  
  3400. /** plays clientside hit effects using the data in HitFxInfo */
  3401. simulated function ApplyBloodDecals(int HitZoneIndex, vector HitLocation, vector HitDirection, name HitZoneName, name HitBoneName, class<KFDamageType> DmgType, bool bIsDismemberingHit, bool bWasObliterated)
  3402. {
  3403.     local KFGoreManager GoreManager;
  3404.  
  3405.     GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
  3406.  
  3407.     if( DmgType != none && GoreManager != none )
  3408.     {
  3409.         // Spawn wound decal on the damaged pawn
  3410.         if( !bWasObliterated && !bIsCloaking )
  3411.         {
  3412.         GoreManager.LeaveABodyWoundDecal(self, HitLocation, HitDirection, HitZoneName, HitBoneName, DmgType);
  3413.         }
  3414.  
  3415.         // Spawn blood splatter decal effect behind the damaged pawn
  3416.         if ( DmgType.default.bShouldSpawnBloodSplat )
  3417.         {
  3418.             GoreManager.LeaveABloodSplatterDecal(self, HitLocation, HitDirection);
  3419.         }
  3420.  
  3421.         // Spawn persistent blood splatters
  3422.         if( DmgType.default.bShouldSpawnPersistentBlood )
  3423.         {
  3424.              GoreManager.CausePersistentBlood(self, DmgType, HitLocation, HitDirection, HitZoneIndex, bIsDismemberingHit, bWasObliterated);
  3425.         }
  3426.     }
  3427. }
  3428.  
  3429. /**
  3430.  *  Gibbing, partial gore and gib impusle effects handled here.
  3431.  *
  3432.  *  @param  ObliterateGibs  If true this function will rip apart all limbs. If false, the default behavior is to select random bones to rip apart.
  3433. */
  3434. simulated function HandlePartialGoreAndGibs(
  3435.     class<KFDamageType> DmgType,
  3436.     vector HitLocation,
  3437.     vector HitDirection,
  3438.     name HitBoneName,
  3439.     bool ObliterateGibs)
  3440. {
  3441.     local KFGoreManager GoreManager;
  3442.     local KFCharacterInfo_Monster MonsterInfo;
  3443.  
  3444.     GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
  3445.  
  3446.     if( DmgType != none && GoreManager != none )
  3447.     {
  3448.         if( GoreManager.AllowMutilation() )
  3449.         {
  3450.             // Enable alternate bone weighting and gore skeleton
  3451.             if( !bIsGoreMesh )
  3452.             {
  3453.                 SwitchToGoreMesh();
  3454.             }
  3455.  
  3456.             // Apply gore only if we were able to successfully switch to the gore mesh
  3457.             if( bIsGoreMesh )
  3458.             {
  3459.                 MonsterInfo = GetCharacterMonsterInfo();
  3460.                 // Try to apply exlosion gore first
  3461.                 if(ObliterateGibs)
  3462.                 {
  3463.                     ApplyObliterationFxGore( GoreManager, MonsterInfo, DmgType );
  3464.                 }
  3465.                 else
  3466.                 if( HitFxInfo.bRadialDamage)
  3467.                 {
  3468.                     ApplyRadialFxGore( GoreManager, MonsterInfo, DmgType );
  3469.                 }
  3470.                 else
  3471.                 {
  3472.                     ApplyTakeHitFxGore( GoreManager, MonsterInfo, DmgType, HitLocation, HitDirection, HitBoneName );
  3473.                 }
  3474.             }
  3475.         }
  3476.     }
  3477. }
  3478.  
  3479. /**
  3480.  *  Apply any gibbing from radial damage
  3481.  *
  3482.  *  @param  ObliterateGibs  If true this function will rip apart all limbs. If false, the default behavior is to select random bones to rip apart.
  3483. */
  3484. simulated function ApplyRadialFxGore( KFGoreManager GoreManager, KFCharacterInfo_Monster MonsterInfo, class<KFDamageType> DmgType )
  3485. {
  3486.     local array<name> OutGibBoneList;
  3487.     local float NormalizedDistanceScale;
  3488.     local vector ExplosionOrigin;
  3489.     local int NumGibs;
  3490.  
  3491.     // Try to apply exlosion gore first
  3492.     if( DmgType.default.bCanGib)
  3493.     {
  3494.         ExplosionOrigin = HitFxRadialInfo.RadiusHurtOrigin;
  3495.  
  3496.         // Use distance from explosion along with some randomization to figure out
  3497.         // how many joints to dismember. Distance based calculation is skipped after 10 meters.
  3498.         NormalizedDistanceScale = VSize(Location - ExplosionOrigin)/1000.f;
  3499.         NumGibs = FCeil(FMax(1.f - NormalizedDistanceScale, 0.f) * rand(10)) + rand(3);
  3500.         NumGibs *= MonsterInfo.ExplosionGibScale;
  3501.  
  3502.         GetClosestHitBones(NumGibs, ExplosionOrigin, OutGibBoneList);
  3503.  
  3504.         // Gib-ify!
  3505.         // Spawn exlosion PS at the root bone
  3506.         GoreManager.CauseGibsAndApplyImpulse(
  3507.             self,
  3508.             DmgType,
  3509.             ExplosionOrigin,
  3510.             OutGibBoneList,
  3511.             MonsterInfo.ExplosionEffectTemplate,
  3512.             mesh.GetBoneLocation(mesh.GetBoneName(0)));
  3513.     }
  3514. }
  3515.  
  3516. /**
  3517.  *  This function will dismember all rigid body bones in the mesh.
  3518. */
  3519. simulated function ApplyObliterationFxGore( KFGoreManager GoreManager, KFCharacterInfo_Monster MonsterInfo, class<KFDamageType> DmgType )
  3520. {
  3521.     local array<name> OutGibBoneList;
  3522.     local vector ObliterationLocation;
  3523.     local int MaxNumGibs;
  3524.  
  3525.     if( DmgType.default.MaxObliterationGibs > 0 )
  3526.     {
  3527.         MaxNumGibs = DmgType.default.MaxObliterationGibs;
  3528.     }
  3529.     else
  3530.     {
  3531.         // Allowed to disconnect 100 bones (basically, all bones.)
  3532.         MaxNumGibs = 100;
  3533.     }
  3534.  
  3535.     // Try to apply exlosion gore first
  3536.     if( DmgType.default.bCanGib)
  3537.     {
  3538.         // Origin is the point where the explosive went off
  3539.         if(HitFxInfo.bRadialDamage == true)
  3540.         {
  3541.             ObliterationLocation = HitFxRadialInfo.RadiusHurtOrigin;
  3542.         }
  3543.         else if( DmgType.default.bUseHitLocationForGibImpulses )
  3544.         {
  3545.             ObliterationLocation = HitFxInfo.HitLocation;
  3546.             if( DmgType.default.bPointImpulseTowardsOrigin )
  3547.             {
  3548.                 ObliterationLocation -= DecodeUnitVector(HitFxInfo.EncodedHitDirection) * DmgType.default.ImpulseOriginScale;
  3549.             }
  3550.             else
  3551.             {
  3552.                 ObliterationLocation.Z = Location.Z - CylinderComponent.CollisionHeight * 0.25f;
  3553.             }
  3554.         }
  3555.         else
  3556.         {
  3557.             ObliterationLocation = self.Location;
  3558.         }
  3559.  
  3560.         GetClosestHitBones(MaxNumGibs, ObliterationLocation, OutGibBoneList);
  3561.  
  3562.         // Gib-ify!
  3563.         // Spawn exlosion PS at the root bone
  3564.         GoreManager.CauseGibsAndApplyImpulse(
  3565.             self,
  3566.             DmgType,
  3567.             ObliterationLocation,
  3568.             OutGibBoneList,
  3569.             MonsterInfo.ExplosionEffectTemplate,
  3570.             mesh.GetBoneLocation(mesh.GetBoneName(0)));
  3571.     }
  3572. }
  3573.  
  3574. /** Apply any gore or breaks from non radial damage */
  3575. simulated function ApplyTakeHitFxGore(
  3576.     KFGoreManager GoreManager,
  3577.     KFCharacterInfo_Monster MonsterInfo,
  3578.     class<KFDamageType> DmgType,
  3579.     vector HitLocation,
  3580.     vector HitDirection,
  3581.     name HitBoneName)
  3582. {
  3583.     local array<name> OutGibBoneList;
  3584.     local int JointIndex, ExplosionBreakIdx, BoneIdx;
  3585.     local vector ExplosionOrigin;
  3586.     local name GibBone;
  3587.  
  3588.     // Apply partial gore (if supported)
  3589.     GoreManager.ConditionalApplyPartialGore(self, DmgType, HitLocation, HitDirection, HitBoneName);
  3590.  
  3591.     // Apply any hit explosion gore specified for this bone
  3592.     for( JointIndex = 0; JointIndex < MonsterInfo.GoreJointSettings.length; JointIndex++ )
  3593.     {
  3594.         if( MonsterInfo.GoreJointSettings[JointIndex].HitBoneName == HitBoneName )
  3595.         {
  3596.             for( ExplosionBreakIdx = 0;
  3597.                  ExplosionBreakIdx < MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore.length;
  3598.                  ExplosionBreakIdx++ )
  3599.             {
  3600.                 if( MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore[ExplosionBreakIdx].ConstrainToDamageGroups.length == 0 ||
  3601.                     MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore[ExplosionBreakIdx].ConstrainToDamageGroups.Find(DGT_None) != INDEX_None ||
  3602.                     MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore[ExplosionBreakIdx].ConstrainToDamageGroups.Find(DmgType.default.GoreDamageGroup) != INDEX_None)
  3603.                 {
  3604.                     // Origin is the place of hit
  3605.                     ExplosionOrigin = mesh.GetBoneLocation(HitBoneName);
  3606.  
  3607.                     // Only include bones that haven't been gibbed/broken already
  3608.                     for( BoneIdx = 0;
  3609.                          BoneIdx < MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore[ExplosionBreakIdx].BreakBones.length;
  3610.                          BoneIdx++ )
  3611.                     {
  3612.                         GibBone = MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore[ExplosionBreakIdx].BreakBones[BoneIdx].BoneName;
  3613.                         if( !mesh.IsBrokenConstraint(GibBone) )
  3614.                         {
  3615.                             OutGibBoneList.AddItem(GibBone);
  3616.                         }
  3617.                     }
  3618.  
  3619.                     // Gib-ify!
  3620.                     GoreManager.CauseGibsAndApplyImpulse(
  3621.                         self,
  3622.                         DmgType,
  3623.                         ExplosionOrigin,
  3624.                         OutGibBoneList,
  3625.                         MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore[ExplosionBreakIdx].ParticleSystemTemplate,
  3626.                         ExplosionOrigin,
  3627.                         HitBoneName);
  3628.                 }
  3629.             }
  3630.         }
  3631.     }
  3632. }
  3633.  
  3634. /** Gibbing and partial gore effects handled here */
  3635. simulated function ApplyHeadChunkGore(class<KFDamageType> DmgType, vector HitLocation, vector HitDirection)
  3636. {
  3637.     local KFGoreManager GoreManager;
  3638.  
  3639.     if( bCanCloak && IsAliveAndWell() )
  3640.     {
  3641.         return;
  3642.     }
  3643.  
  3644.     GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
  3645.  
  3646.     if( DmgType != none && GoreManager != none )
  3647.     {
  3648.         if( GoreManager.AllowMutilation() )
  3649.         {
  3650.             // Enable alternate bone weighting and gore skeleton
  3651.             if( !bIsGoreMesh )
  3652.             {
  3653.                 SwitchToGoreMesh();
  3654.             }
  3655.  
  3656.             if ( !bPlayedDeath )
  3657.             {
  3658.                 // @hack: force all gore as if it was a handgun so we only get head chunks
  3659.                 DmgType = class'KFDT_Ballistic';
  3660.                 GoreManager.ConditionalApplyPartialGore(self, DmgType, HitLocation, HitDirection, HitZones[HZI_HEAD].BoneName);
  3661.             }
  3662.         }
  3663.     }
  3664. }
  3665.  
  3666. /** Reliably play any gore effects related to a zone/limb being dismembered */
  3667. simulated function HitZoneInjured(optional int HitZoneIdx=INDEX_None)
  3668. {
  3669.     // --------------------------------------------------------------
  3670.     // Network: Server (Also called on clients after TearOff)
  3671.     // --------------------------------------------------------------
  3672.     if ( Role == ROLE_Authority && HitZoneIdx != INDEX_None )
  3673.     {
  3674.         if ( HitZoneIdx == HZI_Head )
  3675.         {
  3676.             CauseHeadTrauma();
  3677.         }
  3678.  
  3679.         // replicate to all clients
  3680.         InjuredHitZones = InjuredHitZones | (1 << HitZoneIdx);
  3681.     }
  3682.  
  3683.     // Play living head explosion effects. Some effects use the clientside HitFX system (e.g. PlayDismemberment),
  3684.     // but this should be used when we need a reliably replicated effect.
  3685.     if  (WorldInfo.NetMode != NM_DedicatedServer && (!bTearOff && !bPlayedDeath))
  3686.     {
  3687.         if ( (InjuredHitZones & (1 << HZI_Head)) > 0 && !bDisableHeadless)
  3688.         {
  3689.             // Use HitFxInfo for "best guess" DT and CreationTime to fallback
  3690.             // on HeadAsplode when coming into relevancy.
  3691.             if ( `TimeSince(CreationTime) > 1.f )
  3692.             {
  3693.                 PlayDismemberment(HZI_Head, HitFxInfo.DamageType);
  3694.             }
  3695.  
  3696.             // If there was no dismemberment, explode head instead
  3697.             if  ( !HitZones[HZI_Head].bPlayedInjury )
  3698.             {
  3699.                 PlayHeadAsplode();
  3700.             }
  3701.         }
  3702.     }
  3703. }
  3704.  
  3705. simulated function PlayHeadAsplode()
  3706. {
  3707.     local KFGoreManager GoreManager;
  3708.     local name BoneName;
  3709.  
  3710.     if( HitZones[HZI_Head].bPlayedInjury )
  3711.     {
  3712.         return;
  3713.     }
  3714.  
  3715.     //Headless turned off, get out of here
  3716.     if (bDisableHeadless)
  3717.     {
  3718.         return;
  3719.     }
  3720.  
  3721.     // make sure this doesn't happen after death so that normal HitFX/Gore path is followed.  Using
  3722.     // bTearOff since bPlayDying may not be set yet on the client
  3723.     // Let the head be blown off for a short time still after death
  3724.     if ( (bTearOff || bPlayedDeath) && TimeOfDeath > 0 && `TimeSince(TimeOfDeath) > 0.75 )
  3725.     {
  3726.         return;
  3727.     }
  3728.  
  3729.     // Enable alternate bone weighting and gore skeleton
  3730.     GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
  3731.     if( GoreManager != none && GoreManager.AllowHeadless() )
  3732.     {
  3733.         if( !bIsGoreMesh )
  3734.         {
  3735.             SwitchToGoreMesh();
  3736.         }
  3737.     }
  3738.  
  3739.     // Apply mutilation gore only if we were able to successfully switch to the gore mesh
  3740.     if( bIsGoreMesh && GoreManager != none )
  3741.     {
  3742.         BoneName = HitZones[HZI_Head].BoneName;
  3743.         GoreManager.CrushBone( self, BoneName );
  3744.         SoundGroupArch.PlayHeadPopSounds( self, mesh.GetBoneLocation(BoneName) );
  3745.         HitZones[HZI_Head].bPlayedInjury = true;
  3746.         SpawnHeadShotFX(KFPlayerReplicationInfo(HitFxInfo.DamagerPRI));
  3747.     }
  3748. }
  3749.  
  3750. /** Dismember this hit zone if it's not dismembered already
  3751. * InDmgType and HitDirection are only ever used on the client after a death shot
  3752. */
  3753. simulated function bool PlayDismemberment(int InHitZoneIndex, class<KFDamageType> InDmgType, optional vector HitDirection)
  3754. {
  3755.     local KFGoreManager GoreManager;
  3756.     local name BreakBoneName;
  3757.  
  3758.     // Bail out if the hit zone has already been dismembered
  3759.     if( HitZones[InHitZoneIndex].bPlayedInjury )
  3760.     {
  3761.         return false;
  3762.     }
  3763.  
  3764.     // This hit zone was injured (see CanInjureHitZone), but shouldn't be dismembered
  3765.     if ( !InDmgType.static.CanDismemberHitZone( HitZones[InHitZoneIndex].ZoneName ) )
  3766.     {
  3767.         return false;
  3768.     }
  3769.  
  3770.     // Enable alternate bone weighting and gore skeleton
  3771.     GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
  3772.     if( GoreManager != none && GoreManager.AllowMutilation() )
  3773.     {
  3774.         if( !bIsGoreMesh )
  3775.         {
  3776.             SwitchToGoreMesh();
  3777.         }
  3778.  
  3779.         // Apply mutilation gore only if we were able to successfully switch to the gore mesh
  3780.         if( bIsGoreMesh )
  3781.         {
  3782.             // Get the bone to dismember from the hit zone
  3783.             BreakBoneName = HitZones[InHitZoneIndex].BoneName;
  3784.             // If we're dead, allow damage type to override the bone
  3785.             if ( Health <= 0 && !IsZero(HitDirection) )
  3786.             {
  3787.                 InDmgType.static.GetBoneToDismember(self, HitDirection, HitZones[InHitZoneIndex].ZoneName, BreakBoneName);
  3788.             }
  3789.             // Dismember
  3790.  
  3791.             GoreManager.CauseDismemberment(self, BreakBoneName, InDmgType);
  3792.             if (InHitZoneIndex == HZI_HEAD)
  3793.             {
  3794.                 SpawnHeadShotFX(KFPlayerReplicationInfo(HitFxInfo.DamagerPRI));
  3795.             }
  3796.             PlayHitZoneGoreSounds(BreakBoneName, mesh.GetBoneLocation(BreakBoneName));
  3797.             HitZones[InHitZoneIndex].bPlayedInjury = true;
  3798.  
  3799.             // If we're still alive (non-ragdoll), start partial physics ragdoll
  3800.             // Note: bPlayedDeath may not be set yet on client, so use Health
  3801.             if ( Health > 0 && bHasBrokenConstraints )
  3802.             {
  3803.                 InitPartialKinematics();
  3804.             }
  3805.             return true;
  3806.         }
  3807.     }
  3808.     return false;
  3809. }
  3810.  
  3811. /** Apply damage to a specific zone (useful for gore effects) */
  3812. function TakeHitZoneDamage(float Damage, class<DamageType> DamageType, int HitZoneIdx, vector InstigatorLocation)
  3813. {
  3814.     local float HeadHealthPercentage;
  3815.  
  3816.     if (HitZoneIdx > HitZones.Length)
  3817.     {
  3818.         return;
  3819.     }
  3820.  
  3821.     Super.TakeHitZoneDamage(Damage, DamageType, HitZoneIdx, InstigatorLocation);
  3822.  
  3823.     // When GoreHealth <= 0, check to see if this weapon can dismember limbs
  3824.     if ( HitZones[HitZoneIdx].GoreHealth <= 0 && CanInjureHitZone(DamageType, HitZoneIdx) )
  3825.     {
  3826.         HitZoneInjured(HitZoneIdx);
  3827.     }
  3828.  
  3829.     // Handle head injuries
  3830.     if ( HitZoneIdx == HZI_Head )
  3831.     {
  3832.         // Based on head health, calculate number of head chunks we're allowed to remove
  3833.         if( !bPlayedDeath && !bIsHeadless && !bTearOff )
  3834.         {
  3835.             HeadHealthPercentage = GetHeadHealthPercent();
  3836.             if( HeadHealthPercentage > 0.5 )
  3837.             {
  3838.                 MaxHeadChunkGoreWhileAlive = 1;
  3839.             }
  3840.             else if ( HeadHealthPercentage > 0.25 )
  3841.             {
  3842.                 MaxHeadChunkGoreWhileAlive = 2;
  3843.             }
  3844.             else if ( HeadHealthPercentage > 0.0 )
  3845.             {
  3846.                 MaxHeadChunkGoreWhileAlive = 3;
  3847.             }
  3848.         }
  3849.     }
  3850. }
  3851.  
  3852. /** Returns the percentage of head health remaining on this zed */
  3853. function float GetHeadHealthPercent()
  3854. {
  3855.     local float HeadHealth, HeadHealthMax;
  3856.  
  3857.     HeadHealth = float(HitZones[HZI_Head].GoreHealth);
  3858.     HeadHealthMax = float(HitZones[HZI_Head].MaxGoreHealth);
  3859.  
  3860.     return HeadHealth / HeadHealthMax;
  3861. }
  3862.  
  3863. /** Called by KFPawnAnimInfo when determining whether an attack can be performed */
  3864. simulated function bool ShouldPlayHeadlessMeleeAnims()
  3865. {
  3866.     return bIsHeadless || bEmpPanicked;
  3867. }
  3868.  
  3869. /** Generally used to determine which/whether dialog/voice events should be played */
  3870. simulated event bool HasMouth()
  3871. {
  3872.     if( !bHasBrokenConstraints )
  3873.     {
  3874.         return true;
  3875.     }
  3876.  
  3877.     return !Mesh.IsBrokenConstraint( 'head' ) && !Mesh.IsBoneHidden( Mesh.MatchRefBone('gore_jaw') );
  3878. }
  3879.  
  3880. /** Breaks all joint constraints using dependent gore system */
  3881. simulated function ForceBreakAllConstraints()
  3882. {
  3883.     local int i;
  3884.     local KFGoreManager GoreManager;
  3885.  
  3886.     bHasBrokenConstraints = TRUE;
  3887.  
  3888.     // Enable alternate bone weighting and gore skeleton
  3889.     GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
  3890.     if( GoreManager == none )
  3891.     {
  3892.         return;
  3893.     }
  3894.  
  3895.     if( GoreManager.AllowMutilation() )
  3896.     {
  3897.         if( !bIsGoreMesh )
  3898.         {
  3899.             SwitchToGoreMesh();
  3900.         }
  3901.  
  3902.         for( i = 0; i < CharacterMonsterArch.GoreJointSettings.length; i++ )
  3903.         {
  3904.             if ( !CharacterMonsterArch.GoreJointSettings[i].bNonBreakableJoint )
  3905.             {
  3906.                 GoreManager.BreakConstraint( self, CharacterMonsterArch.GoreJointSettings[i].HitBoneName );
  3907.             }
  3908.         }
  3909.     }
  3910. }
  3911.  
  3912. function NotifyMeleeAttackFinished();
  3913.  
  3914. /** Checks if we're able to reduce or restore our collision size if we're going through
  3915. *  a choke point and we have a large collision cylinder */
  3916. simulated function ChokePointTimer()
  3917. {
  3918.     if( !IsTimerActive(nameof(ChokePointTimer)) )
  3919.     {
  3920.         SetTimer(0.3f, true, nameof(ChokePointTimer), self);
  3921.     }
  3922.  
  3923.     if( CurrentChokePointTrigger != none )
  3924.     {
  3925.         // Check if we want to restore our collision
  3926.         if( CurrentChokePointTrigger.CanRestoreChokeCollision(self) )
  3927.         {
  3928.             // If our enemy has entered the choke point and we can fit in the geometry, retore our collision
  3929.             if( CylinderComponent.CollisionRadius < CylinderComponent.default.CollisionRadius
  3930.                      && !CheckEncroachingWorldGeometry() )
  3931.             {
  3932.                 SetChokePointCollision(false);
  3933.             }
  3934.         }
  3935.         // if our collision is too large to fit through this chokepiont, reduce it
  3936.         else if( CylinderComponent.CollisionRadius > CurrentChokePointTrigger.MaxCollisionRadius )
  3937.         {
  3938.             SetChokePointCollision(true);
  3939.         }
  3940.     }
  3941.     // if we are out of a chokepoint and can restore our collision, do so
  3942.     else if( !CheckEncroachingWorldGeometry() )
  3943.     {
  3944.         // Only restore collision if it's been reduced
  3945.         if( CylinderComponent.CollisionRadius < CylinderComponent.default.CollisionRadius )
  3946.         {
  3947.             SetChokePointCollision(false);
  3948.         }
  3949.         ClearTimer(nameof(ChokePointTimer), self);
  3950.     }
  3951. }
  3952.  
  3953. event SetDamageInflation(float NewInflation)
  3954. {
  3955.     RepDamageInflateParam = FloatToByte(NewInflation);
  3956.     SetHeadScale(1.0 + (GetCurrentInflation() / 2.0), CurrentHeadScale);
  3957.     HandleDamageInflation();
  3958. }
  3959.  
  3960. simulated function float GetCurrentInflation()
  3961. {
  3962.     local float CurrentInflation;
  3963.     CurrentInflation = FClamp(ByteToFloat(RepInflateMatParam) + ByteToFloat(RepDamageInflateParam) - ByteToFloat(RepBleedInflateMatParam), -1.0, 1.0);
  3964.     //`log("*** Inflation" @ CurrentInflation);
  3965.     //RepInflateMatParam - From microwave affliction
  3966.     //RepDamageInflateParam - From modes where gametype modifies size (Shrinky Dinky, Beefcake, etc)
  3967.     //RepBleedInflateMatParam - From bleed affliction, set to negative here so we don't lose precision in byte conversion
  3968.     return CurrentInflation;
  3969. }
  3970.  
  3971. simulated function HandleDamageInflation()
  3972. {
  3973.     if ( WorldInfo.NetMode != NM_DedicatedServer )
  3974.     {
  3975.         UpdateVisualInflation(GetCurrentInflation() * 2.0);
  3976.     }
  3977. }
  3978.  
  3979. simulated function UpdateBleedIncapFX()
  3980. {
  3981.     local float CurrentStrength;
  3982.     if (WorldInfo.NetMode != NM_DedicatedServer)
  3983.     {
  3984.         CurrentStrength = ByteToFloat(RepBleedInflateMatParam);
  3985.         //We've gone past the threshold, create a PSC
  3986.         if ((CurrentStrength != 0 && IsAliveAndWell()) && BleedIncapPSC == none)
  3987.         {
  3988.             BleedIncapPSC = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment(BleedIncapFX, Mesh, class'KFSM_Stunned'.default.DazedFXSocketName, true);
  3989.             if (BleedIncapPSC != none)
  3990.             {
  3991.                 BleedIncapPSC.SetAbsolute(false, true, false);
  3992.                 BleedIncapPSC.SetRotation(rotator(vect(0, 0, 1)) + class'KFSM_Stunned'.default.DazedFXRelativeRotation);
  3993.             }
  3994.         }
  3995.         //We've underrun the threshold, remove PSC
  3996.         else if ((CurrentStrength == 0 || !IsAliveAndWell()) && BleedIncapPSC != none)
  3997.         {
  3998.             BleedIncapPSC.DeactivateSystem();
  3999.             DetachComponent(BleedIncapPSC);
  4000.             BleedIncapPSC = none;
  4001.         }
  4002.     }
  4003. }
  4004.  
  4005. private final simulated function SpawnHeadShotFX(KFPlayerReplicationInfo DamagerPRI)
  4006. {
  4007.     local KFPlayerController KFPC;
  4008.     local HeadshotEffect SHeadshotEffect;
  4009.     local vector SpawnVector;
  4010.  
  4011.     if (DamagerPRI != none)
  4012.     {
  4013.         if (WorldInfo.NetMode != NM_DedicatedServer)
  4014.         {
  4015.             KFPC = KFPlayerController(WorldInfo.GetALocalPlayerController());
  4016.  
  4017.             if (KFPC == none || (KFPC.bHideRemotePlayerHeadshotEffects && DamagerPRI != KFPC.PlayerReplicationInfo))
  4018.             {
  4019.                 return;
  4020.             }
  4021.             SHeadshotEffect = class'KFHeadShotEffectList'.static.GetUnlockedHeadshotEffect(DamagerPRI.GetHeadShotEffectID());
  4022.             if (SHeadshotEffect.Id != INDEX_NONE)
  4023.             {
  4024.                 Mesh.GetSocketWorldLocationAndRotation(class'KFSM_Stunned'.default.DazedFXSocketName, SpawnVector);
  4025.                 WorldInfo.MyEmitterPool.SpawnEmitter(SHeadshotEffect.EffectPS, SpawnVector);
  4026.                 HeadShotAkComponent.PlayEvent(SHeadshotEffect.HeadshotSoundEffect, true, true);
  4027.             }
  4028.         }
  4029.     }
  4030. }
  4031.  
  4032. /*********************************************************************************************
  4033.  * @name    Ephemeral Stats tracking
  4034. ********************************************************************************************* */
  4035. static function bool IsLargeZed(){ return default.bLargeZed; }
  4036.  
  4037. static event bool IsABoss(){ return false; }
  4038.  
  4039. /*********************************************************************************************
  4040.  * @name    Perk related
  4041. ********************************************************************************************* */
  4042. simulated event UpdateSpottedStatus();
  4043. static function bool IsStalkerClass(){ return default.bIsStalker${1}< ${3} > }
  4044. static function bool IsCrawlerClass(){ return default.bIsCrawler${1}< ${3} > }
  4045. static function bool IsFleshpoundClass(){ return default.bIsFleshpound${1}< ${3} > }
  4046. static function bool IsClotClass(){ return default.bIsClot${1}< ${3} > }
  4047. static function bool IsBloatClass(){ return default.bIsBloat${1}< ${3} > }
  4048.  
  4049. function float GetPerkDoTScaler( optional Controller InstigatedBy, optional class<KFDamageType> KFDT )
  4050. {
  4051.     local KFPlayerController KFPC;
  4052.     local KFPerk InstigatorPerk;
  4053.     local float DoTScaler;
  4054.  
  4055.     DoTScaler = 1.f;
  4056.  
  4057.     if( InstigatedBy != none )
  4058.     {
  4059.         KFPC = KFPlayerController(InstigatedBy);
  4060.         if( KFPC != none )
  4061.         {
  4062.             InstigatorPerk = KFPC.GetPerk();
  4063.             if( InstigatorPerk != none )
  4064.             {
  4065.                 DoTScaler += InstigatorPerk.GetDoTScalerAdditions(KFDT);
  4066.             }
  4067.         }
  4068.     }
  4069.  
  4070.     return DoTScaler;
  4071. }
  4072.  
  4073. /*********************************************************************************************
  4074.  * @name    Overhead Debug Text
  4075. ********************************************************************************************* */
  4076.  
  4077. /**
  4078.  * Sets debug text rendering
  4079.  * @param   bTurnOn true to enable, false to disable over-NPC-head debug text rendering
  4080.  */
  4081. function SetDebugTextRendering( bool bTurnOn )
  4082. {
  4083.     local PlayerController PC;
  4084.     local KFHUDBase KFHud;
  4085.  
  4086.     bDebug_DrawOverheadInfo = bTurnOn;
  4087.  
  4088.     // final local player's hud object
  4089.     ForEach LocalPlayerControllers(class'PlayerController', PC)
  4090.     {
  4091.         KFHud = KFHUDBase(PC.MyHUD);
  4092.     }
  4093.  
  4094.     if ( KFHud != None )
  4095.     {
  4096.         // Add self to player HUD PostRenderedActor list
  4097.         KFHud.SetPostRenderingFor(bTurnOn, self);
  4098.     }
  4099. }
  4100.  
  4101. /** Called by HUD for actors in HUD's PostRenderedActor list */
  4102. simulated event PostRenderFor( PlayerController PC, Canvas Canvas, vector CameraPosition, vector CameraDir )
  4103. {
  4104.     local KFHUDBase PCHUD;
  4105.     local bool bShowAllCategories;
  4106.     local vector2d ScreenPos;
  4107.  
  4108.     PCHUD = KFHUDBase(PC.myHUD );
  4109.  
  4110.     if( PCHUD.ShouldDisplayDebug('All') || PCHUD.ShouldDisplayDebug('AllVerbose') )
  4111.     {
  4112.         bShowAllCategories = true;
  4113.     }
  4114.  
  4115.     if( MyKFAIC != none )
  4116.     {
  4117.         if( bShowAllCategories || PCHUD.ShouldDisplayDebug('AIMovement') || PCHUD.ShouldDisplayDebug('AIPathing') )
  4118.         {
  4119.             MyKFAIC.DrawDebugOverheadMovementPhaseData( PCHUD, ScreenPos );
  4120.         }
  4121.         if( bShowAllCategories || PCHUD.ShouldDisplayDebug('BehaviorTree') )
  4122.         {
  4123.             MyKFAIC.DrawBehaviorTreeIconOverhead( PCHUD );
  4124.         }
  4125.     }
  4126.  
  4127.     if( PC != none && PC.myHUD != none && bDebug_DrawOverheadInfo )
  4128.     {
  4129.         // Note: HUD type will either be KFHUDBase or KFDebugCameraHUD (for ToggleDebugCamera mode) which is a KFHDUBase subclass.
  4130.         // ...will need to make sure any other similar code works for both types.
  4131.         PCHUD = KFHUDBase(PC.myHUD );
  4132.         if( PCHUD != none )
  4133.         {
  4134.             DrawDebugOverheadText( PCHUD, ScreenPos );
  4135.         }
  4136.     }
  4137.     else if( PC != none && PC.myHUD != none && bDebug_DrawSprintingOverheadInfo )
  4138.     {
  4139.         PCHUD = KFHUDBase(PC.myHUD );
  4140.         if( PCHUD != none )
  4141.         {
  4142.             DrawDebugOverheadSprintingText( PCHUD );
  4143.         }
  4144.     }
  4145. }
  4146.  
  4147. function DrawDebugOverheadText( KFHUDBase HUD, Out Vector2d ScreenPos )
  4148. {
  4149.     local Texture2D         Icon;
  4150.     local PlayerController  PC;
  4151.     local Canvas            Canvas;
  4152.     local Vector            CameraLoc, ScreenLoc;
  4153.     local Rotator           CameraRot;
  4154.     local float             X, Y;
  4155.     local float             DOT;
  4156.     local array<string>     OverheadTexts;
  4157.     local array<Color>      OverheadColors;
  4158.     local int i;
  4159.  
  4160.     if( !IsAliveAndWell() )
  4161.     {
  4162.         return;
  4163.     }
  4164.  
  4165.     Canvas = HUD.Canvas;
  4166.     ScreenLoc = Canvas.Project( Location + vect(0,0,1) * GetCollisionHeight() * 1.5f );
  4167.     if( ScreenLoc.X < 0 || ScreenLoc.X >= HUD.Canvas.ClipX || ScreenLoc.Y < 0 && ScreenLoc.Y >= HUD.Canvas.ClipY )
  4168.     {
  4169.         return;
  4170.     }
  4171.  
  4172.     PC = HUD.PlayerOwner;
  4173.     Canvas.SetDrawColor(0,255,64);
  4174.     OverheadColors[OverheadColors.Length] = MakeColor(0,255,64);
  4175.  
  4176.     PC.GetPlayerViewPoint( CameraLoc, CameraRot );
  4177.     Dot = vector( CameraRot ) dot ( Location - CameraLoc );
  4178.     if( Dot < 0.5f )
  4179.     {
  4180.         return;
  4181.     }
  4182.     // Draw Icon (need some unique Zed icons)
  4183.     Icon = Texture2D'ENG_EditorResources_TEX.AI.S_AI';
  4184.     if (Icon != None)
  4185.     {
  4186.         Canvas.SetPos( ScreenLoc.X - Icon.SizeX / 2, ScreenLoc.Y - Icon.SizeY / 2, ScreenLoc.Z );
  4187.         Canvas.DrawTexture( Icon, 1.f );
  4188.         X = ScreenLoc.X + Icon.SizeX/2 + 5;
  4189.         Y = ScreenLoc.Y - Icon.SizeY/2;
  4190.     }
  4191.     else
  4192.     {
  4193.         X = ScreenLoc.X;
  4194.         Y = ScreenLoc.Y;
  4195.     }
  4196.  
  4197.     if( ScreenPos.X == 0 && ScreenPos.Y == 0 )
  4198.     {
  4199.         Canvas.SetPos( X, Y );
  4200.     }
  4201.     else
  4202.     {
  4203.         Canvas.SetPos( ScreenPos.X, ScreenPos.Y );
  4204.     }
  4205.  
  4206.     Canvas.Font = class'Engine'.Static.GetSmallFont();
  4207.  
  4208.     GetOverheadDebugText( HUD, OverheadTexts, OverheadColors );
  4209.  
  4210.     // Draw the AI Command info, etc
  4211.     if( MyKFAIC != none )
  4212.     {
  4213.         MyKFAIC.GetCommandStack( HUD, OverheadTexts, OverheadColors );
  4214.     }
  4215.  
  4216.     for (i = 0; i < OverheadTexts.length; i++)
  4217.     {
  4218.         // Set custom color if it exists
  4219.         if( OverheadColors[i] != MakeColor(0,0,0,0) )
  4220.         {
  4221.             Canvas.SetDrawColor(OverheadColors[i].R, OverheadColors[i].G, OverheadColors[i].B, 255);
  4222.         }
  4223.         else
  4224.         {
  4225.             Canvas.SetDrawColor(0, 255, 64, 255);
  4226.         }
  4227.  
  4228.         Canvas.DrawText(OverheadTexts[i]);
  4229.     }
  4230.  
  4231.     ScreenPos.X = Canvas.CurX;
  4232.     ScreenPos.Y = Canvas.CurY;
  4233.  
  4234.     if( HUD.ShouldDisplayDebug('AIMovement') )
  4235.     {
  4236.        DrawDebugSphere( Location - vect(0,0,1) * (GetCollisionHeight() - MaxStepHeight), 8, 10, 255, 255, 0, FALSE); // Yellow = MaxStepHeight
  4237.        DrawDebugSphere( Location + vect(0,0,1) * MaxJumpHeight, 15, 10, 0, 255, 0, FALSE); // Green = MaxJumpHeight
  4238.     }
  4239.  
  4240. }
  4241.  
  4242. function DrawDebugOverheadSprintingText( KFHUDBase HUD )
  4243. {
  4244.     local Texture2D         moveTypeIcon;
  4245.     local PlayerController  plyCtrl;
  4246.     local Canvas            displayCanvas;
  4247.     local Vector            plyCameraLoc, plyScreenLoc;
  4248.     local Rotator           plyCameraRot;
  4249.     local String            displayStr;
  4250.     local float             displayX, displayY;
  4251.     local float             infrontDOT;
  4252.     local Color             newTextColor;
  4253.  
  4254.     if( !IsAliveAndWell() )
  4255.     {
  4256.         return;
  4257.     }
  4258.  
  4259.     displayCanvas = HUD.Canvas;
  4260.     plyScreenLoc = displayCanvas.Project( Location + vect(0,0,1) * GetCollisionHeight() * 1.5f );
  4261.     if( plyScreenLoc.X < 0 || plyScreenLoc.X >= HUD.Canvas.ClipX || plyScreenLoc.Y < 0 && plyScreenLoc.Y >= HUD.Canvas.ClipY )
  4262.     {
  4263.         return;
  4264.     }
  4265.  
  4266.     plyCtrl = HUD.PlayerOwner;
  4267.     displayCanvas.SetDrawColor(255,255,255);
  4268.  
  4269.     plyCtrl.GetPlayerViewPoint( plyCameraLoc, plyCameraRot );
  4270.     infrontDOT = vector( plyCameraRot ) dot ( Location - plyCameraLoc );
  4271.     if( infrontDOT < 0.5f )
  4272.     {
  4273.         return;
  4274.     }
  4275.  
  4276.     if( bDebug_UseIconForShowingSprintingOverheadInfo )
  4277.     {
  4278.         if( bIsSprinting )
  4279.         {
  4280.             moveTypeIcon = MyKFAIC.MyAIDirector.GetDebugIsSprintingIcon();
  4281.         }
  4282.         else
  4283.         {
  4284.             moveTypeIcon = MyKFAIC.MyAIDirector.GetDebugIsWalkingIcon();
  4285.         }
  4286.     }
  4287.     else
  4288.     {
  4289.         moveTypeIcon = none;
  4290.         if( bIsSprinting )
  4291.         {
  4292.             displayStr = "S";
  4293.             newTextColor = class'HUD'.default.RedColor;
  4294.         }
  4295.         else
  4296.         {
  4297.             displayStr = "W";
  4298.             newTextColor = class'HUD'.default.GreenColor;
  4299.         }
  4300.     }
  4301.  
  4302.  
  4303.     if (moveTypeIcon != None)
  4304.     {
  4305.         displayCanvas.SetPos( plyScreenLoc.X - moveTypeIcon.SizeX / 2, plyScreenLoc.Y - moveTypeIcon.SizeY / 2, plyScreenLoc.Z );
  4306.         displayCanvas.DrawTexture( moveTypeIcon, 1.f );
  4307.         displayX = plyScreenLoc.X + moveTypeIcon.SizeX/2 + 5;
  4308.         displayY = plyScreenLoc.Y - moveTypeIcon.SizeY/2;
  4309.     }
  4310.     else
  4311.     {
  4312.         displayX = plyScreenLoc.X - GetCollisionRadius();
  4313.         displayY = plyScreenLoc.Y;
  4314.     }
  4315.     displayCanvas.SetPos( displayX, displayY );
  4316.  
  4317.  
  4318.     if( Len(displayStr) > 0 )
  4319.     {
  4320.         displayCanvas.Font = MyKFAIC.MyAIDirector.GetAiDebugScreenLargeFont();
  4321.         class'KFAIController'.static.DrawDebugText( HUD, displayStr, newTextColor );
  4322.     }
  4323. }
  4324.  
  4325. simulated function GetOverheadDebugText( KFHUDBase HUD, out array<string> OverheadTexts, out array<Color> OverheadColors )
  4326. {
  4327.     local string DebugText;
  4328.     local KFGameInfo KFGI;
  4329.     local float HealthMod;
  4330.     local float HeadHealthMod;
  4331.     local bool bShowAll, bShowAllVerbose;
  4332.  
  4333.     if( HUD.ShouldDisplayDebug('All') )
  4334.     {
  4335.         bShowAll = true;
  4336.     }
  4337.  
  4338.     if( HUD.ShouldDisplayDebug('AllVerbose') )
  4339.     {
  4340.         bShowAll = true;
  4341.         bShowAllVerbose = true;
  4342.     }
  4343.  
  4344.     KFGI = KFGameInfo(WorldInfo.Game);
  4345.     if ( KFGI != none )
  4346.     {
  4347.         KFGI.DifficultyInfo.GetAIHealthModifier(self, KFGI.GetModifiedGameDifficulty(), KFGI.GetLivingPlayerCount(), HealthMod, HeadHealthMod);
  4348.         if( bShowAllVerbose || HUD.ShouldDisplayDebug('ZedHealthVerbose') )
  4349.         {
  4350.             DebugText = " Health: "$Health
  4351.                 $" HeadHealth: "$HitZones[HZI_HEAD].GoreHealth$"\n"
  4352.                 $" Starting Health: "$HealthMod*default.Health
  4353.                 $" Starting HeadHealth: "$HeadHealthMod*Default.HitZones[HZI_HEAD].GoreHealth$"\n"
  4354.                 $" Health Modifier: "$HealthMod
  4355.                 $" Default Health: "$default.Health
  4356.                 $" Default HeadHealth: "$Default.HitZones[HZI_HEAD].GoreHealth;
  4357.         }
  4358.         else if( bShowAll || HUD.ShouldDisplayDebug('ZedHealth') )
  4359.         {
  4360.             DebugText = " Health: "$Health
  4361.                 $" HeadHealth: "$HitZones[HZI_HEAD].GoreHealth
  4362.                 $" HeadHealth %: "$(HitZones[HZI_HEAD].GoreHealth/(HeadHealthMod*Default.HitZones[HZI_HEAD].GoreHealth) * 100);
  4363.         }
  4364.     }
  4365.  
  4366.     OverheadTexts[OverheadTexts.Length] = DebugText;
  4367.  
  4368.     if( bShowAll || HUD.ShouldDisplayDebug('AITargeting') )
  4369.     {
  4370.         if( MyKFAIC != none )
  4371.         {
  4372.             DebugText = "---------- AI Targeting ----------\n";
  4373.  
  4374.             if ( MyKFAIC.Enemy != None )
  4375.             {
  4376.                  DebugText = DebugText@"ENEMY: "$MyKFAIC.Enemy.GetHumanReadableName()$" Enemy Dist: "$VSize(MyKFAIC.Enemy.Location - Location)$"\n";
  4377.             }
  4378.             else
  4379.             {
  4380.                 DebugText = DebugText@"ENEMY: NO Enemy "$"\n";
  4381.             }
  4382.  
  4383.  
  4384.             if( MyKFAIC.Focus != none )
  4385.             {
  4386.                 DebugText = DebugText@"FOCUS: "$MyKFAIC.Focus$"\n";
  4387.                 DrawDebugLine(Mesh.GetBoneLocation(HeadBoneName), MyKFAIC.Focus.Location, 255, 255, 0, FALSE);  // Yellow = focus
  4388.             }
  4389.             if( MyKFAIC.GetFocalPoint() != vect(0,0,0) )
  4390.             {
  4391.                 DrawDebugLine(Mesh.GetBoneLocation(HeadBoneName), MyKFAIC.GetFocalPoint(), 255, 255, 0, FALSE);  // Yellow = focal point
  4392.             }
  4393.             OverheadTexts[OverheadTexts.Length] = DebugText;
  4394.         }
  4395.     }
  4396.  
  4397.     if( bShowAll || HUD.ShouldDisplayDebug('AIMovement') )
  4398.     {
  4399.         DebugText = "---------- AI MOVEMENT ----------\n";
  4400.         DebugText = DebugText$"Velocity: "$VSize(Velocity)$" X: "$Velocity.X$" Y: "$Velocity.Y$" Z: "$Velocity.Z$" UU/S, "$VSize(Velocity)/100$"\n";
  4401.         DebugText = DebugText$"Acceleration: "$VSize(Acceleration)$" X: "$Acceleration.X$" Y: "$Acceleration.Y$" Z: "$Acceleration.Z$" Physics: "$GetPhysicsName()$"\n";
  4402.         //DebugText = DebugText$"SuperSpeed: "$IsUsingSuperSpeed()$" LastLOSOrRelevantTime: "$`TimeSince(LastLOSOrRelevantTime)$" LastRenderTime: "$`TimeSince(Mesh.LastRenderTime)$"\n";
  4403.         DebugText = DebugText$"SuperSpeed: "$IsUsingSuperSpeed()$" LastLOSOrRelevantTime: "$`TimeSince(LastLOSOrRelevantTime)$" LastRenderTime: "$`TimeSince(LastRenderTime)$"\n";
  4404.         if( MyKFAIC != none )
  4405.         {
  4406.             if( MyKFAIC.bPreparingMove )
  4407.             {
  4408.                 DebugText = DebugText@"bPreparingMove:"$MyKFAIC.bPreparingMove;
  4409.             }
  4410.  
  4411.             //DebugText = DebugText@"AICommand: "$MyKFAIC.GetActiveCommand();
  4412.         }
  4413.         OverheadTexts[OverheadTexts.Length] = DebugText;
  4414.     }
  4415. }
  4416.  
  4417. /**
  4418.  * Dialog
  4419.  **/
  4420.  
  4421. /** Returns (hardcoded) dialog event ID for when players kills this zed type */
  4422. function int GetKillerDialogID()
  4423. {
  4424.     return 65;//KILL_Generic
  4425. }
  4426.  
  4427. /** Returns (hardcoded) dialog event ID for when players spots this zed type */
  4428. function int GetSpotterDialogID()
  4429. {
  4430.     return 125;//SPOTZ_Generic
  4431. }
  4432.  
  4433. function UpdateDeadHorseStreak( bool bStillActive)
  4434. {
  4435.     if( bStillActive )
  4436.     {
  4437.         DeadHorseHitStreakAmt++;
  4438.     }
  4439.     else
  4440.     {
  4441.         DeadHorseHitStreakAmt = 1;
  4442.     }
  4443.  
  4444.     LastDeadHorseHitTime = WorldInfo.TimeSeconds;
  4445. }
  4446.  
  4447. /** Returns (hardcoded) trader advice dialog ID */
  4448. static function int GetTraderAdviceID()
  4449. {
  4450.     return -1;
  4451. }
  4452.  
  4453. /** Play sounds when jumping and landing */
  4454. function PlayLeapedDialog();
  4455. function PlayLandedDialog();
  4456.  
  4457. function MotivatePlayerToAttack( float Percentage, class<DamageType> AntiGriefDamageTypeClass )
  4458. {
  4459.     local PlayerController PC;
  4460.  
  4461.     PC = PlayerController(Controller);
  4462.     if( PC != none && WorldInfo.TimeSeconds - LastAttackHumanWarningTime > 9 )
  4463.     {
  4464.         PC.ReceiveLocalizedMessage( class'KFLocalMessage_Priority', GMT_AttackHumanPlayers );
  4465.         LastAttackHumanWarningTime = WorldInfo.TimeSeconds;
  4466.     }
  4467.  
  4468.     TakeDamage( int(float(HealthMax) * 0.05f), none, Location + vRand()*5.f, vRand(), AntiGriefDamageTypeClass );
  4469. }
  4470.  
  4471. /*********************************************************************************************
  4472.  * @name    UI / Localization
  4473.  ********************************************************************************************* */
  4474. /**Looks up and returns localized name */
  4475. static function string GetLocalizedName()
  4476. {
  4477.     local string MonsterName;
  4478.  
  4479.     //check if we have a seasonal variant
  4480.     MonsterName = Localize("Zeds", String(default.LocalizationKey) $ GetSeasonalLocalizationSuffix(), "KFGame");
  4481.  
  4482.     //Has a question mark in it, which would indicate failure. return default.
  4483.     if (InStr(MonsterName, "?") >= 0)
  4484.     {
  4485.         MonsterName = Localize("Zeds", String(default.LocalizationKey), "KFGame");
  4486.     }
  4487.  
  4488.     return MonsterName;
  4489. }
  4490.  
  4491. static function string GetSeasonalLocalizationSuffix()
  4492. {
  4493.     //Remove any year information, just get 1s digit
  4494.     switch (class'KFGameEngine'.static.GetSeasonalEventID() % 10)
  4495.     {
  4496.     case SEI_Spring:
  4497.         return "_Spring";
  4498.     case SEI_Summer:
  4499.         return "_Summer";
  4500.     case SEI_Fall:
  4501.         return "_Fall";
  4502.     case SEI_Winter:
  4503.         return "_Winter";
  4504.     default:
  4505.         return "";
  4506.     }
  4507.  
  4508.     return "";
  4509. }
  4510.  
  4511. /*********************************************************************************************
  4512. * @name   Armor
  4513. ********************************************************************************************* */
  4514.  
  4515. function ZedExplodeArmor(int ArmorZoneIdx, name ArmorZoneName)
  4516. {
  4517.     if (ArmorInfo != none)
  4518.     {
  4519.         ArmorInfo.ExplodeArmor(ArmorZoneIdx, ArmorZoneName);
  4520.     }
  4521. }
  4522.  
  4523. /*********************************************************************************************
  4524.  * @name   Achievements
  4525.  ********************************************************************************************* */
  4526.  
  4527. native protected function bool ShouldGrandOnDeathAchievement();
  4528. native protected function int GetZedOnDeathAchievement();
  4529. native function DisablebOnDeathAchivement();
  4530.  
  4531. DefaultProperties
  4532. {
  4533.     // ---------------------------------------------
  4534.     // AI / Navigation
  4535.     PeripheralVision=-1.f // 360
  4536.     Alertness=1.f
  4537.     SightRadius=16384.f
  4538.     ControllerClass=class'KFGame.KFAIController_Monster'
  4539.     PursuitSpeedScale=1.f
  4540.     PathSearchType=PST_Constraint
  4541.     AccelConvergeFalloffDistance=400.f
  4542.     bAllowAccelSmoothing=false
  4543.     MatchEnemySpeedAtDistance=200.f
  4544.     MinimumEnemySpeedToMatch=280.f
  4545.     bModifyReachSpecCost=true
  4546.     bDebug_DrawSprintingOverheadInfo=false
  4547.  
  4548.     LedgeCheckThreshold=350.0f
  4549.     bDebug_UseIconForShowingSprintingOverheadInfo=true
  4550.     CollisionRadiusForReducedZedOnZedPinchPointCollisionState=1
  4551.  
  4552.     Begin Object Name=KFPawnSkeletalMeshComponent
  4553.         WireframeColor=(R=255,G=255,B=0,A=255)
  4554.         bUseAsOccluder=TRUE
  4555.     End Object
  4556.  
  4557.     // ---------------------------------------------
  4558.     // Special Moves
  4559.     Begin Object Name=SpecialMoveHandler_0
  4560.         SpecialMoveClasses(SM_MeleeAttack)       =class'KFGame.KFSM_MeleeAttack'
  4561.         SpecialMoveClasses(SM_MeleeAttackDoor)   =class'KFSM_DoorMeleeAttack'
  4562.         SpecialMoveClasses(SM_GrappleAttack)         =class'KFGame.KFSM_GrappleCombined'
  4563.         SpecialMoveClasses(SM_Stumble)           =class'KFGame.KFSM_Stumble'
  4564.         SpecialMoveClasses(SM_RecoverFromRagdoll)=class'KFGame.KFSM_RecoverFromRagdoll'
  4565.         SpecialMoveClasses(SM_Knockdown)         =class'KFSM_RagdollKnockdown'
  4566.         SpecialMoveClasses(SM_DeathAnim)         =class'KFSM_DeathAnim'
  4567.         SpecialMoveClasses(SM_Stunned)           =class'KFSM_Stunned'
  4568.         SpecialMoveClasses(SM_Frozen)            =class'KFSM_Frozen'
  4569.         SpecialMoveClasses(SM_Taunt)             =class'KFGame.KFSM_Zed_Taunt'
  4570.         SpecialMoveClasses(SM_WalkingTaunt)      =class'KFGame.KFSM_Zed_WalkingTaunt'
  4571.         SpecialMoveClasses(SM_BossTheatrics)     =class'KFGame.KFSM_Zed_Boss_Theatrics'
  4572.     End Object
  4573.  
  4574.     IncapSettings(AF_Poison)=(Cooldown=5.0, Duration=5.0,)
  4575.     IncapSettings(AF_Microwave)=(Cooldown=5.0, Duration=5.0,)
  4576.     IncapSettings(AF_Freeze)=(Cooldown=5.0)
  4577.     IncapSettings(AF_Snare)=(Cooldown=5.0, Duration=5.0,)
  4578.  
  4579.     // ---------------------------------------------
  4580.     // Movement / Physics
  4581.     bCanCrouch=false
  4582.     AccelRate=+02048.000000
  4583.     JumpZ=750.f
  4584.     HiddenGroundSpeed=600.f // same speed for all monsters
  4585.     bCanStrafe=true
  4586.     MaxJumpHeight=128.f
  4587.     MaxTurningRadius=64.f
  4588.     BumpDamageType=class'KFDT_NPCBump'
  4589.     BumpFrequency=0.5f
  4590.     bLimitFallAccel=TRUE
  4591.     SpeedAdjustTransitionRate=200.f
  4592.     InitialGroundSpeedModifier=1.0
  4593.  
  4594.     // ---------------------------------------------
  4595.     // Spawning
  4596.     MinSpawnSquadSizeType=EST_Small
  4597.  
  4598.     // ---------------------------------------------
  4599.     // Gameplay
  4600.     DifficultySettings=class'KFMonsterDifficultyInfo'
  4601.  
  4602.     Begin Object Class=KFMeleeHelperAI Name=MeleeHelper_0
  4603.         MaxHitRange=180.f
  4604.         BaseDamage=6.f
  4605.     End Object
  4606.     MeleeAttackHelper=MeleeHelper_0
  4607.  
  4608.     bHasExtraSprintJumpVelocity=false
  4609.     bCanRage=false
  4610.     bCanGrabAttack=false
  4611.     bCanMeleeAttack=true
  4612.     ReachedEnemyThresholdScale=1.f
  4613.     ZedBumpDamageScale=1.f
  4614.     HeadlessBleedOutTime=5.f
  4615.     ParryResistance=1
  4616.     DifficultyDamageMod=1.0
  4617.     GameResistancePct=1.f
  4618.  
  4619.     //NapalmCheckInterval=0.5f
  4620.  
  4621.     // Blocking
  4622.     MinBlockFOV=0.1f
  4623.     BlockSprintSpeedModifier=0.75f
  4624.  
  4625.     bIsStalkerClass=false
  4626.     bIsCrawlerClass=false
  4627.     bIsFleshpoundclass=false
  4628.     bIsClotClass=false
  4629.     bIsBloatClass=false
  4630.  
  4631.     bDisableGoreMeshWhileAlive=false
  4632.  
  4633.     DamageInflationPercent = 1.0
  4634.     IntendedDamageInflationPercent = 1.0
  4635.     DamageInflationRate=1.0
  4636.     RandomColorIdx=-1
  4637.  
  4638.     // ---------------------------------------------
  4639.     // Hit Zones
  4640.     HitZones.Empty
  4641.     HitZones.Add((ZoneName=head,      BoneName=Head,         Limb=BP_Head, GoreHealth=20, DmgScale=1.1, SkinID=1))
  4642.     HitZones.Add((ZoneName=neck,      BoneName=Neck,         Limb=BP_Head, GoreHealth=20))
  4643.     HitZones.Add((ZoneName=chest,     BoneName=Spine2,       Limb=BP_Torso, GoreHealth=150))
  4644.     HitZones.Add((ZoneName=heart,     BoneName=Spine2,       Limb=BP_Special, GoreHealth=150))
  4645.     HitZones.Add((ZoneName=lupperarm, BoneName=LeftArm,      Limb=BP_LeftArm, GoreHealth=50))
  4646.     HitZones.Add((ZoneName=lforearm,  BoneName=LeftForearm,  Limb=BP_LeftArm, GoreHealth=15))
  4647.     HitZones.Add((ZoneName=lhand,     BoneName=LeftForearm,  Limb=BP_LeftArm, GoreHealth=20))
  4648.     HitZones.Add((ZoneName=rupperarm, BoneName=RightArm,     Limb=BP_RightArm, GoreHealth=50))
  4649.     HitZones.Add((ZoneName=rforearm,  BoneName=RightForearm, Limb=BP_RightArm, GoreHealth=15))
  4650.     HitZones.Add((ZoneName=rhand,     BoneName=RightForearm, Limb=BP_RightArm, GoreHealth=20))
  4651.     HitZones.Add((ZoneName=stomach,   BoneName=Spine1,       Limb=BP_Torso, GoreHealth=150))
  4652.     HitZones.Add((ZoneName=abdomen,   BoneName=Hips,         Limb=BP_Torso, GoreHealth=150))
  4653.     HitZones.Add((ZoneName=lthigh,    BoneName=LeftUpLeg,    Limb=BP_LeftLeg, GoreHealth=75))
  4654.     HitZones.Add((ZoneName=lcalf,     BoneName=LeftLeg,      Limb=BP_LeftLeg, GoreHealth=25))
  4655.     HitZones.Add((ZoneName=lfoot,     BoneName=LeftLeg,      Limb=BP_LeftLeg, GoreHealth=15))
  4656.     HitZones.Add((ZoneName=rthigh,    BoneName=RightUpLeg,   Limb=BP_RightLeg, GoreHealth=75))
  4657.     HitZones.Add((ZoneName=rcalf,     BoneName=RightLeg,     Limb=BP_RightLeg, GoreHealth=25))
  4658.     HitZones.Add((ZoneName=rfoot,     BoneName=RightLeg,     Limb=BP_RightLeg, GoreHealth=15))
  4659.  
  4660.     // Pad the arrays so we can modify them with the live update system if needed
  4661.     LiveDamageTypeModifiers.Add(())
  4662.     LiveDamageTypeModifiers.Add(())
  4663.     LiveDamageTypeModifiers.Add(())
  4664.     LiveDamageTypeModifiers.Add(())
  4665.     LiveDamageTypeModifiers.Add(())
  4666.     LiveDamageTypeModifiers.Add(())
  4667.     LiveDamageTypeModifiers.Add(())
  4668.     LiveDamageTypeModifiers.Add(())
  4669.     LiveDamageTypeModifiers.Add(())
  4670.     LiveDamageTypeModifiers.Add(())
  4671.     LiveDamageTypeModifiers.Add(())
  4672.     LiveDamageTypeModifiers.Add(())
  4673.     LiveDamageTypeModifiers.Add(())
  4674.     LiveDamageTypeModifiers.Add(())
  4675.     LiveDamageTypeModifiers.Add(())
  4676.     LiveDamageTypeModifiers.Add(())
  4677.  
  4678.     WeakSpotSocketNames.Add(FX_Dazed) // Head
  4679.  
  4680.     // List of BodySetups turned to phycsi for arm injury
  4681.     ArmPhysicsBoneList=("RightShoulder","RightArm","RightForeArm","RightHand")
  4682.  
  4683.     BleedIncapFX=ParticleSystem'FX_Gameplay_EMIT_THREE.FX_Incap_Bleed_01'
  4684.  
  4685.     // ---------------------------------------------
  4686.     // Animation
  4687.     bCanHeadTrack=true
  4688.  
  4689. `if(`notdefined(ShippingPC))
  4690.     DebugRadarTexture=Texture2D'GP_Debug.SineWaveMarker_TEX';
  4691.     `endif
  4692.         OnDeathAchievementID = INDEX_NONE
  4693.  
  4694.     bKnockdownWhenJumpedOn = True
  4695.  
  4696.     // ---------------------------------------------
  4697.     // sounds
  4698.     Begin Object Class=AkComponent name=SprintAkComponent0
  4699.         BoneName=dummy
  4700.         bStopWhenOwnerDestroyed=true
  4701.         bForceOcclusionUpdateInterval=true
  4702.         OcclusionUpdateInterval=0.2f
  4703.     End Object
  4704.     SprintAkComponent=SprintAkComponent0
  4705.     Components.Add(SprintAkComponent0)
  4706.  
  4707.     Begin Object Class=AkComponent name=HeadshotAkComponent0
  4708.         BoneName=head
  4709.         bStopWhenOwnerDestroyed=false
  4710.         bForceOcclusionUpdateInterval=true
  4711.         OcclusionUpdateInterval=0.2f
  4712.     End Object
  4713.     HeadShotAkComponent=HeadshotAkComponent0
  4714.     Components.Add(HeadshotAkComponent0)
  4715. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement