Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //=============================================================================
- // KFPawn_Monster
- //=============================================================================
- // Base pawn class for Zeds
- //=============================================================================
- // Killing Floor 2
- // Copyright (C) 2015 Tripwire Interactive LLC
- // - Andrew "Strago" Ladenberger
- //=============================================================================
- class KFPawn_Monster extends KFPawn
- abstract
- nativereplication
- native(Pawn)
- dependson(KFPawnAnimInfo, KFPawnVoiceGroup, KFPawnVoiceGroupEventData, KFSpawnVolume, KFHeadShotEffectList);
- `include(KFGame\KFGameAnalytics.uci);
- `include(KFGame\KFMatchStats.uci);
- /************************************
- * @name General info for AAR/Stats
- ************************************/
- var bool bLargeZed;
- var bool bVersusZed;
- /************************************
- * @name Content
- ************************************/
- /** Path for DLO of the MonsterArch */
- var const string MonsterArchPath;
- /**
- * Default content loaded by this pawn. Private, use GetCharacterMonsterInfo()
- * NOTE: DO NOT statically reference in defaults as it needs to be loaded dynamically
- */
- var private const KFCharacterInfo_Monster CharacterMonsterArch;
- /** List of variants that this pawn can be spawned as */
- var const array<class<KFPawn_Monster> > ElitePawn${1}< ${3} >
- /** Custom third person camera offsets */
- var() ViewOffsetData ThirdPersonViewOffset;
- /** Randomized color index from the monster arch if it exists for this zed */
- var int RandomColorIdx;
- /** List of static attachments from the character info */
- var array<StaticMeshComponent> StaticAttachList;
- /*********************************************************************************************
- * @name Combat
- ********************************************************************************************* */
- /** The chance that this monster pawn will sprint */
- var(Combat) float SprintChance;
- var(Combat) bool bCanGrabAttack;
- /** Odds (0-1) of evaluating whether to do a grab attack instead of a basic melee attack */
- var(Combat) float GrabAttackFrequency;
- var(Combat) bool bCanMeleeAttack;
- var(Combat) bool bHasExtraSprintJumpVelocity;
- /** The amount to scale this Zed's damage based on difficulty */
- var(Combat) float DifficultyDamageMod;
- /** GameInfo based damage resistance modifier cached at spawn time (0: Ignore, 1: Standard) */
- var(Combat) float GameResistancePct;
- /** Time until death after head is taken off */
- var(Combat) float HeadlessBleedOutTime;
- /** Enables crippled animation and behavior state */
- var repnotify bool bIsHeadless;
- /** If > 0, clientside head injury gore can be applied while still alive */
- var byte MaxHeadChunkGoreWhileAlive;
- /** Object that manages melee attacks, and stores default damage */
- var(Weapon) instanced KFMeleeHelperAI MeleeAttackHelper;
- var protected bool bHasReducedMeleeDamage;
- /** Total dosh this monster is worth on kill */
- var private const int DoshValue;
- /** XP this monster is worth (per difficulty) */
- var private const float XPValues[4];
- /** List of sockets representing weakpoint zone locations */
- var() array<name> WeakSpotSocketNames;
- /**
- * Information on resistant or vulnerable damage types
- * @todo: This is all static data so we should consider moving to the archetype
- */
- struct native DamageModifierInfo
- {
- /** A damage type to modify damage to this zed when it is received */
- var() class<DamageType> DamageType;
- /** Damage scale for this damage type. Additional array elements (MAX:4) can be used to modify for higher game difficulties */
- var() array<float> DamageScale;
- structdefaultproperties
- {
- DamageScale=(1.f)
- }
- };
- /** These damage types cause this zed to receive increased damage */
- var array<DamageModifierInfo> DamageTypeModifiers;
- /** Additional damage type modifiers that can override the defaults */
- var array<DamageModifierInfo> LiveDamageTypeModifiers;
- /** Special move cooldown info */
- struct native SpecialMoveCooldownInfo
- {
- var float CooldownTime;
- var transient float LastUsedTime;
- var ESpecialMove SMHandle;
- /** Used by UI */
- var Texture2D SpecialMoveIcon;
- var int Charges;
- var string NameLocalizationKey;
- var bool bShowOnHud;
- structdefaultproperties
- {
- SMHandle=SM_None
- bShowOnHud=true
- LastUsedTime=0.f
- SpecialMoveIcon=Texture2D'UI_Widgets.MenuBarWidget_SWF_IF'
- Charges=-1
- }
- };
- /** The amount to scale damage dealt by the fleshpound */
- var float ZedBumpDamageScale;
- var protected bool bShowHealth;
- /** Set only while inside AdjustDamage when calculating extra head explosion damage*/
- var transient bool bCheckingExtraHeadDamage;
- /** Base human-controlled melee damage */
- var(Combat) float HumanBaseMeleeDamage;
- /** Contains balance settings per game difficulty */
- var class<KFMonsterDifficultyInfo> DifficultySettings;
- /*********************************************************************************************
- * @name Player-controlled
- ********************************************************************************************* */
- /*
- * Move list classification used by MoveListGamepadScheme
- * Must be aligned firemode
- * -- label w/ default bind for readability
- */
- enum EPlayerZedGamepadMove
- {
- ZGM_Attack_R2, // firemode 0 -- R2
- ZGM_Block_R1, // firemode 1 -- R1
- ZGM_Melee_Square, // firemode 2 -- Square
- ZGM_Special_R3, // firemode 3 -- R3
- ZGM_Explosive_Ll, // firemode 4 -- L1
- ZGM_Attack_L2, // firemode 5 -- L2
- ZGM_Melee_Triangle, // firemode 6 -- Triangle
- };
- /* For gamepads remap the controls by type (aligned to EPlayerZedGamepadMove) */
- var array<ESpecialMove> MoveListGamepadScheme;
- /** Cooldown times for each special move ability */
- var array<SpecialMoveCooldownInfo> SpecialMoveCooldowns;
- /** Flag set after executing a jump */
- var bool bJumped;
- var transient float LastAttackHumanWarningTime;
- /*********************************************************************************************
- * @name Hit Reactions
- ********************************************************************************************* */
- /** Settings for the slowed down incapacitation effect */
- const SLOW_SPEED_MOD = 0.8f;
- /** Resistance value for special move reaction to being parried */
- var byte ParryResistance;
- /** true if we currently incap poisoned */
- var repnotify bool bIsPoisoned;
- /** Is microwave panic active? (server only) */
- var bool bMicrowavePanicked;
- /** Replicated material parameter */
- var repnotify byte RepInflateMatParam;
- /** If true, jumping on this pawn triggers ragdoll knockdown */
- var bool bKnockdownWhenJumpedOn;
- /** Cached health before taking damage for status effect resistance */
- var transient int OldHealth;
- /** Server-controlled disable of the gore mesh while alive */
- var bool bDisableGoreMeshWhileAlive;
- /** Whether or not to explode on death (set by server) */
- var bool bUseExplosiveDeath;
- /** Inflate on damage (LERP between none and full inflation based on health) */
- var bool bUseDamageInflation;
- /** Max damage inflation value */
- var float ZeroHealthInflation;
- /** Rate of inflation per second when inflating from damage, up to current intended */
- var float DamageInflationRate;
- /** Rate of deflation per second for damage inflation */
- var float DamageDeflationRate;
- /** Intended inflation. If a pawn is still inflating, this will be higher */
- var float IntendedDamageInflationPercent;
- /** Current inflation percent stored as float */
- var float DamageInflationPercent;
- /** Current replicated damage inflation */
- var repnotify byte RepDamageInflateParam;
- /** Gravity value to use on inflate death */
- var float InflateDeathGravity;
- /** Timer after death until inflation explosion */
- var float InflationExplosionTimer;
- /** Deflate value when affected by bleed */
- var repnotify byte RepBleedInflateMatParam;
- // Base emitter to use for incap effect
- var const ParticleSystem BleedIncapFX;
- // PSC attached to effected pawn when incap is in effect
- var ParticleSystemComponent BleedIncapPSC;
- /*********************************************************************************************
- * @name Anim Tree Controls
- ********************************************************************************************* */
- /* Plays panicked animation (e.g. On Fire, Poisoned) */
- var bool bPlayPanicked;
- /* Plays shambling animation (e.g. Headless, EMP) */
- var bool bPlayShambling;
- /** Cached anim nodes */
- var KFAnim_RandomScripted WalkBlendList;
- /*********************************************************************************************
- * @name Special Abilities
- ********************************************************************************************* */
- /** Struct containing block settings, used on a per-difficulty basis */
- struct native sBlockInfo
- {
- /** Chance of blocking an attack */
- var float Chance;
- /** Time from the start of a block until the zed unblocks again */
- var float Duration;
- /** Maximum number of attacks that can be blocked before the block is broken */
- var float MaxBlocks;
- /** Cooldown duration from the end of the last block */
- var float Cooldown;
- /** How much damage to accumulate (as a percentage of maximum health) before attempting to trigger a block */
- var float DamagedHealthPctToTrigger;
- /** How much to mitigate melee damage when blocking */
- var float MeleeDamageModifier;
- /** How much to mitigate non-melee damage when blocking */
- var float DamageModifier;
- /** How much to scale incap/affliction power by when blocking */
- var float AfflictionModifier;
- /** How much to multiply the chance by when in a solo game */
- var float SoloChanceMultiplier;
- structdefaultproperties
- {
- Chance=0.f
- MeleeDamageModifier=1.f
- DamageModifier=1.f
- AfflictionModifier=1.f
- }
- };
- var protected transient sBlockInfo DifficultyBlockSettings;
- /** Minimum FOV angle attacks must fall in to be blocked */
- var protected const float MinBlockFOV;
- /** Whether this zed is blocking attacks */
- var transient bool bIsBlocking;
- /** Multiplier applied to sprint speed when blocking */
- var protected const float BlockSprintSpeedModifier;
- /** The last time a successful block ended */
- var transient float LastBlockTime;
- var float KnockedDownBySonicWaveOdds;
- var bool bCloakOnMeleeEnd;
- var bool bIsCloakingSpottedByLP;
- var repnotify bool bIsCloakingSpottedByTeam;
- var float LastSpottedStatusUpdate;
- /** The most recent controller that made the pawn visible for other players */
- var KFPlayerController LastStoredCC;
- /** Rage flags */
- var bool bCanRage;
- var repnotify bool bIsEnraged;
- /** Particle system component for player alpha rally effect */
- var transient ParticleSystemComponent RallyPSC;
- /** Particle system components attached to the hands when rallied */
- var transient ParticleSystemComponent RallyHandPSCs[2];
- /** AI rally buff variables */
- struct native sRallyInfo
- {
- /** Whether this AI can rally in this difficulty */
- var bool bCanRally;
- /** Whether rally causes this AI to sprint */
- var bool bCauseSprint;
- /** How long the rally buff lasts for */
- var float RallyBuffTime;
- /** How much to modify incoming (taken) damage */
- var float TakenDamageModifier;
- /** How much to modify outgoing (dealt) damage */
- var float DealtDamageModifier;
- structdefaultproperties
- {
- bCanRally=true
- bCauseSprint=false
- RallyBuffTime=10.f
- TakenDamageModifier=1.f
- DealtDamageModifier=1.f
- }
- };
- var protected transient sRallyInfo DifficultyRallySettings;
- /*********************************************************************************************
- * @name Perks
- ********************************************************************************************* */
- var private bool bIsStalker${1}< ${3} >
- var private bool bIsCrawler${1}< ${3} >
- var private bool bIsFleshpound${1}< ${3} >
- var private bool bIsClot${1}< ${3} >
- var private bool bIsBloat${1}< ${3} >
- /*********************************************************************************************
- * @name Movement
- ********************************************************************************************* */
- /** The difficulty adjusted original GroundSpeed for this character */
- var transient float NormalGroundSpeed;
- /** The difficulty adjusted original SprintSpeed for this character */
- var transient float NormalSprintSpeed;
- /** The random ground speed modifier that is applied whent this character is initialized */
- var transient float InitialGroundSpeedModifier;
- /** Time interval for HiddenGroundSpeed check */
- var transient float LastAISpeedCheckTime;
- /** The last time this Zed was network relevant to someone, or had line of sight to someone */
- var transient float LastLOSOrRelevantTime;
- /** When true, will attempt to match player speed while in pursuit. Use only if you want to allow upperbody attacks while moving. */
- var bool bMatchEnemySpeed;
- /** Match enemy speed while in pursuit if enemy distance <= this value */
- var float MatchEnemySpeedAtDistance;
- /** Minimum speed of enemy required for me to begin matching speed */
- var float MinimumEnemySpeedToMatch;
- var float PursuitSpeedScale;
- /** Scales distance threshold that enemy is considered reached */
- var float ReachedEnemyThresholdScale;
- /** Instead of scaling, will just use this value for reached checks, if value > 0 and using walking physics */
- var float ReachedGoalThresh_Walking;
- /** Instead of scaling, will just use this value for reached checks, if value > 0 and using spider physics */
- var float ReachedGoalThresh_Spider;
- var float LastBumpTime;
- /** Used as an optimization for zeds and limits how frequently NotifyBump will be called */
- var float BumpFrequency;
- /** Set internally if pawn's collision size was modified at start of a jump */
- var bool bRestoreCollisionOnLand;
- /** Damage type applied to destructible actors when bumped */
- var class<KFDamageType> BumpDamageType;
- /** Damagetype applied to human players when hitting them in mid-air */
- var class<KFDamageType> JumpBumpDamageType;
- /** Footstep shakes activated in footstep sound animnotify */
- var protected float FootstepCameraShakeInnerRadius;
- var protected float FootstepCameraShakeOuterRadius;
- var CameraShake FootstepCameraShake;
- /** A ground speed this pawn has been set to blend to */
- var float DesiredAdjustedGroundSpeed;
- /** A sprint speed this pawn has been set to blend to */
- var float DesiredAdjustedSprintSpeed;
- /** The rate to transition speeds when blending to new movement speeds */
- var float SpeedAdjustTransitionRate;
- var AkComponent SprintAkComponent;
- var AkComponent HeadShotAkComponent;
- var AkEvent StartSprintingSound;
- var AkEvent SprintLoopingSound;
- var AkEvent StopSprintingSound;
- var bool bPlayingSprintLoop;
- /*********************************************************************************************
- * @name Dismemberment / Gore
- ********************************************************************************************* */
- /** The explosion effect should only be played once */
- var transient bool bPlayedExplosionEffect;
- /** Information regardint the currently attached gore chunks */
- struct native AttachedGoreChunkInfo
- {
- var int AttachmentIndex;
- var StaticMeshComponent AttachedComponent;
- };
- /** The currently attached gore chunks */
- var transient array<AttachedGoreChunkInfo> AttachedGoreChunks;
- /** Keeps track of the number of head chunks removed for the ZED */
- var transient int NumHeadChunksRemoved;
- /** Keep track of the head bones that have already been broken */
- var transient array<Name> BrokenHeadBones;
- /** How often to check for napalm infections */
- //var protected const float NapalmCheckInterval;
- /** Last time we checked for a napalm infection upon taking damage */
- var transient protected float LastNapalmInfectCheckTime;
- /** Is there a chanced that we explode when we die? Set by the firebug's Zed shrapnel skill */
- var bool bCouldTurnIntoShrapnel;
- /** Gameplay-facing disable of head destruction */
- var bool bDisableHeadless;
- /*********************************************************************************************
- * @name Dialog
- ********************************************************************************************* */
- var transient int DeadHorseHitStreakAmt;
- var transient float LastDeadHorseHitTime;
- /*********************************************************************************************
- * @name Debug
- ********************************************************************************************* */
- var bool bDebug_DrawOverheadInfo;
- var bool bDebug_DrawSprintingOverheadInfo;
- var const bool bDebug_UseIconForShowingSprintingOverheadInfo;
- var bool bDebug_SpawnedThroughCheat;
- /*********************************************************************************************
- * @name Door Navigation
- ********************************************************************************************* */
- var float DefaultCollisionRadius;
- var KFTrigger_ChokePoint CurrentChokePointTrigger;
- var bool bReducedZedOnZedPinchPointCollisionStateActive;
- var const float CollisionRadiusForReducedZedOnZedPinchPointCollisionState;
- /*********************************************************************************************
- * @name Spawning
- ********************************************************************************************* */
- /** When spawning in a spawn volume, the squad type as to be at least this big (can be bigger
- * if there are other zeds in the spawn squad that are larger). */
- var() ESquadType MinSpawnSquadSizeType;
- /*********************************************************************************************
- * @name Achievements
- ********************************************************************************************* */
- var protected const int OnDeathAchievementID;
- var protected bool bOnDeathAchivementbDisabled;
- /*********************************************************************************************
- * @name Armor
- ********************************************************************************************* */
- /** Armor component to handle armor damage and detachment. */
- var class<KFZedArmorInfo> ArmorInfo${1}< ${3} >
- var KFZedArmorInfo ArmorInfo;
- //Byte array of armor percentages, replicated to clients
- var repnotify byte RepArmorPct[3];
- //Bit field for the status of the armor zones. 1 = attached
- var repnotify byte ArmorZoneStatus;
- var byte PreviousArmorZoneStatus;
- //Hit FX overrides for hitting armor
- var const int OverrideArmorFXIndex;
- /*********************************************************************************************
- * @name Delegates
- ********************************************************************************************* */
- /** Declaration for attach/detach gore chunk delegates */
- simulated delegate bool GoreChunkAttachmentCriteria();
- simulated delegate bool GoreChunkDetachmentCriteria();
- replication
- {
- // Replicated to ALL
- if (bNetDirty)
- bIsHeadless, bIsPoisoned, bPlayPanicked, bPlayShambling, MaxHeadChunkGoreWhileAlive,
- RepInflateMatParam, RepDamageInflateParam, RepBleedInflateMatParam, bDisableGoreMeshWhileAlive,
- bDisableHeadless, InflateDeathGravity, InflationExplosionTimer, bUseDamageInflation, bUseExplosiveDeath;
- if ( bNetDirty && bCanCloak )
- bIsCloakingSpottedByTeam;
- if ( bNetDirty && bCanRage )
- bIsEnraged;
- if (Role == ROLE_Authority)
- ArmorZoneStatus, RepArmorPct;
- }
- cpptext
- {
- // Actor
- INT* GetOptimizedRepList( BYTE* InDefault, FPropertyRetirement* Retire, INT* Ptr, UPackageMap* Map, UActorChannel* Channel );
- virtual void TickSpecial( FLOAT DeltaSeconds );
- virtual void TickAuthoritative( FLOAT DeltaSeconds );
- // AI / navigation
- virtual UBOOL IgnoreBlockingBy( const AActor *Other) const;
- virtual void NotifyBump( AActor *Other, UPrimitiveComponent* OtherComp, const FVector &HitNormal );
- virtual FLOAT MaxSpeedModifier();
- virtual void UpdateAISuperSpeed();
- virtual void UpdateAIPursuitSpeed();
- virtual UBOOL IsGlider();
- virtual UBOOL ReachThresholdTest( const FVector &TestPosition, const FVector &Dest, AActor* GoalActor, FLOAT UpThresholdAdjust, FLOAT DownThresholdAdjust, FLOAT ThresholdAdjust );
- virtual void HandleSerpentineMovement(FVector& out_Direction, FLOAT Distance, const FVector& Dest);
- virtual INT ModifyCostForReachSpec(UReachSpec* Spec, INT Cost);
- virtual void NotifyBumpLevel(AActor* Wall, const FVector &HitLocation, const FVector& HitNormal);
- virtual void OnRigidBodyCollision(const FRigidBodyCollisionInfo& MyInfo, const FRigidBodyCollisionInfo& OtherInfo, const FCollisionImpactData& RigidCollisionData);
- virtual UBOOL ShouldTrace(UPrimitiveComponent* Primitive,AActor *SourceActor, DWORD TraceFlags);
- virtual void physicsRotation(FLOAT deltaTime, FVector OldVelocity);
- }
- native function bool SpiderPhysicsWallAdjust( vector HitNormal, actor HitActor );
- /** Tells the monster whether or not it should shrink its collision cylinder when going through a choke point */
- native function SetChokePointCollision( bool bUseChokeCollision );
- /** Checks if we are our current location encroaches any world geometry */
- native function bool CheckEncroachingWorldGeometry();
- /**
- * Check on various replicated data and act accordingly.
- */
- simulated event ReplicatedEvent(name VarName)
- {
- switch( VarName )
- {
- case nameof(bIsHeadless):
- StopAkEventsOnBone( 'head' );
- // No more auto aiming to this zed
- bCanBeAdheredTo=false;
- bCanBeFrictionedTo=false;
- break;
- case nameof(bIsPoisoned):
- AfflictionHandler.ToggleEffects(AF_Poison, bIsPoisoned);
- break;
- case nameof(RepInflateMatParam):
- AfflictionHandler.UpdateMaterialParameter(AF_Microwave, GetCurrentInflation());
- break;
- case nameof(RepBleedInflateMatParam):
- AfflictionHandler.UpdateMaterialParameter(AF_Bleed, ByteToFloat(RepBleedInflateMatParam));
- UpdateBleedIncapFX();
- break;
- case nameof(Controller):
- // Set audio switch based on AI or player-controlled
- SetSwitch( 'Player_Zed', IsHumanControlled() ? 'Player' : 'NotPlayer' );
- break;
- case nameof(bEmpDisrupted):
- if( bEmpDisrupted )
- {
- // If this pawn was disrupted, put all of its moves on cooldown
- PutAllMovesOnCooldown();
- }
- break;
- case nameof(RepDamageInflateParam):
- HandleDamageInflation();
- break;
- case nameof(RepArmorPct):
- if(ArmorInfo != none)
- {
- ArmorInfo.UpdateArmorPieces();
- }
- break;
- case nameof(ArmorZoneStatus):
- if (ArmorInfo != none)
- {
- ArmorInfo.UpdateArmorPieces();
- }
- break;
- case nameof(bIsEnraged):
- SetEnraged(bIsEnraged);
- break;
- }
- super.ReplicatedEvent( VarName );
- }
- /*********************************************************************************************
- * @name Static
- ********************************************************************************************* */
- // returns the zed's score/dosh value
- simulated static function int GetDoshValue()
- {
- return default.DoshValue;
- }
- simulated static function float GetXPValue(byte Difficulty)
- {
- return default.XPValues[Difficulty];
- }
- /** Gets the actual classes used for spawning. Can be overridden to replace this monster with another */
- static event class<KFPawn_Monster> GetAIPawnClassToSpawn()
- {
- local WorldInfo WI;
- WI = class'WorldInfo'.static.GetWorldInfo();
- if (default.ElitePawnClass.length > 0 && default.DifficultySettings != none && fRand() < default.DifficultySettings.static.GetSpecialSpawnChance(KFGameReplicationInfo(WI.GRI)))
- {
- return default.ElitePawnClass[Rand(default.ElitePawnClass.length)];
- }
- return default.class;
- }
- /** Load content archetype when map loads */
- native static final function PreloadContent();
- /** Called if preload was not called before 1st spawn */
- native private function LastChanceLoad();
- /*********************************************************************************************
- * @name Constructors, Destructors, and Loading
- ********************************************************************************************* */
- /** Called immediately before gameplay begins */
- simulated event PreBeginPlay()
- {
- CheckShouldAlwaysBeRelevant(); //these guys could be alive already so this is unreliable
- DefaultCollisionRadius = CylinderComponent.default.CollisionRadius;
- Super.PreBeginPlay();
- // If we don't have an archetype select one
- if ( CharacterArch == None )
- {
- // Preload should have been called already, but if not do it now!
- if ( CharacterMonsterArch == None )
- {
- LastChanceLoad();
- }
- if ( CharacterMonsterArch != None )
- {
- SetCharacterArch(CharacterMonsterArch);
- }
- }
- if ( CharacterArch == None )
- {
- `warn("Failed to find character info for KFMonsterPawn!");
- Destroy();
- }
- NormalGroundSpeed = default.GroundSpeed;
- NormalSprintSpeed = default.SprintSpeed;
- if (ArmorInfoClass != none)
- {
- ArmorInfo = new(self) ArmorInfo${1}< ${3} >
- }
- }
- simulated event CheckShouldAlwaysBeRelevant()
- {
- local KFGameReplicationInfo KFGRI;
- // Set to always relevant if we're the last few remaining
- KFGRI = KFGameReplicationInfo( WorldInfo.GRI );
- if( KFGRI != none && KFGRI.AIRemaining <= class'KFGameInfo'.static.GetNumAlwaysRelevantZeds() )
- {
- bAlwaysRelevant = true;
- }
- }
- /** Called immediately after gameplay begins */
- simulated event PostBeginPlay()
- {
- local KFGameReplicationInfo KFGRI;
- super.PostBeginPlay();
- // Set our (Network: ALL) difficulty-based settings
- KFGRI = KFGameReplicationInfo( WorldInfo.GRI );
- if( KFGRI != none )
- {
- SetRallySettings( DifficultySettings.static.GetRallySettings(self, KFGRI) );
- SetZedTimeSpeedScale( DifficultySettings.static.GetZedTimeSpeedScale(self, KFGRI) );
- }
- if( (WorldInfo.NetMode != NM_Client) && IsABoss() )
- {
- `AnalyticsLog(("boss_spawn", None, Class.Name));
- }
- }
- simulated function SetMeshLightingChannels(LightingChannelContainer NewLightingChannels)
- {
- local int i;
- super.SetMeshLightingChannels(NewLightingChannels);
- for (i = 0; i < StaticAttachList.Length; ++i)
- {
- if (StaticAttachList[i] != none)
- {
- StaticAttachList[i].SetLightingChannels(NewLightingChannels);
- }
- }
- }
- //update the portrait when we get a PRI
- simulated function NotifyTeamChanged()
- {
- if(CharacterArch != none)
- {
- CharacterArch.SetCharacterFromArch( self, KFPlayerReplicationInfo(PlayerReplicationInfo) );
- }
- }
- /** Called from Possessed event when this controller has taken control of a Pawn */
- function PossessedBy( Controller C, bool bVehicleTransition )
- {
- local string NPCName;
- local KFPlayerReplicationInfo KFPRI;
- local KFGameReplicationInfo KFGRI;
- Super.PossessedBy( C, bVehicleTransition );
- KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
- /** Set MyKFAIC for convenience to avoid casting */
- if( KFAIController(C) != none )
- {
- MyKFAIC = KFAIController( C );
- // If AI Zed has spawned during trader time, should be killed immediately.
- if (!(bDebug_SpawnedThroughCheat || WorldInfo.Game.GetStateName() == 'DebugSuspendWave') && KFGRI != none && !KFGRI.bWaveIsActive)
- {
- Suicide();
- return;
- }
- }
- bReducedZedOnZedPinchPointCollisionStateActive = false;
- //CollisionRadiusBeforeReducedZedOnZedPinchPointCollisionState = CollisionCylinderReducedPercentForSameTeamCollision;
- // Turn off air control for AI because it can mess up landing.
- `if(`notdefined(ShippingPC))
- if( IsHumanControlled() || MyKFAIC.bIsSimulatedPlayerController )
- `else
- if( IsHumanControlled() )
- `endif
- {
- KFPRI = KFPlayerReplicationInfo(C.PlayerReplicationInfo);
- if(KFPRI != none)
- {
- KFPRI.PlayerHealth = Health;
- KFPRI.PlayerHealthPercent = FloatToByte( float(Health) / float(HealthMax) );
- SetCharacterArch(CharacterMonsterArch, true);
- }
- if( Role == ROLE_Authority )
- {
- LastAttackHumanWarningTime = WorldInfo.TimeSeconds;
- }
- }
- else
- {
- AirControl = AIAirControl;
- }
- KFGameInfo(WorldInfo.Game).SetMonsterDefaults( self );
- if( MyKFAIC != none && MyKFAIC.PlayerReplicationInfo != None )
- {
- NPCName = string(self);
- NPCName = Repl(NPCName,"KFPawn_Zed","",false);
- PlayerReplicationInfo.PlayerName = NPCName;
- MyKFAIC.PlayerReplicationInfo.PlayerName = NPCName;
- }
- // Set our (Network: Server) difficulty-based settings
- if( KFGRI != none )
- {
- SetBlockSettings( DifficultySettings.static.GetBlockSettings(self, KFGRI) );
- // Set any AI-specific difficulty settings
- if( MyKFAIC != none )
- {
- MyKFAIC.EvadeOnDamageSettings = DifficultySettings.static.GetEvadeOnDamageSettings( self, KFGRI );
- }
- }
- if (ArmorInfo != none)
- {
- ArmorInfo.InitArmor();
- }
- // Set audio switch based on AI or player-controlled
- `if(`notdefined(ShippingPC))
- SetSwitch( 'Player_Zed', (IsHumanControlled() || (MyKFAIC != none && MyKFAIC.bIsSimulatedPlayerController)) ? 'Player' : 'NotPlayer' );
- `else
- SetSwitch( 'Player_Zed', IsHumanControlled() ? 'Player' : 'NotPlayer' );
- `endif
- }
- /*
- simulated event FellOutOfWorld(class<DamageType> dmgType)
- {
- local string Msg;
- `warn( self$" FELL OUT OF WORLD!" );
- if( Health > 0 )
- {
- msg = WorldInfo.TimeSeconds@self@GetFuncName()@"fell out of world! Current location:"@Location;
- if( MyKFAIC != None )
- {
- msg = msg@"Controller:"@MyKFAIC@"active command:"@MyKFAIC.GetActiveCommand();
- if( MyKFAIC.MoveTarget != none )
- {
- msg = msg@"MoveTarget:"@MyKFAIC.MoveTarget;
- }
- }
- `warn( msg );
- }
- super.FellOutOfWorld(dmgType);
- }*/ //log spam
- event BaseChange()
- {
- if( IsAliveAndWell() && MyKFAIC != none )
- {
- if( MyKFAIC.NotifyBaseChange( Base, Floor ) )
- {
- return;
- }
- }
- super.BaseChange();
- }
- simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
- {
- Super.PostInitAnimTree(SkelComp);
- WalkBlendList = KFAnim_RandomScripted(SkelComp.FindAnimNode('WalkRandomList'));
- }
- /** Initialize GoreHealth (Server only) */
- function ApplySpecialZoneHealthMod(float HealthMod)
- {
- //Update head
- HitZones[HZI_HEAD].GoreHealth = default.HitZones[HZI_HEAD].GoreHealth * HealthMod;
- HitZones[HZI_HEAD].MaxGoreHealth = HitZones[HZI_HEAD].GoreHealth;
- }
- function SetShieldScale(float InScale)
- {
- if (ArmorInfo != none)
- {
- ArmorInfo.SetShieldScale(InScale);
- }
- }
- /** Used by the Versus takeover code to determine if this zed can be taken over */
- function bool CanTakeOver()
- {
- local KFVersusNoTakeoverVolume KFNTV;
- if( bVersusZed || IsDoingSpecialMove() || IsHeadless() || !IsAliveAndWell() )
- {
- return false;
- }
- foreach TouchingActors( class'KFVersusNoTakeoverVolume', KFNTV )
- {
- return false;
- }
- return true;
- }
- simulated event Tick(float DeltaTime)
- {
- super.Tick(DeltaTime);
- if (WorldInfo.NetMode != NM_DedicatedServer && IsAliveAndWell())
- {
- if (bIsSprinting && VSizeSQ(Velocity) > 40000.f)
- {
- if (!bPlayingSprintLoop)
- {
- if (StartSprintingSound != none && !SprintAkComponent.IsPlaying(StartSprintingSound))
- {
- SprintAkComponent.PlayEvent(StartSprintingSound, true, true);
- }
- }
- if(SprintLoopingSound != none && !SprintAkComponent.IsPlaying(SprintLoopingSound))
- {
- SprintAkComponent.PlayEvent(SprintLoopingSound, true, true);
- bPlayingSprintLoop = true;
- }
- }
- else if (bPlayingSprintLoop && (!bIsSprinting || VSizeSQ(Velocity) <= 40000.f))
- {
- if (SprintLoopingSound != none && SprintAkComponent.IsPlaying(SprintLoopingSound))
- {
- SprintAkComponent.StopEvents();
- bPlayingSprintLoop = false;
- }
- if (StopSprintingSound != none && !SprintAkComponent.IsPlaying(StopSprintingSound))
- {
- SprintAkComponent.PlayEvent(StopSprintingSound, true, true);
- }
- }
- }
- }
- /*********************************************************************************************
- * @name Character Info Methods
- ********************************************************************************************* */
- simulated function SetCharacterArch(KFCharacterInfoBase Info, optional bool bForce)
- {
- super.SetCharacterArch(Info, bForce);
- PlayExtraVFX('AlwaysOn');
- }
- native function KFCharacterInfo_Monster GetCharacterMonsterInfo();
- /** If true, assign custom player controlled skin when available */
- simulated event bool UsePlayerControlledZedSkin()
- {
- return bVersusZed;
- }
- /*********************************************************************************************
- * @name Movement Methods
- ********************************************************************************************* */
- function float GetBlockingSprintSpeedModifier()
- {
- return BlockSprintSpeedModifier;
- }
- /** Set a desired movement speed adjustment that will blend in over time */
- function AdjustMovementSpeed( float SpeedAdjust )
- {
- DesiredAdjustedGroundSpeed = default.GroundSpeed * SpeedAdjust * InitialGroundSpeedModifier;
- if( IsDoingSpecialMove() )
- {
- DesiredAdjustedSprintSpeed = fMax( default.SprintSpeed * SpeedAdjust * InitialGroundSpeedModifier * SpecialMoves[SpecialMove].GetSprintSpeedModifier(), DesiredAdjustedGroundSpeed );
- }
- else
- {
- DesiredAdjustedSprintSpeed = fMax( default.SprintSpeed * SpeedAdjust * InitialGroundSpeedModifier, DesiredAdjustedGroundSpeed );
- }
- NormalGroundSpeed = DesiredAdjustedGroundSpeed;
- NormalSprintSpeed = DesiredAdjustedSprintSpeed;
- }
- /** Overridden to cause slight camera shakes when walking. */
- simulated event PlayFootStepSound(int FootDown)
- {
- if( WorldInfo.NetMode == NM_DedicatedServer )
- {
- return;
- }
- Super.PlayFootStepSound(FootDown);
- // 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.
- // Changed the IsDoingLatentMoveCheck() to a velocity check, so player-controlled zeds trigger camera shake as well.
- if( Physics == PHYS_Walking
- && Base != none
- && Mesh.RootMotionMode == RMM_Ignore
- && FootstepCameraShake != none
- && VSizeSQ(Velocity) >= 10000.f )
- {
- class'Camera'.static.PlayWorldCameraShake(FootstepCameraShake, self, Location, FootstepCameraShakeInnerRadius, FootstepCameraShakeOuterRadius, 1.3f, true);
- }
- }
- event SpiderBumpLevel( vector HitLocation, vector HitNormal, optional actor Wall );
- simulated event Bump( Actor Other, PrimitiveComponent OtherComp, Vector HitNormal )
- {
- Super.Bump( Other, OtherComp, HitNormal );
- if( SpecialMove != SM_None )
- {
- SpecialMoves[SpecialMove].NotifyBump( Other, HitNormal );
- }
- // Check for a midair bump
- if( Physics == Phys_Falling && JumpBumpDamageType != none && Other.GetTeamNum() != GetTeamNum() && VSizeSq2D(Velocity) > Square(GroundSpeed * 1.1) )
- {
- Other.TakeDamage( MeleeAttackHelper.BaseDamage, Controller, Other.Location, Normal(Velocity), JumpBumpDamageType );
- }
- }
- /** Called from AICommand_MoveToGoal, notifies us that we've bumped another KFPawn */
- function HandleMonsterBump( KFPawn_Monster Other, Vector HitNormal )
- {
- local KFPlayerController KFPC;
- if( !Other.IsNapalmInfected() && CanNapalmInfect(KFPC) )
- {
- InfectWithNapalm( Other, KFPC );
- }
- }
- /** Override to handle special berserker functionality */
- event HitWall( vector HitNormal, actor Wall, PrimitiveComponent WallComp )
- {
- local KFDoorActor Door;
- if (IsHumanControlled())
- {
- if (!Wall.bStatic && IsAliveAndWell())
- {
- Door = KFDoorActor(Wall);
- if (Door != none)
- {
- TryDestroyDoor(Door);
- }
- }
- }
- `AILog_Ext( GetFuncName()$"() Wall: "$Wall, 'BumpEvent', MyKFAIC );
- // Call our special notification
- NotifyCollideWithActor(HitNormal, Wall);
- Super.HitWall(HitNormal, Wall, WallComp);
- }
- /** Allows pawn to handle door bump events instead of controller */
- function bool HandleAIDoorBump(KFDoorActor Door)
- {
- return TryDestroyDoor(Door);
- }
- /** Destroy unwelded doors instantly when there are few players remaining if I'm a boss */
- function bool TryDestroyDoor(KFDoorActor Door)
- {
- if (IsABoss() && Door != none && !Door.bIsDoorOpen && !Door.bIsDestroyed && Door.WeldIntegrity == 0 && CanObliterateDoors())
- {
- Door.IncrementHitCount(self);
- Door.DestroyDoor(Controller);
- return true;
- }
- return false;
- }
- /** Determines if this pawn can plow through doors */
- function bool CanObliterateDoors()
- {
- // We only want the kool-aid man effect if we're sprinting
- if (IsABoss() && !bIsSprinting)
- {
- return false;
- }
- // Only allow door obliteration if there is one player remaining in a multiplayer game
- return true;
- }
- /** Notification that Zed collided with an actor */
- function bool NotifyCollideWithActor( Vector HitNormal, Actor Other )
- {
- local KFDestructibleActor KFDA;
- if( !Other.bStatic && IsHumanControlled() )
- {
- KFDA = KFDestructibleActor( Other );
- if( KFDA != none )
- {
- HandleDestructibleBump( KFDA, HitNormal );
- }
- }
- `AILog_Ext( GetFuncName()$"() Other: "$Other, 'BumpEvent', MyKFAIC );
- return false;
- }
- function HandleDestructibleBump( KFDestructibleActor Destructible, vector HitNormal )
- {
- Destructible.BumpedByMonster( self, HitNormal );
- }
- simulated event Touch(Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal)
- {
- if( MyKFAIC != none )
- {
- MyKFAIC.Touch( Other, OtherComp, HitLocation, HitNormal);
- }
- Super.Touch(Other, OtherComp, HitLocation, HitNormal);
- }
- function SetSprinting( bool bNewSprintStatus )
- {
- if( MyKFAIC != none )
- {
- if( !MyKFAIC.CanSetSprinting(bNewSprintStatus) )
- {
- return;
- }
- }
- // Disallow sprinting if blocking
- if( bIsBlocking && IsHumanControlled() )
- {
- if( bIsSprinting )
- {
- bNewSprintStatus = false;
- }
- else
- {
- return;
- }
- }
- super.SetSprinting(bNewSprintStatus);
- }
- /** Haven't seen this happen in a long time - logging it in case it does occur again */
- event StuckOnPawn (Pawn OtherPawn)
- {
- JumpOffPawn();
- if( MyKFAIC != none )
- {
- `AILog_Ext( self$" StuckOnPawn event at "$Location$" Base: "$Base,, MyKFAIC );
- MyKFAIC.DumpCommandStack();
- MyKFAIC.DumpCommandHistory();
- }
- }
- /** Monster jumped */
- function bool DoJump( bool bUpdating )
- {
- local vector JumpVelocity;
- local rotator ViewRotation;
- local vector ViewDirection;
- if (bCanJump)
- {
- if (IsHumanControlled())
- {
- if (IsDoingSpecialMove())
- {
- return false;
- }
- ViewRotation = GetViewRotation();
- ViewDirection = Normal(Vector(ViewRotation));
- if (bJumpCapable && !bIsCrouched && !bWantsToCrouch && (Physics == PHYS_Walking || Physics == PHYS_Ladder || Physics == PHYS_Spider))
- {
- if (Physics == PHYS_Spider)
- `if(`__TW_PATHFINDING_)
- Velocity = Velocity + (GetJumpZ() * Floor);
- `else
- Velocity = GetJumpZ() * Floor;
- `endif
- else if (Physics == PHYS_Ladder)
- Velocity.Z = 0;
- else if (bIsWalking)
- {
- Velocity.Z = default.JumpZ;
- }
- else
- {
- if (bIsSprinting && bHasExtraSprintJumpVelocity)
- {
- JumpVelocity = GetSprintJumpVelocity(ViewDirection);
- JumpVelocity.Z = GetJumpZ();
- Velocity += JumpVelocity;
- }
- else
- {
- Velocity.Z = GetJumpZ();
- }
- }
- if (Base != None && !Base.bWorldGeometry && Base.Velocity.Z > 0.f)
- {
- Velocity.Z += Base.Velocity.Z;
- }
- SetPhysics(PHYS_Falling);
- bJumped = true;
- return true;
- }
- return false;
- }
- else
- {
- return super.DoJump(bUpdating);
- }
- }
- }
- /** Returns the Z force used for a jump. Different move/physics states can return different values. */
- simulated function float GetJumpZ()
- {
- return JumpZ;
- }
- /** Returns the sprint jump velocity, used in modifying jump mechanics while sprinting */
- simulated function vector GetSprintJumpVelocity( vector ViewDirection )
- {
- return ViewDirection * GetJumpZ() * GetDirectionalJumpScale();
- }
- /** Determines how much a directional jump's velocity is scaled */
- simulated function float GetDirectionalJumpScale()
- {
- return 1.f;
- }
- /** Overridden to access specific bumping damage types like the fleshpounds enraged hit */
- function class<KFDamageType> GetBumpAttackDamageType();
- /** Overridden to prevent falling damage for Zeds */
- function TakeFallingDamage()
- {
- }
- event Landed( vector HitNormal, Actor FloorActor )
- {
- local Controller StoredLastHitBy;
- local int SMIndex;
- StoredLastHitBy = LastHitBy;
- if( bRestoreCollisionOnLand )
- {
- bRestoreCollisionOnLand = false;
- SetCollisionSize( default.CylinderComponent.CollisionRadius, default.CylinderComponent.CollisionHeight );
- FitCollision();
- }
- if( IsHumanControlled() )
- {
- if( bJumped && IsLocallyControlled() )
- {
- SMIndex = SpecialMoveCooldowns.Find( 'SMHandle', SM_Jump );
- if( SMIndex != INDEX_NONE )
- {
- SpecialMoveCooldowns[SMIndex].LastUsedTime = WorldInfo.TimeSeconds;
- }
- }
- bJumped = false;
- }
- super.Landed( HitNormal, FloorActor );
- LastHitBy = StoredLastHitBy;
- }
- function SetMovementPhysics()
- {
- // do not change when in special move - special move will reset when it's ready
- if( Physics == PHYS_None && IsDoingSpecialMove() )
- return;
- super.SetMovementPhysics();
- }
- /** Implements bKnockdownWhenJumpedOn */
- function CrushedBy(Pawn OtherPawn)
- {
- Super.CrushedBy(OtherPawn);
- if ( bKnockdownWhenJumpedOn
- // Still alive after crush damage
- && Health > 0
- // Not emerging
- && !IsDoingSpecialMove(SM_Emerge)
- // Actually above and not a side-swipe
- && ((OtherPawn.Location.Z - Location.Z) > (OtherPawn.CylinderComponent.CollisionHeight + CylinderCOmponent.CollisionHeight))
- // Opposing team; player only
- && !IsHumanControlled()
- && GetTeamNum() != OtherPawn.GetTeamNum()
- )
- {
- Knockdown(, vect(1,1,1), OtherPawn.Location, 1000, 100 );
- }
- }
- /** Sets the speed scale this zed will use in zed time */
- simulated function SetZedTimeSpeedScale( float SpeedScale )
- {
- if( SpeedScale > 0.f )
- {
- bMovesFastInZedTime = true;
- ZedTimeSpeedScale = SpeedScale;
- }
- else
- {
- bMovesFastInZedTime = false;
- }
- }
- /*********************************************************************************************
- * @name Combat Methods
- ********************************************************************************************* */
- /** Override to handle cloaking */
- native function bool IsValidEnemyTargetFor(const PlayerReplicationInfo PRI, bool bNoPRIisEnemy);
- /** Is test location within charge range? */
- native function bool InChargeRange( const Vector TestLocation );
- /** Is test location within melee range? */
- native function bool InMeleeRange( const Vector TestLocation, optional name AttackTag );
- native function bool InAttackTagRange( const name AttackTag, const Vector TestLocation );
- native function bool InAnyAttackTagRange( const Vector TestLocation, out name outAttackTag );
- /** SERVER ONLY: Gets current enemy, to avoid having to get it from the controller */
- native function KFPawn GetEnemy();
- /** Checks to see if area between CheckLocation and pawn is clear for combat */
- native final function bool IsLocationValidForCombat( KFPawn CheckPawn, const vector CheckLocation );
- /** Used by subclass to implement rage mode */
- simulated function bool SetEnraged(bool bNewEnraged)
- {
- if (!bCanRage)
- {
- return false;
- }
- if (Role == ROLE_Authority)
- {
- if (bNewEnraged == bIsEnraged)
- {
- return false;
- }
- bIsEnraged = bNewEnraged;
- // End blocking on rage
- if (IsDoingSpecialMove(SM_Block))
- {
- EndSpecialMove();
- }
- // Sprint right away if we're AI
- if (!IsHumanControlled())
- {
- SetSprinting(bNewEnraged);
- }
- }
- if (WorldInfo.NetMode != NM_DedicatedServer && !IsHumanControlled())
- {
- if (bNewEnraged)
- {
- PlayExtraVFX('Enraged');
- }
- else
- {
- StopExtraVFX('Enraged');
- }
- }
- return true;
- }
- /** Used by subclass to check rage mode */
- simulated event bool IsEnraged();
- /** Notify the AI controller that we've taken damage from a friendly */
- function NotifyFriendlyAIDamageTaken( Controller DamagerController, int Damage, Actor DamageCauser, class<KFDamageType> DamageType )
- {
- if( MyKFAIC != none )
- {
- MyKFAIC.NotifyFriendlyAIDamageTaken( DamagerController, Damage, DamageCauser, DamageType );
- }
- }
- /** Notification called from Pawn animation, by KFAnimNotify_MeleeImpact.
- * Network: Server Only
- */
- simulated function MeleeImpactNotify(KFAnimNotify_MeleeImpact Notify)
- {
- if ( MeleeAttackHelper != None )
- {
- MeleeAttackHelper.bHasAlreadyHit = false; // reset attack
- MeleeAttackHelper.MeleeImpactNotify(Notify);
- }
- // check to see if our enemy was killed
- if ( MyKFAIC != none && MyKFAIC.Enemy != None && MyKFAIC.Enemy.Health <= 0 )
- {
- ClearHeadTrackTarget(Controller.Enemy);
- // TODO: Below command aborts could be problematic, but this code is pretty old- keep an eye on it
- MyKFAIC.AbortCommand( None, class'AICommand_Attack_Grab' );
- MyKFAIC.AbortCommand( None, class'AICommand_Attack_Melee' );
- // Start up a taunt kill special move (used when enemy killed, as opposed to regular taunts)
- class'AICommand_TauntEnemy'.static.Taunt( KFAIController( Controller ),, TAUNT_EnemyKilled );
- }
- }
- /** Called from MeleeHelper when damage is done to another pawn */
- function NotifyMeleeDamageDealt();
- /** Am I within range to perform an attack in the AttackTag category? (see PawnAnimInfo) */
- event bool IsInAttackTagRange( vector TestLocation, name AttackTag )
- {
- local vector2D MinMaxRange;
- local float DistSq;
- if( MyKFAIC == none || PawnAnimInfo == none )
- {
- MinMaxRange = PawnAnimInfo.GetAttackRangeByName( AttackTag );
- DistSq = VSizeSq( TestLocation - Location );
- if( (DistSq > (MinMaxRange.X * MinMaxRange.X)) && (DistSq < (MinMaxRange.Y * MinMaxRange.Y)) )
- {
- return true;
- }
- }
- return false;
- }
- /** Am I within range to perform any attack in my PawnAnimInfo? */
- event bool IsInAnyAttackTagRange( vector TestLocation, out name outAttackTag )
- {
- local int idx;
- if( MyKFAIC == none || PawnAnimInfo == none )
- {
- outAttackTag = '';
- return false;
- }
- for( idx = 0; idx < PawnAnimInfo.Attacks.Length; idx++ )
- {
- if( PawnAnimInfo.Attacks[idx].Tag == '' || PawnAnimInfo.Attacks[idx].Chance <= 0.f )
- {
- continue;
- }
- if( IsInAttackTagRange(TestLocation, PawnAnimInfo.Attacks[idx].Tag) )
- {
- outAttackTag = PawnAnimInfo.Attacks[idx].Tag;
- return true;
- }
- }
- outAttackTag = '';
- return false;
- }
- /**
- * Network: Local Player
- *
- * @param FireModeNum fire mode number
- */
- simulated function StartPlayerZedMove(ESpecialMove Move)
- {
- local byte SMFlags;
- if( Move != SM_None )
- {
- // Make sure attack is off cooldown
- if( GetSpecialMoveCooldownTimeRemaining(Move) > 0.f )
- {
- return;
- }
- // Tell our currently-running special move to retrigger the button press if it was released mid-move
- if( SpecialMove == Move )
- {
- SpecialMoves[SpecialMove].SpecialMoveButtonRetriggered();
- }
- else if( CanDoSpecialMove(Move) )
- {
- SMFlags = SpecialMoveHandler.SpecialMoveClasses[Move].static.PackFlagsBase(self);
- DoSpecialMove(Move, true, InteractionPawn, SMFlags);
- if ( Role < ROLE_Authority && IsDoingSpecialMove(Move) )
- {
- ServerDoSpecialMove(Move, true, InteractionPawn, SMFlags);
- }
- }
- }
- }
- /**
- * Network: Local Player
- *
- * @param FireModeNum fire mode number
- */
- simulated function StopPlayerZedMove(ESpecialMove Move)
- {
- if( !IsDoingSpecialMove() )
- {
- return;
- }
- // notify move that button has been released (for held moves)
- if( SpecialMove == Move )
- {
- SpecialMoves[Move].SpecialMoveButtonReleased();
- }
- }
- /** Called from KFSpecialMove::SpecialMoveEnded */
- simulated function NotifySpecialMoveEnded( KFSpecialMove FinishedMove, ESpecialMove SMHandle )
- {
- local int SMIndex;
- if( IsHumanControlled() && IsLocallyControlled() )
- {
- SMIndex = SpecialMoveCooldowns.Find( 'SMHandle', SMHandle );
- if( SMIndex != INDEX_NONE )
- {
- SpecialMoveCooldowns[SMIndex].LastUsedTime = WorldInfo.TimeSeconds;
- }
- }
- }
- /** Returns cooldown time of a special move */
- function float GetSpecialMoveCooldownPercent( const out SpecialMoveCooldownInfo Cooldown )
- {
- local float CDTime;
- if( Cooldown.SMHandle != SM_None)
- {
- if( Cooldown.LastUsedTime > 0.f && Cooldown.CooldownTime > 0.f )
- {
- CDTime = (!bEmpDisrupted) ? Cooldown.CooldownTime : AfflictionHandler.GetAfflictionDuration(AF_EMP);
- return fClamp( (WorldInfo.TimeSeconds - Cooldown.LastUsedTime) / CDTime, 0.f, 1.f );
- }
- }
- return 1.f;
- }
- /** Returns amount of time remaining in cooldown */
- function float GetSpecialMoveCooldownTimeRemaining( ESpecialMove SMHandle )
- {
- local float CDTime;
- local int i;
- i = SpecialMoveCooldowns.Find( 'SMHandle', SMHandle );
- if( i != INDEX_NONE )
- {
- if( SpecialMoveCooldowns[i].LastUsedTime > 0.f && SpecialMoveCooldowns[i].CooldownTime > 0.f )
- {
- CDTime = (!bEmpDisrupted) ? SpecialMoveCooldowns[i].CooldownTime : AfflictionHandler.GetAfflictionDuration(AF_EMP);
- return CDTime - fMin( WorldInfo.TimeSeconds - SpecialMoveCooldowns[i].LastUsedTime, CDTime );
- }
- // if 1st use or no cooldown set return 0 remaining time
- return 0.f;
- }
- return 100.f; // invalid move
- }
- /** Allows access to the cooldowns array without being able to modify it */
- simulated function array<SpecialMoveCooldownInfo> GetSpecialMoveCooldowns()
- {
- return SpecialMoveCooldowns;
- }
- /** Puts all moves on this pawn on cooldown */
- function PutAllMovesOnCooldown()
- {
- local int i;
- if( IsHumanControlled() && IsLocallyControlled() )
- {
- for( i = 0; i < SpecialMoveCooldowns.Length; ++i )
- {
- if( SpecialMoveCooldowns[i].SMHandle != SM_None )
- {
- SpecialMoveCooldowns[i].LastUsedTime = WorldInfo.TimeSeconds;
- }
- }
- }
- }
- /** Returns TRUE if we should use adjusted controller sensitivity */
- simulated function bool UseAdjustedControllerSensitivity();
- /** Returns TRUE if this zed can block attacks */
- function bool CanBlock()
- {
- // Make sure this zed is allowed to block first
- if( Physics == PHYS_Walking
- && CanDoSpecialMove(SM_Block)
- && IsCombatCapable() )
- {
- // Check chance
- if( fRand() > DifficultyBlockSettings.Chance * (WorldInfo.Game.NumPlayers == 1 ? DifficultyBlockSettings.SoloChanceMultiplier : 1.f) )
- {
- return false;
- }
- // Return cooldown status
- return `TimeSince(LastBlockTime) >= DifficultyBlockSettings.Cooldown;
- }
- return false;
- }
- /** Returns the block settings for the current difficulty */
- simulated function SetBlockSettings( const sBlockInfo NewBlockSettings )
- {
- DifficultyBlockSettings = NewBlockSettings;
- }
- /** Returns the block settings for the current difficulty */
- simulated function sBlockInfo GetBlockSettings()
- {
- return DifficultyBlockSettings;
- }
- /** Returns the minimum field of view required to detect a block */
- simulated function float GetMinBlockFOV()
- {
- return MinBlockFOV;
- }
- /** Reduce affliction power when blocking */
- simulated function AdjustAffliction( out float AfflictionPower )
- {
- if( Role == ROLE_Authority && bIsBlocking )
- {
- AfflictionPower *= DifficultyBlockSettings.AfflictionModifier;
- }
- super.AdjustAffliction( AfflictionPower );
- }
- /** Used by subclasses to determine if the boss icon can be rendered */
- function bool ShouldDrawBossIcon()
- {
- return false;
- }
- /*********************************************************************************************
- * @name Damage/Death Methods
- ********************************************************************************************* */
- /*
- * PlayDying() is called upon death
- * Network: ALL
- */
- simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
- {
- local KFPlayerController KFPC;
- local string ClassName;
- Timer_EndRallyBoost();
- super.PlayDying(DamageType, HitLoc);
- //Shared functionality for boss death
- if (IsABoss() && class<KFGameInfo>(WorldInfo.GRI.GameClass).default.bGoToBossCameraOnDeath)
- {
- KFPC = KFPlayerController(GetALocalPlayerController());
- if(KFPC != none)
- {
- KFPC.SetBossCamera( self );
- }
- //@HSL_BEGIN - JRO - 5/17/2016 - PS4 Activity Feeds
- ClassName = string(Class.Name);
- ClassName -= '_Versus';
- class'GameEngine'.static.GetOnlineSubsystem().PlayerInterfaceEx.PostActivityFeedBossKill(GetLocalizedName(), ClassName, WorldInfo.GetMapName(true));
- //@HSL_END
- }
- if (bUseExplosiveDeath)
- {
- PlayExplosiveDeath();
- }
- if (bUseDamageInflation)
- {
- PlayInflationDeath();
- }
- UpdateBleedIncapFX();
- StopExtraVFX(`NAME_NONE);
- }
- simulated function PlayInflationDeath()
- {
- local int Idx;
- for (Idx = 0; Idx < Mesh.PhysicsAssetInstance.Bodies.Length; ++Idx)
- {
- Mesh.PhysicsAssetInstance.Bodies[Idx].CustomGravityFactor = InflateDeathGravity;
- if (InflationExplosionTimer > 0)
- {
- SetTimer(InflationExplosionTimer, false, 'InflationExplode');
- }
- }
- }
- simulated function PlayExplosiveDeath()
- {
- local KFGoreManager GoreManager;
- GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
- if (GoreManager != none && GoreManager.AllowMutilation())
- {
- HandlePartialGoreAndGibs(class'KFDT_Explosive_Sacrifice', Location, vect(0, 0, 0), 'hips', true);
- GoreManager.SpawnObliterationBloodEffect(self);
- }
- else
- {
- HandlePartialGoreAndGibs(class'KFDT_Explosive_Sacrifice', Location, vect(0, 0, 0), 'hips', false);
- }
- }
- simulated function InflationExplode()
- {
- local int Idx;
- RepDamageInflateParam = 0.f;
- bUseDamageInflation = false;
- for (Idx = 0; Idx < Mesh.PhysicsAssetInstance.Bodies.Length; ++Idx)
- {
- Mesh.PhysicsAssetInstance.Bodies[Idx].CustomGravityFactor = 1.0;
- }
- PlayExplosiveDeath();
- if ( WorldInfo.NetMode != NM_DedicatedServer )
- {
- UpdateVisualInflation(GetCurrentInflation() * 2.0);
- }
- }
- State Dying
- {
- event OnSleepRBPhysics()
- {
- local int i;
- // Shrink shadow cull distance when dead. We don't do this for characters because they
- // have extra cosmetics and custom head components. Must reattach component so render data gets updated.
- Mesh.PerObjectShadowCullDistance *= 0.6;
- ReattachComponent(Mesh);
- //Reattach monster PACs
- for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; ++i)
- {
- if (ThirdPersonAttachments[i] != None)
- {
- ThirdPersonAttachments[i].PerObjectShadowCullDistance *= 0.6;
- ReattachComponent(ThirdPersonAttachments[i]);
- }
- }
- Super.OnSleepRBPhysics();
- }
- event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
- {
- local KFPawn_Human KFPH;
- super.TakeDamage( Damage, InstigatedBy, HitLocation, Momentum, DamageType, HitInfo, DamageCauser );
- if( InstigatedBy != none && InstigatedBy.Pawn != none )
- {
- // beat dead horse
- KFPH = KFPawn_Human( InstigatedBy.Pawn );
- if( KFPH != none )
- {
- `DialogManager.PlayBeatDeadHorseDialog( KFPH, self );
- }
- }
- }
- simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
- {
- local PlayerController PC;
- local matrix HeadMatrix;
- local vector HeadLoc;
- //local rotator HeadRot;
- PC = GetALocalPlayerController();
- if( PC != none && !PC.IsSpectating() && PC.ViewTarget == self )
- {
- HeadMatrix = Mesh.GetBoneMatrix( Mesh.MatchRefBone('head') );
- HeadLoc = MatrixGetOrigin( HeadMatrix );
- //HeadMatrix = MakeRotationMatrix( rot(0, -16383, 16383) ) * HeadMatrix;
- //HeadRot = MatrixGetRotator( HeadMatrix );
- //DrawDebugLine(HeadLoc, HeadLoc + 100*vector(HeadRot), 0, 255, 0);
- //out_CamLoc = VInterpTo( out_CamLoc, HeadLoc, fDeltaTime, 10.0 );
- out_CamRot = RInterpTo( out_CamRot, rotator(HeadLoc - out_CamLoc), fDeltaTime, 10.0 );
- return true;
- }
- return Global.CalcCamera( fDeltaTime, out_CamLoc, out_CamRot, out_FOV );
- }
- };
- /**
- * Don't allow headshots to be counted for any purpose if we were headless before entering the takedamage state
- * NOTE: This addresses an issue with low gore where the head is not hidden after a headless event, allowing headshots
- * to count when they shouldn't.
- */
- function bool CanCountHeadshots()
- {
- return !bIsHeadless;
- }
- event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
- {
- local KFPawn_Human HumanInstigator;
- local KFAIController_Monster AIMonster;
- local class<KFDamageType> KFDT;
- local KFPlayerController KFPC;
- local KFPerk InstigatorPerk;
- local KfPlayerReplicationInfo KFPRI;
- local KFAIController KFAIC;
- local KFPawn_Monster KFPM;
- local float NapalmCheckDist;
- AIMonster = KFAIController_Monster(InstigatedBy);
- KFDT = class<KFDamageType>(DamageType);
- KFPC = KFPlayerController(InstigatedBy);
- if( KFPC != none )
- {
- InstigatorPerk = KFPC.GetPerk();
- KFAIC = KFAIController(Controller);
- if( KFAIC != none && KFAIC.TimeFirstSawPlayer == 0 )
- {
- // If we took damage, count that as "seeing a player" for our purposes
- KFAIC.TimeFirstSawPlayer = Worldinfo.TimeSeconds;
- }
- }
- if( AIMonster != none && KFDT != none && !KFDT.default.bIgnoreZedOnZedScaling )
- {
- // Deal extra damage to other zeds if we have over 100 health
- if( Health >= 100 )
- {
- Damage *= ( 2 - (HealthMax - Health ) / HealthMax );
- }
- }
- if( Damage > 0 && InstigatorPerk != none && KFDT != none )
- {
- bCouldTurnIntoShrapnel = InstigatorPerk.CouldBeZedShrapnel( KFDT );
- }
- super.TakeDamage( Damage, InstigatedBy, HitLocation, Momentum, DamageType, HitInfo, DamageCauser );
- if( InstigatedBy != none && InstigatedBy.Pawn != none )
- {
- HumanInstigator = KFPawn_Human( InstigatedBy.Pawn );
- if( HumanInstigator != none )
- {
- HumanInstigator.ResetIdleStartTime();
- }
- }
- if( Damage > 0
- && InstigatedBy != none
- && DamageCauser != none
- && KFDT != none
- && KFDT.default.DoT_Type == DOT_Fire
- && KFDT != class'KFDT_Fire_Napalm'
- && WorldInfo.RealTimeSeconds - LastNapalmInfectCheckTime > 0.25f )
- {
- if( KFPC != none
- && KFPC.GetPerk() != none
- && KFPC.GetPerk().CanSpreadNapalm()
- && class'KFPerk'.static.IsDamageTypeOnThisPerk(KFDT, class'KFPerk_Firebug') )
- {
- NapalmCheckDist = Square( CylinderComponent.CollisionRadius * class'KFPerk_Firebug'.static.GetNapalmCheckCollisionScale() );
- foreach WorldInfo.AllPawns( class'KFPawn_Monster', KFPM, Location )
- {
- if( KFPM != self && KFPM.IsAliveAndWell() && !KFPM.IsNapalmInfected() )
- {
- if( VSizeSQ(Location - KFPM.Location) > NapalmCheckDist )
- {
- continue;
- }
- InfectWithNapalm( KFPM, KFPC );
- }
- }
- LastNapalmInfectCheckTime = WorldInfo.RealTimeSeconds;
- // Make sure we're infected
- if( !IsNapalmInfected() )
- {
- InfectWithNapalm( self, KFPC );
- }
- }
- }
- KFPRI = KFPlayerReplicationInfo( PlayerReplicationInfo );
- if( KFPRI != none )
- {
- KFPRI.PlayerHealth = Health;
- KFPRI.PlayerHealthPercent = FloatToByte( float(Health) / float(HealthMax) );
- }
- if (bUseDamageInflation)
- {
- IntendedDamageInflationPercent = float(Health) / float(HealthMax);
- }
- }
- /** Disable falling damage, apply blocking modifier */
- function AdjustDamage(out int InDamage, out vector Momentum, Controller InstigatedBy, vector HitLocation, class<DamageType> DamageType, TraceHitInfo HitInfo, Actor DamageCauser)
- {
- local KFPlayerController KFPC;
- local KFPawn_Human KFPH;
- local float TempDamage;
- local int HitZoneIdx;
- local int ExtraHeadDamage;
- local KFPerk InstigatorPerk;
- local class<KFDamageType> KFDT;
- local vector DamageSource;
- Super.AdjustDamage(InDamage, Momentum, InstigatedBy, HitLocation, DamageType, HitInfo, DamageCauser);
- if( DamageType.default.bCausedByWorld && ClassIsChildOf(DamageType, class'KFDT_Falling') )
- {
- InDamage = 0;
- return;
- }
- // start with damage type resistance/vulnerability
- InDamage *= GetDamageTypeModifier(DamageType);
- // Applies rally damage resistance
- GetRallyBoostResistance( InDamage );
- // Apply blocking modifier
- ApplyBlockingDamageModifier( InDamage, InstigatedBy, DamageType );
- // Cached hit params
- HitZoneIdx = HitZones.Find('ZoneName', HitInfo.BoneName);
- // Let the instigator's perk adjust the damage
- KFPC = KFPlayerController(InstigatedBy);
- if( KFPC != none )
- {
- InstigatorPerk = KFPC.GetPerk();
- if( InstigatorPerk != none )
- {
- InstigatorPerk.ModifyDamageGiven( InDamage, DamageCauser, self, KFPC, class<KFDamageType>(DamageType), HitZoneIdx );
- }
- if( KFPC.Pawn != none )
- {
- KFPH = KFPawn_Human(KFPC.Pawn);
- if( KFPH != none )
- {
- TempDamage = InDamage;
- TempDamage *= KFPH.GetHealingDamageBoostModifier();
- InDamage = FCeil( TempDamage );
- }
- }
- }
- // NVCHANGE_BEGIN - RLS - Debugging Effects
- if( WorldInfo.Game != none && KFGameInfo(WorldInfo.Game).bNVAlwaysHeadshot )
- {
- HitZoneIdx = HZI_HEAD;
- }
- // NVCHANGE_BEGIN - RLS - Debugging Effects
- // Do extra damage on blowing the head off
- if( !bCheckingExtraHeadDamage && HitZoneIdx == HZI_Head &&
- HitZones[HZI_Head].GoreHealth > 0 && InDamage > HitZones[HZI_Head].GoreHealth )
- {
- KFDT = class<KFDamageType>(DamageType);
- // For certain weapons do even more damage on blowing the head off
- if( KFDT != none )
- {
- InDamage *= KFDT.default.HeadDestructionDamageScale;
- }
- ExtraHeadDamage = InDamage + HealthMax * 0.25;
- bCheckingExtraHeadDamage = true;
- AdjustDamage( ExtraHeadDamage, Momentum, InstigatedBy, HitLocation, DamageType, HitInfo, DamageCauser );
- bCheckingExtraHeadDamage = false;
- InDamage += ExtraHeadDamage;
- }
- // register damage to divide up money/XP/Score. This is done here instead of NotifyTakeHit,
- // so that it's called on the killing blow
- if( !bCheckingExtraHeadDamage && InstigatedBy != none )
- {
- AddTakenDamage( InstigatedBy, FMin(Health, InDamage), DamageCauser, class<KFDamageType>(DamageType) );
- }
- // If the head has been dismembered reduce damage to 1 (non-zero so play hit is called).
- // Since zed is bleeding out already this is not as big of a issue.
- if ( HitZoneIdx == HZI_HEAD && bIsHeadless )
- {
- InDamage = 1;
- }
- // Never allow damage to reach zero if coming from opposing team
- if( InstigatedBy == none || InstigatedBy.GetTeamNum() != GetTeamNum() )
- {
- InDamage = Max( InDamage, 1 );
- }
- if (ArmorInfo != none)
- {
- //If the damage doesn't have a bone hit source, it's likely AoE. Split over all remaining armor evenly.
- if (HitInfo.BoneName != '')
- {
- if (InstigatedBy != none && InstigatedBy.Pawn != none)
- {
- DamageSource = InstigatedBy.Pawn.Location;
- }
- else if (KFWeapon(DamageCauser) != none)
- {
- DamageSource = KFWeapon(DamageCauser).GetMuzzleLoc();
- }
- else
- {
- DamageSource = DamageCauser.Location;
- }
- ArmorInfo.AdjustBoneDamage(InDamage, HitInfo.BoneName, DamageSource);
- }
- else
- {
- ArmorInfo.AdjustNonBoneDamage(InDamage);
- }
- `log(self @ GetFuncName() @ " After armor adjustment Damage=" $ InDamage @ "Momentum=" $ Momentum @ "Zone=" $ HitInfo.BoneName @ "DamageType=" $ DamageType, bLogTakeDamage);
- }
- `log(self @ "Adjusted Monster Damage=" $ InDamage, bLogTakeDamage);
- }
- /** Returns damage multiplier for an incoming damage type @todo: c++?*/
- function float GetDamageTypeModifier(class<DamageType> DT)
- {
- local int i;
- local int DifficultyIdx;
- local float DamageModifier;
- if ( LiveDamageTypeModifiers.Length > 0 )
- {
- AppendLiveDamageTypeModifiers();
- }
- // reverse iteration so that most specific damage type can be last on the array
- for (i = DamageTypeModifiers.Length - 1; i >= 0; --i)
- {
- if ( ClassIsChildOf(DT, DamageTypeModifiers[i].DamageType) )
- {
- // If specified assign difficulty based value
- if ( WorldInfo.Game != None && DamageTypeModifiers[i].DamageScale.Length > 1 )
- {
- DifficultyIdx = Min(WorldInfo.Game.GetModifiedGameDifficulty(), DamageTypeModifiers[i].DamageScale.Length);
- DamageModifier = DamageTypeModifiers[i].DamageScale[DifficultyIdx];
- }
- else
- {
- DamageModifier = DamageTypeModifiers[i].DamageScale[0];
- }
- // for resistances only, allow game info to modify (e.g. num players)
- if ( DamageModifier < 1.f )
- {
- DamageModifier = FMax(Lerp(1.f, DamageModifier, GameResistancePct), 0.f);
- }
- `log(self@"Scaling damage taken from"@DT@"by"@DamageModifier, bLogTakeDamage);
- return DamageModifier;
- }
- }
- return 1.f;
- }
- /** Append (one-time) damage type modifiers from the live balance into the damage modifiers array */
- function AppendLiveDamageTypeModifiers()
- {
- local int i;
- for (i = 0; i < LiveDamageTypeModifiers.Length; i++)
- {
- if ( LiveDamageTypeModifiers[i].DamageType != None )
- {
- DamageTypeModifiers.AddItem(LiveDamageTypeModifiers[i]);
- }
- else
- {
- break; // finished once we find an empty entry
- }
- }
- LiveDamageTypeModifiers.Length = 0;
- }
- /** Applies the blocking damage modifier to incoming damage */
- function ApplyBlockingDamageModifier( out int Damage, Controller InstigatedBy, class<DamageType> damageType )
- {
- local vector DamageDirNormal;
- if( !bIsBlocking || Damage <= 0 || InstigatedBy == none || InstigatedBy.Pawn == none || InstigatedBy.GetTeamNum() == GetTeamNum() )
- {
- return;
- }
- // Only block attacks that come from the front
- DamageDirNormal = Normal( InstigatedBy.Pawn.Location - Location );
- if( DamageDirNormal dot vector(Rotation) > MinBlockFOV )
- {
- // Melee damage first
- if( ClassIsChildOf(damageType, class'KFDT_Bludgeon') || ClassIsChildOf(damageType, class'KFDT_Slashing') )
- {
- Damage = int( float(Damage) * DifficultyBlockSettings.MeleeDamageModifier );
- }
- else
- {
- Damage = int( float(Damage) * DifficultyBlockSettings.DamageModifier );
- }
- }
- }
- event bool HealDamage(int Amount, Controller Healer, class<DamageType> DamageType, optional bool bRepairArmor=true, optional bool bMessageHealer=true)
- {
- `DialogManager.PlaySpotZedHealingDialog( self );
- Super.HealDamage(Amount, Healer, DamageType);
- return true;
- }
- /** Is this monster effected by a melee damage debuff? */
- function bool HasReducedMeleeDamage()
- {
- return bHasReducedMeleeDamage;
- }
- /** sends any notifications to anything that needs to know this pawn has taken damage */
- function NotifyTakeHit(Controller InstigatedBy, vector HitLocation, int Damage, class<DamageType> DamageType, vector Momentum, Actor DamageCauser)
- {
- local KFPawn_Human KFPH_Instigator;
- Super.NotifyTakeHit(InstigatedBy, HitLocation, Damage, DamageType, Momentum, DamageCauser);
- // continuous damage check needs to happen before we set LastPainTime and LastHitBy for current hit
- if( InstigatedBy != none && InstigatedBy.Pawn != none )
- {
- KFPH_Instigator = KFPawn_Human( InstigatedBy.Pawn );
- if( KFPH_Instigator != none )
- {
- `DialogManager.PlayDamageZedContinuousDialog( KFPH_Instigator, self );
- }
- }
- // Allow special move to react to TakeHit/TakeDamage
- if( SpecialMove != SM_None )
- {
- SpecialMoves[SpecialMove].NotifyOwnerTakeHit(class<KFDamageType>(damageType), HitLocation, Normal(Momentum), InstigatedBy);
- }
- `AILog_Ext(GetFuncName()$"() Instigator:"$InstigatedBy$" DT: "$DamageType, 'Damage', MyKFAIC);
- }
- function PlayHit(float Damage, Controller InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, TraceHitInfo HitInfo)
- {
- local KFPawn_Human KFPH_Instigator;
- super.PlayHit( Damage, InstigatedBy, HitLocation, damageType, Momentum, HitInfo );
- // play damage zed dialog after Afflictions happen in super
- if( InstigatedBy != none && InstigatedBy.Pawn != none )
- {
- KFPH_Instigator = KFPawn_Human( InstigatedBy.Pawn );
- if( KFPH_Instigator != none )
- {
- `DialogManager.PlayDamagedZedDialog( KFPH_Instigator, self, DamageType );
- }
- }
- }
- /** Called before, and in addition to, NotifyTakeHit(), but processes melee specifically (Server only) */
- function NotifyMeleeTakeHit(Controller InstigatedBy, vector HitLocation);
- /** Delayed death from lethal damage */
- function BleedOutTimer()
- {
- if ( !bPlayedDeath )
- {
- `log(GetFuncName() @ "LastHitBy" @ LastHitBy, bLogTakeDamage);
- Died(LastHitBy, class'KFDT_Bleeding', Location);
- }
- }
- /** Applies the rally buff and spawns a rally effect */
- simulated function bool Rally(
- KFPawn RallyInstigator,
- ParticleSystem RallyEffect,
- name EffectBoneName,
- vector EffectOffset,
- ParticleSystem AltRallyEffect,
- name AltEffectBoneNames[2],
- vector AltEffectOffset,
- optional bool bSkipEffects=false
- )
- {
- local sRallyInfo RallyInfo;
- local KFAIController KFAIC;
- local bool bStartedBoostRally;
- local Name SocketBoneName;
- local Name AltSocketBoneName;
- GetDifficultyRallyInfo( RallyInfo );
- if( !RallyInfo.bCanRally || !IsAliveAndWell() )
- {
- return false;
- }
- if( RallyInfo.DealtDamageModifier > 1.f )
- {
- if( !IsTimerActive(nameOf(Timer_EndRallyBoost)) )
- {
- SetTimer( RallyInfo.RallyBuffTime, false, nameOf(Timer_EndRallyBoost) );
- bStartedBoostRally = true;
- }
- }
- if( Role == ROLE_Authority && Controller != none && RallyInfo.bCauseSprint )
- {
- KFAIC = KFAIController( Controller );
- KFAIC.SetSprintingDisabled( false );
- KFAIC.SetCanSprint( true );
- KFAIC.bDefaultCanSprint = true;
- KFAIC.bCanSprintWhenDamaged = true;
- KFAIC.bForceFrustration = true;
- // Trigger sprint/rage immediately
- SetSprinting( true );
- SetEnraged( true );
- if (KFGameInfo(WorldInfo.Game) != none)
- {
- KFGameInfo(WorldInfo.Game).NotifyRally(self);
- }
- }
- if( !bSkipEffects && WorldInfo.NetMode != NM_DedicatedServer )
- {
- if( RallyPSC != none )
- {
- RallyPSC.DeactivateSystem();
- DetachComponent( RallyPSC );
- }
- RallyPSC = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( RallyEffect, Mesh, EffectBoneName, false, EffectOffset );
- // Spawn player rally particle systems and attach them
- if( bStartedBoostRally )
- {
- SocketBoneName = Mesh.GetSocketBoneName(AltEffectBoneNames[0]);
- if (SocketBoneName != '' && SocketBoneName != 'None')
- {
- RallyHandPSCs[0] = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( AltRallyEffect, Mesh, AltEffectBoneNames[0], true, AltEffectOffset );
- }
- AltSocketBoneName = Mesh.GetSocketBoneName(AltEffectBoneNames[1]);
- if (AltSocketBoneName != '' && AltSocketBoneName != 'None')
- {
- RallyHandPSCs[1] = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( AltRallyEffect, Mesh, AltEffectBoneNames[1], true, AltEffectOffset );
- }
- }
- }
- return true;
- }
- /** Ends the rally boost and cancels effects */
- simulated function Timer_EndRallyBoost()
- {
- if( RallyHandPSCs[0] != none && RallyHandPSCs[0].bIsActive )
- {
- RallyHandPSCs[0].DeactivateSystem();
- }
- if( RallyHandPSCs[1] != none && RallyHandPSCs[1].bIsActive )
- {
- RallyHandPSCs[1].DeactivateSystem();
- }
- }
- /** Sets the difficulty-based rally settings for this pawn */
- simulated function SetRallySettings( sRallyInfo NewRallyInfo )
- {
- DifficultyRallySettings = NewRallyInfo;
- }
- /** Returns the difficulty-based rally settings */
- simulated function GetDifficultyRallyInfo( out sRallyInfo RallyInfo )
- {
- RallyInfo = DifficultyRallySettings;
- }
- /** Applies the rally damage boost if applicable */
- simulated function int GetRallyBoostDamage( int NewDamage )
- {
- local sRallyInfo RallyInfo;
- GetDifficultyRallyInfo( RallyInfo );
- return NewDamage * ( IsTimerActive(nameOf(Timer_EndRallyBoost)) ? RallyInfo.DealtDamageModifier : 1.f );
- }
- /** Applies the rally damage reduction if applicable */
- simulated function int GetRallyBoostResistance( int NewDamage )
- {
- local sRallyInfo RallyInfo;
- GetDifficultyRallyInfo( RallyInfo );
- return NewDamage * ( IsTimerActive(nameOf(Timer_EndRallyBoost)) ? RallyInfo.TakenDamageModifier : 1.f );
- }
- function bool Died(Controller Killer, class<DamageType> DamageType, vector HitLocation)
- {
- local KFPlayerController KFPC;
- local KFPerk InstigatorPerk;
- if ( super.Died(Killer, damageType, HitLocation) )
- {
- if( Killer != none && Killer.Pawn != none && KFPawn_Human(Killer.Pawn) != none )
- {
- `DialogManager.PlayKilledZedDialog( KFPawn_Human(Killer.Pawn), self, DamageType, IsDoingSpecialMove(SM_Knockdown) || IsDoingSpecialMove(SM_RecoverFromRagdoll) );
- }
- if( bCouldTurnIntoShrapnel )
- {
- if( Killer != none )
- {
- KFPC = KFPlayerController(Killer);
- if( KFPC != none )
- {
- InstigatorPerk = KFPC.GetPerk();
- if( InstigatorPerk != none && InstigatorPerk.ShouldShrapnel() )
- {
- ShrapnelExplode( Killer, InstigatorPerk );
- }
- }
- }
- }
- OnZedDied(Killer);
- return true;
- }
- return false;
- }
- /** Handle any subclassed functionality for when a zed dies */
- function OnZedDied(Controller Killer);
- /** Handle this pawn being destroyed
- */
- simulated event Destroyed()
- {
- // Kill any affliction sounds/effects when this pawn is destroyed
- AfflictionHandler.Shutdown();
- super.Destroyed();
- }
- event OnRigidBodyLinearConstraintViolated(name StretchedBoneName)
- {
- local KFGoreManager GoreManager;
- `log("Linear constraint violated, hiding bone " @ StretchedBoneName);
- GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
- if( GoreManager != none && GoreManager.AllowMutilation() )
- {
- if( !bIsGoreMesh )
- {
- SwitchToGoreMesh();
- }
- if( bIsGoreMesh )
- {
- GoreManager.CrushBone( self, StretchedBoneName );
- return;
- }
- }
- // no gore fallback
- mesh.HideBoneByName(StretchedBoneName, PBO_Term);
- }
- protected simulated function ResetHealthVisibilty()
- {
- bShowHealth = false;
- }
- simulated function bool CanShowHealth()
- {
- return bShowHealth;
- }
- simulated function bool IsNapalmInfected()
- {
- return DamageOverTimeArray.Find('DamageType', class'KFDT_Fire_Napalm') != INDEX_NONE;
- }
- function bool CanNapalmInfect( out KFPlayerController NapalmInstigator )
- {
- local int DoTIndex;
- local KFPlayerController KFPC;
- local KFPerk InstigatorPerk;
- if( DamageOverTimeArray.Length > 0 )
- {
- DoTIndex = DamageOverTimeArray.Find( 'DoT_Type', class'KFDT_Fire'.default.DoT_Type );
- if( DoTIndex != INDEX_NONE && DamageOverTimeArray[DoTIndex].InstigatedBy != none )
- {
- KFPC = KFPlayerController( DamageOverTimeArray[DoTIndex].InstigatedBy );
- if( KFPC != none )
- {
- InstigatorPerk = KFPC.GetPerk();
- if( InstigatorPerk != none && InstigatorPerk.CanSpreadNapalm() )
- {
- NapalmInstigator = KFPC;
- return true;
- }
- }
- }
- }
- NapalmInstigator = none;
- return false;
- }
- function InfectWithNapalm( KFPawn_Monster KFPM, KFPlayerController KFPC )
- {
- if( KFPC != none )
- {
- KFPM.TakeDamage( class'KFPerk_Firebug'.static.GetNapalmDamage(),
- KFPC,
- vect(0,0,0),
- vect(0,0,0),
- class'KFDT_Fire_Napalm',,
- KFPC );
- }
- }
- /**
- * @brief Spawns a radioactive cloud that hurts other Zeds
- *
- * @param Killer The monster's killer (that had the shrapnel skill enabled)
- */
- function ShrapnelExplode( Controller Killer, KFPerk InstigatorPerk )
- {
- local KFExplosionActorReplicated ExploActor;
- local Actor InstigatorActor;
- local GameExplosion ExplosionTemplate;
- if ( Role < ROLE_Authority )
- {
- return;
- }
- InstigatorActor = Killer.Pawn != none ? Killer.Pawn : Killer;
- // explode using the given template
- ExploActor = Spawn(class'KFExplosionActorReplicated', InstigatorActor,, Location,,, true);
- if( ExploActor != None )
- {
- ExploActor.InstigatorController = Killer;
- if( Killer.Pawn != none )
- {
- ExploActor.Instigator = Killer.Pawn;
- }
- if( InstigatorPerk != none )
- {
- ExplosionTemplate = InstigatorPerk.GetExplosionTemplate();
- ExploActor.Explode( ExplosionTemplate );
- }
- }
- }
- /*********************************************************************************************
- * @name Injuries & Status Effects
- ********************************************************************************************* */
- /** Called to set any visual inflation that needs to occur */
- simulated function UpdateVisualInflation(float InflationAmount)
- {
- local MaterialInstanceConstant MIC;
- local int i, j;
- //Turn off inflation entirely if we're in gore mode
- if (bIsGoreMesh)
- {
- InflationAmount = 0.f;
- }
- //Base character MICs
- foreach CharacterMICs(MIC)
- {
- MIC.SetScalarParameterValue('Scalar_Inflate', InflationAmount);
- }
- //Update MICs in any active PACs
- for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; ++i)
- {
- if (ThirdPersonAttachments[i] != none)
- {
- for (j = 0; j < ThirdPersonAttachments[i].Materials.Length; ++j)
- {
- MIC = MaterialInstanceConstant(ThirdPersonAttachments[i].GetMaterial(j));
- if (MIC != none)
- {
- MIC.SetScalarParameterValue('Scalar_Inflate', InflationAmount);
- }
- }
- }
- }
- //Update MICs in any active static attachments
- for (i = 0; i < StaticAttachList.Length; ++i)
- {
- if (StaticAttachList[i] != none)
- {
- for (j = 0; j < StaticAttachList[i].Materials.Length; ++j)
- {
- MIC = MaterialInstanceConstant(StaticAttachList[i].GetMaterial(j));
- if (MIC != none)
- {
- MIC.SetScalarParameterValue('Scalar_Inflate', InflationAmount);
- }
- }
- }
- }
- }
- /** Called on server when pawn should has been crippled (e.g. Headless) */
- function CauseHeadTrauma(float BleedOutTime=5.f)
- {
- if ( !bIsHeadless && !bPlayedDeath && !bDisableHeadless )
- {
- if( MyKFAIC != none && KFGameInfo(WorldInfo.Game) != none && MyKFAIC.TimeFirstSawPlayer >= 0 )
- {
- KFGameInfo(WorldInfo.Game).GameConductor.HandleZedKill( FMax(`TimeSince(MyKFAIC.TimeFirstSawPlayer),0.0));
- // Set this so we know we already logged a kill for our pawn
- MyKFAIC.TimeFirstSawPlayer = -1;
- }
- bPlayShambling = true;
- bIsHeadless = true;
- if( MyKFAIC != none )
- {
- MyKFAIC.SetSprintingDisabled(true);
- }
- // No more auto aiming to this zed
- bCanBeAdheredTo=false;
- bCanBeFrictionedTo=false;
- StopAkEventsOnBone( 'head' );
- // insti-kill while doing a root motion SM (uninterruptable)
- if ( IsDoingSpecialMove() && Mesh.RootMotionMode == RMM_Accel )
- {
- Died(LastHitBy, class'DamageType', Location);
- }
- // initiate ragdoll knockdown while sprinting
- else if ( bIsSprinting && FRand() < 0.25f )
- {
- Knockdown(Velocity, vect(1,1,1));
- }
- // initiate the "headless wander" AICommand
- if( IsAliveAndWell() && MyKFAIC != none )
- {
- // Only allow headless wander if were doing an SM that allows a wander interupt
- // otherwise wait until the end of the move
- if ( SpecialMove == SM_None || !SpecialMoves[SpecialMove].bCanOnlyWanderAtEnd )
- {
- MyKFAIC.DoHeadlessWander();
- }
- }
- if ( BleedOutTime > 0 )
- {
- SetTimer(BleedOutTime, false, nameof(BleedOutTimer));
- }
- }
- }
- /**
- * SERVER ONLY - Update any AI or animation behaviors based on state
- */
- function OnStackingAfflictionChanged(byte Id)
- {
- local bool bShouldBeWandering, bWasWandering;
- bWasWandering = (bPlayShambling || bPlayPanicked);
- bPlayShambling = bEmpPanicked || bIsHeadless;
- bPlayPanicked = bFirePanicked || bIsPoisoned || bMicrowavePanicked;
- bShouldBeWandering = (bPlayShambling || bPlayPanicked);
- if( !bWasWandering && bShouldBeWandering )
- {
- CausePanicWander();
- }
- else if( bWasWandering )
- {
- EndPanicWander();
- }
- }
- /** Called on server when pawn should do EMP Wandering */
- function CausePanicWander()
- {
- if ( !bIsHeadless && !bPlayedDeath )
- {
- // if bCanOnlyWanderAtEnd is true, do not begin to wander until the current special move has ended
- if ( SpecialMove != SM_None && SpecialMoves[SpecialMove].bCanOnlyWanderAtEnd )
- {
- return;
- }
- if( MyKFAIC != none )
- {
- MyKFAIC.SetSprintingDisabled(true);
- }
- // initiate the "EMP wander" AICommand
- if( IsAliveAndWell() && MyKFAIC != none )
- {
- MyKFAIC.DoPanicWander();
- }
- }
- }
- /** Called on server when pawn should stop doing EMP Wandering */
- function EndPanicWander()
- {
- if( MyKFAIC != none )
- {
- MyKFAIC.SetSprintingDisabled(false);
- }
- // stop the "EMP wander" AICommand
- if( IsAliveAndWell() && MyKFAIC != none )
- {
- MyKFAIC.EndPanicWander();
- }
- }
- /** Returns true if a stacking incap is set that will cause a zed to wander */
- simulated function bool ShouldBeWandering()
- {
- return bPlayShambling || bPlayPanicked;
- }
- /** Returns true if a stacking incap is set that will cause a zed to wander */
- simulated function bool IsHeadless()
- {
- return bIsHeadless;
- }
- /** Returns true if a stacking incap is set that will prevent or interrupt a
- * zed from using its special abilities (Ex Husk Flamethrower) */
- simulated function bool IsImpaired()
- {
- return bPlayPanicked || bEmpDisrupted || (bPlayShambling && !bIsHeadless);
- }
- /** Reapply active gameplay related MIC params (e.g. when switching to the gore mesh) */
- simulated function UpdateGameplayMICParams()
- {
- if ( AfflictionHandler != None )
- {
- AfflictionHandler.ToggleEffects(AF_Poison, bIsPoisoned);
- // Turn off inflated if we are using the gore mic
- if( bIsGoreMesh )
- {
- AfflictionHandler.UpdateMaterialParameter(AF_Microwave, 0.f);
- }
- }
- super.UpdateGameplayMICParams();
- }
- /** Called when a melee attack has been parried by another pawn */
- function bool NotifyAttackParried(Pawn InstigatedBy, byte InParryStrength)
- {
- if ( InParryStrength < ParryResistance )
- {
- return FALSE; // resisted
- }
- return Super.NotifyAttackParried(InstigatedBy, InParryStrength);
- }
- /*********************************************************************************************
- * @name Special Moves
- ********************************************************************************************* */
- /** Cloaking & Spotted */
- function SetCloaked(bool bNewCloaking)
- {
- if( bNewCloaking )
- {
- ClearBloodDecals();
- }
- }
- function CallOutCloaking( optional KFPlayerController CallOutController );
- /** Notification when a melee special move ends */
- function MeleeSpecialMoveEnded();
- /** Cause a physics knockdown (e.g. SM_Emerge) */
- function ANIMNOTIFY_Knockdown()
- {
- // notify emerge anim to end with a ragdoll knockdown
- if ( SpecialMove == SM_Emerge )
- {
- //Knockdown(Velocity, vect(1,1,1));
- KFSM_Emerge(SpecialMoves[SM_Emerge]).bDoKnockdown = true;
- }
- }
- /** Called from melee special move when it's interrupted */
- function NotifyAnimInterrupt( optional AnimNodeSequence SeqNode );
- /** Sends message to local players using ClientMessage */
- function Msg( string MsgTxt )
- {
- MessagePlayer( MsgTxt );
- }
- function float GetCircleStrafeDuration()
- {
- return 0.f;
- }
- function float GetStepBackInCombatOdds()
- {
- return 0.12f;
- }
- function Pawn GetPawnToLookAt( optional bool bRequireLOS )
- {
- local Pawn P;
- local array<Pawn> InterestingPawns;
- for( P = WorldInfo.PawnList; P != none; P = P.NextPawn )
- {
- if( P != self && P.IsAliveAndWell() && ( !bRequireLOS || LineOfSightTo( P ) ) )
- {
- InterestingPawns.AddItem( P );
- }
- }
- if( InterestingPawns.Length > 0 )
- {
- return InterestingPawns[ Rand(InterestingPawns.Length) ];
- }
- return None;
- }
- /*********************************************************************************************
- * @name HeadTracking
- ********************************************************************************************* */
- simulated function bool LookAtPawn( optional Pawn P, optional float Strength=0.5f )
- {
- if( IK_Look_Head == none )
- {
- IK_Look_Head = SkelControlLookAt(Mesh.FindSkelControl('HeadLook'));
- //IK_Look_Spine = SkelControlLookAt(Mesh.FindSkelControl('SpineLook'));
- }
- if( IK_Look_Head != none )
- {
- if( P == none )
- {
- P = GetPawnToLookAt( true );
- }
- if( P != none )
- {
- // if( IK_Look_Head.CanLookAtPoint( P.Location + ( vect(0,0,1) * P.BaseEyeHeight ) ) )
- // {
- bIsHeadTrackingActive = true;
- `AILog_Ext( GetFuncName()@P, 'HeadTracking', MyKFAIC );
- SetHeadTrackTarget( P, vect(0,0,1) * P.BaseEyeHeight, Strength, false );
- return true;
- // }
- }
- }
- return false;
- }
- simulated function StopLookingAtPawn( optional Pawn P )
- {
- if( bCanHeadTrack && bIsHeadTrackingActive && MyLookAtInfo.LookAtTarget != none )
- {
- if( P == none )
- {
- P = Pawn( MyLookAtInfo.LookAtTarget );
- }
- if( P != none )
- {
- `AILog_Ext( GetFuncName()@P, 'HeadTracking', MyKFAIC );
- ClearHeadTrackTarget( P );
- }
- }
- }
- /*********************************************************************************************
- * @name Effects / Gore
- ********************************************************************************************* */
- /**
- * Performs the actual gore LOD switch, and optionally switches physics asset upon death
- *
- * @param GoreLODIndex LOD index for the gore LOD
- */
- native final simulated function bool SwitchToGoreMesh();
- /** Called when SwitchToGoreMesh is successful */
- simulated event NotifyGoreMeshActive()
- {
- // Need to reset material params since our MIC was reassigned to the gore mesh
- UpdateGameplayMICParams();
- if ( SpecialMove != SM_None )
- {
- SpecialMoves[SpecialMove].OnGoreMeshSwap();
- }
- }
- /** Returns n closest bones to the TestLocation that belong to the physics asset of the mesh */
- simulated function GetClosestHitBones(int NumBones, vector TestLocation, out array<name> OutHitBoneList)
- {
- local int i;
- local array<name> SearchList;
- // Add bones in the physics asset to the search list
- for( i=0; i<mesh.PhysicsAsset.BodySetup.length; i++ )
- {
- SearchList.AddItem(mesh.PhysicsAsset.BodySetup[i].BoneName);
- }
- mesh.FindClosestBones(TestLocation, NumBones, OutHitBoneList, SearchList);
- }
- simulated event RigidBodyCollision( PrimitiveComponent HitComponent, PrimitiveComponent OtherComponent,
- const out CollisionImpactData RigidCollisionData, int ContactIndex )
- {
- local int i;
- local KFGoreManager GoreManager;
- local RigidBodyContactInfo ContactInfo;
- GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
- if( GoreManager != none && `TimeSince(LastGibCollisionTime) > GoreManager.GetTimeBetweenGibBloodSplats() )
- {
- LastGibCollisionTime = WorldInfo.TimeSeconds;;
- if ( OtherComponent != none && OtherComponent.Owner != none && !OtherComponent.Owner.IsA('KFPawn') ) // Skip pawn-on-pawn collisions
- {
- SoundGroupArch.PlayRigidBodyCollisionSound( self, RigidCollisionData.ContactInfos[ContactIndex].ContactPosition );
- for( i=0; i<RigidCollisionData.ContactInfos.length; i++ )
- {
- ContactInfo = RigidCollisionData.ContactInfos[i];
- GoreManager.LeaveAPersistentBloodSplat(ContactInfo.ContactPosition, -ContactInfo.ContactNormal);
- //DrawDebugCoordinateSystem(ContactInfo.ContactPosition, rotator(-ContactInfo.ContactNormal), 10, true);
- }
- }
- }
- }
- /** plays clientside hit effects using the data in HitFxInfo */
- simulated function PlayTakeHitEffects( vector HitDirection, vector HitLocation, optional bool bUseHitImpulse = true)
- {
- local class<KFDamageType> DmgType;
- local name HitZoneName, HitBoneName;
- local int HitZoneIndex;
- // NVCHANGE_BEGIN - RLS - Debugging Effects
- if( WorldInfo.Game != none && KFGameInfo(WorldInfo.Game).bNVAlwaysHeadshot )
- {
- HitFxInfo.HitBoneIndex = HZI_HEAD;
- }
- // NVCHANGE_BEGIN - RLS - Debugging Effects
- // Cached hit params
- DmgType = HitFxInfo.DamageType;
- HitZoneIndex = HitFxInfo.HitBoneIndex;
- if ( HitZoneIndex != 255 && HitZoneIndex <= HitZones.Length) // INDEX_None -> 255 after byte conversion
- {
- HitZoneName = HitZones[HitZoneIndex].ZoneName;
- HitBoneName = HitZones[HitZoneIndex].BoneName;
- }
- if( DmgType != none )
- {
- // If TornOff hasn't been called yet on client, PlayDying now before hit reactions
- if ( bTearOff && !bPlayedDeath )
- {
- PlayDying(HitDamageType,TakeHitLocation);
- }
- if ( bPlayedDeath )
- {
- PlayDeadHitEffects(HitLocation, HitDirection, HitZoneIndex, HitZoneName, HitBoneName, DmgType, bUseHitImpulse);
- }
- else
- {
- PlayLivingHitEffects(HitLocation, HitDirection, HitZoneIndex, HitZoneName, HitBoneName, DmgType, bUseHitImpulse);
- }
- }
- if( !bPlayedDeath )
- {
- bShowHealth = true;
- SetTimer( 2.f, false, nameOf(ResetHealthVisibilty) );
- }
- Super.PlayTakeHitEffects( HitDirection, HitLocation, bUseHitImpulse );
- }
- /** Plays hit effects on dead zeds, this includes dismemberment and obliteration */
- simulated function PlayDeadHitEffects(vector HitLocation, vector HitDirection, int HitZoneIndex, name HitZoneName, name HitBoneName, class<KFDamageType> DmgType, optional bool bUseHitImpulse)
- {
- local class<KFProj_PinningBullet> PinProjectile${1}< ${3} >
- local KFPawn DeadPawn;
- local KFGoreManager GoreManager;
- local bool bIsDismemberingHit;
- local bool bWasObliterated;
- // If ragdoll and gore is not allowed for dead bodies, check the time of death
- // to see when the pawn died, and skip if he has been dead for a while
- if( bAllowRagdollAndGoreOnDeadBodies || `TimeSince(TimeOfDeath) <= 3.f )
- {
- // If this zone is 'injured' try to dismember it
- if ( (InjuredHitZones & (1 << HitZoneIndex)) > 0 && !HitZones[HitZoneIndex].bPlayedInjury )
- {
- bIsDismemberingHit = PlayDismemberment(HitZoneIndex, DmgType, HitDirection );
- // If there was no dismemberment, explode head instead
- if ( !bIsDismemberingHit && (InjuredHitZones & (1 << HZI_Head)) > 0 )
- {
- PlayHeadAsplode();
- // Set bIsDismemberingHit to true to add an impulse to the neck
- bIsDismemberingHit = true;
- }
- }
- GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
- // If the gore manager doesn't allow mutilation, then we don't allow anything other than basic effects.
- if(GoreManager.AllowMutilation() && HitFxInfo.bObliterated && `TimeSince(TimeOfDeath) < 0.25f && !bUseDamageInflation)
- {
- bWasObliterated = true;
- bIsDismemberingHit = true;
- HandlePartialGoreAndGibs(DmgType, HitLocation, HitDirection, HitBoneName, true);
- GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
- GoreManager.SpawnObliterationBloodEffect(self);
- }
- else
- {
- // Handle damage types from projectiles that can pin zeds to walls
- PinProjectileClass = DmgType.static.GetPinProjectileClass();
- if( PinProjectileClass != none )
- {
- DeadPawn = self;
- PinProjectileClass.static.CreatePin(DeadPawn, HitLocation, HitDirection, HitBoneName);
- }
- HandlePartialGoreAndGibs(DmgType, HitLocation, HitDirection, HitBoneName, false);
- }
- // Apply an impulse to attached limbs and ragdoll
- HandleRagdollImpulseEffects( HitLocation, HitDirection, HitZoneName, HitBoneName, DmgType, bIsDismemberingHit, bUseHitImpulse );
- }
- // Play blood effects. Apply more blood if this was a dismembering hit
- ApplyBloodDecals(HitZoneIndex, HitLocation, HitDirection, HitZoneName, HitBoneName, DmgType, bIsDismemberingHit, bWasObliterated);
- }
- /** Apply impulse to the dead ragdolls bones */
- simulated function HandleRagdollImpulseEffects( vector HitLocation, vector HitDirection, name HitZoneName, name HitBoneName, class<KFDamageType> DmgType, bool bIsDismemberingHit, optional bool bUseHitImpulse )
- {
- local vector ImpulseDir, ParentImpulseDir;
- local float ImpulseScale, ParentImpulseScale;
- local name RBBoneName;
- local name HitBoneParentName;
- // Allow damage type to modify the impulse for a dismembering hit
- ImpulseDir = HitDirection;
- ParentImpulseDir = HitDirection;
- ImpulseScale = 1.f;
- ParentImpulseScale = 1.f;
- if( bIsDismemberingHit )
- {
- DmgType.static.ModifyDismembermentHitImpulse(self, HitZoneName, ImpulseDir, HitDirection, ParentImpulseDir, ImpulseScale, ParentImpulseScale);
- }
- if( HitBoneName != '')
- {
- // Get the rigidbody bone that matches the hit bone name
- RBBoneName = GetRBBoneFromBoneName(HitBoneName);
- }
- // Apply impulse to hit bone. This can be for an alive zed (on death shot) or a dismembered limb
- // In case of explosives, this applies an impulse to the body and not the gibs.
- // Impulse to the gibs is handled separately in CauseGibsAndApplyImpulse()
- if (bUseHitImpulse)
- {
- ApplyRagdollImpulse(DmgType, HitLocation, ImpulseDir, RBBoneName, ImpulseScale);
- // Apply another impulse to parent bone when a limb gets dismembered for the first time. Skip if parent is the root bone.
- if (bIsDismemberingHit && ParentImpulseScale > 0)
- {
- HitBoneParentName = mesh.GetParentBone(HitBoneName);
- // Add additional force if we blew the head off if needed
- if ((HitBoneName == 'head' || HitBoneParentName == 'neck') && DmgType != none)
- {
- ParentImpulseScale *= DmgType.default.HeadDestructionImpulseForceScale;
- }
- // Grab the rigid body bone name so we know which one to apply RB force to
- HitBoneParentName = GetRBBoneFromBoneName(HitBoneParentName);
- // Do not apply an additional impulse to the parent bone on for a dismembering shot if it'st he same as oru RB bone
- if (RBBoneName != HitBoneParentName && Mesh.PhysicsAsset.FindBodyIndex(HitBoneParentName) != INDEX_NONE)
- {
- ApplyRagdollImpulse(DmgType, HitLocation, ParentImpulseDir, HitBoneParentName, ParentImpulseScale);
- }
- }
- }
- }
- /** Hit effects for a living zed */
- simulated function PlayLivingHitEffects(vector HitLocation, vector HitDirection, int HitZoneIndex, name HitZoneName, name HitBoneName, class<KFDamageType> DmgType, optional bool bUseHitImpulse)
- {
- if ( !TryPlayHitReactionAnim(HitDirection, DmgType, HitZoneIndex)
- && PawnAnimInfo.bCanPlayPhysicsHitReactions
- && ActorEffectIsRelevant(HitFXInstigator, false) )
- {
- // Play Physics Body Impact.
- PlayPhysicsBodyImpact(HitLocation, HitDirection, DmgType, HitBoneName);
- }
- // NVCHANGE_BEGIN - RLS - Debugging Effects
- if( WorldInfo.Game != none && KFGameInfo(WorldInfo.Game).bNVAlwaysHeadshot )
- {
- HitZoneIndex = HZI_HEAD;
- }
- // NVCHANGE_BEGIN - RLS - Debugging Effects
- if( HitZoneIndex == HZI_Head )
- {
- // If head is dismembered (InitPartialKinematics was called), do impulse
- if ( bUseHitImpulse && Mesh.PhysicsWeight == 1.f && bIsHeadless )
- {
- ApplyRagdollImpulse(DmgType, HitLocation, HitDirection, HeadBoneName, 1.f);
- }
- ApplyHeadChunkGore(DmgType, HitLocation, HitDirection);
- }
- // Play blood effects. Apply more blood if this was a dismembering hit
- ApplyBloodDecals(HitZoneIndex, HitLocation, HitDirection, HitZoneName, HitBoneName, DmgType, false, false);
- }
- simulated function PlayDyingSound()
- {
- if( ClassIsChildOf(HitDamageType, class'KFDT_Bleeding') )
- {
- SoundGroupArch.PlayBleedoutDyingSound( self );
- }
- else if( bHasBrokenConstraints && !HasMouth() )
- {
- // if we have no head or mouth...
- SoundGroupArch.PlayMouthlessDyingSound( self );
- }
- else
- {
- super.PlayDyingSound();
- }
- }
- simulated function PlayHitZoneGoreSounds( name HitBoneName, vector HitLocation )
- {
- if( SoundGroupArch.ShouldPlayCleaveSound(HitBoneName) )
- {
- SoundGroupArch.PlayCleaveSound( self, HitLocation );
- }
- else
- {
- SoundGroupArch.PlayDismembermentSounds( self, HitLocation );
- }
- }
- /**
- * Return true if we've already broken the input head bone
- *
- * @param BoneName the head bone to check to see if we've already broken it.
- */
- simulated function bool HeadBoneAlreadyBroken(name BoneName)
- {
- if( BrokenHeadBones.Find(BoneName) != INDEX_NONE )
- {
- return true;
- }
- return false;
- }
- /**
- * Return true if we've already broken the input head bone
- *
- * @param BoneName the head bone to add to the broken head bone array.
- */
- simulated function AddBrokenHeadBone(name BoneName)
- {
- if( BrokenHeadBones.Find(BoneName) == INDEX_NONE )
- {
- BrokenHeadBones.AddItem(BoneName);
- }
- return;
- }
- /**
- * Return true if this pawn is ok with having the input head bone be broken
- *
- * @param BoneName the head bone to check to see if we all it to break.
- */
- simulated function bool ShouldAllowHeadBoneToBreak(name BoneName)
- {
- if( !bPlayedDeath && !bIsHeadless && !bTearOff )
- {
- if ( NumHeadChunksRemoved >= MaxHeadChunkGoreWhileAlive )
- {
- return false;
- }
- // Don't allow both sides of the front of the face (both eyes) to be blown
- // off, or both sides of the back of the head and the jaw while the zed is alive and not headless
- if( (BoneName == 'Gore_FrontL' && BrokenHeadBones.Find('Gore_FrontR') != INDEX_NONE)
- || (BoneName == 'Gore_FrontR' && BrokenHeadBones.Find('Gore_FrontL') != INDEX_NONE)
- || (BoneName == 'Gore_BackL' && BrokenHeadBones.Find('Gore_Jaw') != INDEX_NONE && BrokenHeadBones.Find('Gore_BackR') != INDEX_NONE)
- || (BoneName == 'Gore_BackR' && BrokenHeadBones.Find('Gore_Jaw') != INDEX_NONE && BrokenHeadBones.Find('Gore_BackL') != INDEX_NONE)
- || (BoneName == 'Gore_Jaw' && BrokenHeadBones.Find('Gore_BackR') != INDEX_NONE && BrokenHeadBones.Find('Gore_BackL') != INDEX_NONE) )
- {
- return false;
- }
- }
- return true;
- }
- /** Update metadata required by gore chunk attachments */
- simulated function UpdateGoreChunkData(name BoneName)
- {
- if( BoneName == 'Gore_FrontL' ||
- BoneName == 'Gore_FrontR' ||
- BoneName == 'Gore_BackL' ||
- BoneName == 'Gore_BackR' /*||
- BoneName == 'Gore_Jaw'*/ )
- {
- NumHeadChunksRemoved++;
- }
- }
- /** Implementation of delegate function used to determine whether to attach the skull gore chunk.
- The skull gore chunk is used only when one or two head chunks are removed */
- simulated function bool ShouldAttachSkullChunk()
- {
- return NumHeadChunksRemoved < 3;
- }
- /** Implementation of delegate function used to determine whether to detach the skull gore chunk.
- If attached, the skull gore chunk is detached when more than 2 head chunks are removed */
- simulated function bool ShouldDetachSkullChunk()
- {
- return NumHeadChunksRemoved >= 3 && (bPlayedDeath || bIsHeadless || bTearOff);
- }
- /** Handles the gore chunks to be attached to the pawn body when a limb is dismembered or gibbed */
- simulated function HandleGoreChunkAttachments(name DismemberedBone)
- {
- local int AttachmentIndex, ChunkRef;
- local StaticMeshComponent AttachComp;
- local KFCharacterInfo_Monster MonsterInfo;
- local AttachedGoreChunkInfo ChunkInfo;
- local KFGoreChunkAttachmentInfo CurrentChunk;
- // Update information that is required by the attachment/detachment code
- UpdateGoreChunkData(DismemberedBone);
- MonsterInfo = GetCharacterMonsterInfo();
- if( MonsterInfo != none )
- {
- for( AttachmentIndex = 0; AttachmentIndex < MonsterInfo.GoreChunkAttachments.length; AttachmentIndex++ )
- {
- CurrentChunk = MonsterInfo.GoreChunkAttachments[AttachmentIndex];
- if( CurrentChunk.DismemberedBoneList.Find(DismemberedBone) != INDEX_NONE )
- {
- // Search for it in the currently attached chunks list
- ChunkRef = AttachedGoreChunks.Find('AttachmentIndex', AttachmentIndex);
- if( CurrentChunk.ShouldAttachGoreChunk(self) && ChunkRef == INDEX_NONE )
- {
- // Create and intialize the attachment component
- AttachComp = new(self) class'StaticMeshComponent';
- AttachComp.SetStaticMesh(CurrentChunk.StaticMesh);
- AttachComp.SetLightingChannels(PawnLightingChannel);
- AttachComp.CastShadow=true;
- AttachComp.bCastDynamicShadow=true;
- AttachComp.bAllowPerObjectShadowBatching=true;
- //Big head mode specific fix. Increase cull distance for this situation
- if (CurrentChunk.SocketName == 'Head_Attach' && CurrentHeadScale > 1.f)
- {
- AttachComp.SetCullDistance(1000 * CurrentHeadScale);
- }
- else
- {
- AttachComp.SetCullDistance(1000);
- }
- AttachComp.SetShadowParent(mesh);
- AttachComp.bAllowApproximateOcclusion=True;
- AttachComp.SetTraceBlocking(False,False);
- AttachComp.SetActorCollision(False,False);
- // Attach chunk to socket in the pawn mesh
- mesh.AttachComponentToSocket(AttachComp, CurrentChunk.SocketName);
- // Add it to the list of currently active gore attachments
- ChunkInfo.AttachmentIndex = AttachmentIndex;
- ChunkInfo.AttachedComponent = AttachComp;
- AttachedGoreChunks.AddItem(ChunkInfo);
- }
- else if( CurrentChunk.ShouldDetachGoreChunk(self) && ChunkRef != INDEX_NONE )
- {
- AttachComp = AttachedGoreChunks[ChunkRef].AttachedComponent;
- // Detach the chunk from the mesh
- mesh.DetachComponent(AttachComp);
- // Remove it from the list of currently active gore attachments
- AttachedGoreChunks.Remove(ChunkRef, 1);
- }
- }
- }
- }
- }
- /** Calculate type of hit reaction animation from damage info */
- simulated function bool TryPlayHitReactionAnim(vector HitDirection, class<KFDamageType> damageType, byte HitZoneIdx)
- {
- local EPawnOctant AnimDir;
- local EHitReactionAnimType HitReactionType;
- local EHitZoneBodyPart BodyPart;
- local bool bOnlyAdditiveHits;
- if( damageType == none || ActorTimeSince(NextHitReactionAnim_ActorTime) < 0 )
- {
- return false;
- }
- // cannot play hit reaction anims during special moves
- if ( IsDoingSpecialMove() )
- {
- bOnlyAdditiveHits = true;
- if ( !SpecialMoves[SpecialMove].bAllowHitReactions )
- {
- return false;
- }
- }
- // Set the limb used for incap effects
- BodyPart = (HitZoneIdx != 255 && HitZoneIdx < HitZones.Length) ? HitZones[HitZoneIdx].Limb : BP_Torso;
- HitReactionType = HIT_Light;
- // If we're moving (e.g. DoPauseAI wasn't called because of incap cooldown) don't try hard/medium
- // This is not 100% reliable, but worst case we get a reasonable upper body blend while moving
- bOnlyAdditiveHits = bOnlyAdditiveHits || VSizeSq(Velocity) > 50.f;
- if ( !bOnlyAdditiveHits )
- {
- HitReactionType = EHitReactionAnimType(AfflictionHandler.GetPredictedHitReaction(DamageType, BodyPart));
- }
- switch (BodyPart)
- {
- case BP_LeftArm:
- AnimDir = CalcOctagonRegion(Rotation, -HitDirection);
- if ( AnimDir == DIR_Forward || AnimDir == DIR_ForwardLeft || AnimDir == DIR_ForwardRight )
- AnimDir = DIR_ForwardLeft;
- else
- AnimDir = DIR_BackwardLeft;
- break;
- case BP_RightArm:
- AnimDir = CalcOctagonRegion(Rotation, -HitDirection);
- if ( AnimDir == DIR_Forward || AnimDir == DIR_ForwardLeft || AnimDir == DIR_ForwardRight )
- AnimDir = DIR_ForwardRight;
- else
- AnimDir = DIR_BackwardRight;
- break;
- default:
- AnimDir = CalcOctagonRegion(Rotation, -HitDirection);
- break;
- }
- // Play animation
- return PawnAnimInfo.PlayHitReactionAnim(self, HitReactionType, AnimDir);
- }
- /** plays clientside hit effects using the data in HitFxInfo */
- simulated function ApplyBloodDecals(int HitZoneIndex, vector HitLocation, vector HitDirection, name HitZoneName, name HitBoneName, class<KFDamageType> DmgType, bool bIsDismemberingHit, bool bWasObliterated)
- {
- local KFGoreManager GoreManager;
- GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
- if( DmgType != none && GoreManager != none )
- {
- // Spawn wound decal on the damaged pawn
- if( !bWasObliterated && !bIsCloaking )
- {
- GoreManager.LeaveABodyWoundDecal(self, HitLocation, HitDirection, HitZoneName, HitBoneName, DmgType);
- }
- // Spawn blood splatter decal effect behind the damaged pawn
- if ( DmgType.default.bShouldSpawnBloodSplat )
- {
- GoreManager.LeaveABloodSplatterDecal(self, HitLocation, HitDirection);
- }
- // Spawn persistent blood splatters
- if( DmgType.default.bShouldSpawnPersistentBlood )
- {
- GoreManager.CausePersistentBlood(self, DmgType, HitLocation, HitDirection, HitZoneIndex, bIsDismemberingHit, bWasObliterated);
- }
- }
- }
- /**
- * Gibbing, partial gore and gib impusle effects handled here.
- *
- * @param ObliterateGibs If true this function will rip apart all limbs. If false, the default behavior is to select random bones to rip apart.
- */
- simulated function HandlePartialGoreAndGibs(
- class<KFDamageType> DmgType,
- vector HitLocation,
- vector HitDirection,
- name HitBoneName,
- bool ObliterateGibs)
- {
- local KFGoreManager GoreManager;
- local KFCharacterInfo_Monster MonsterInfo;
- GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
- if( DmgType != none && GoreManager != none )
- {
- if( GoreManager.AllowMutilation() )
- {
- // Enable alternate bone weighting and gore skeleton
- if( !bIsGoreMesh )
- {
- SwitchToGoreMesh();
- }
- // Apply gore only if we were able to successfully switch to the gore mesh
- if( bIsGoreMesh )
- {
- MonsterInfo = GetCharacterMonsterInfo();
- // Try to apply exlosion gore first
- if(ObliterateGibs)
- {
- ApplyObliterationFxGore( GoreManager, MonsterInfo, DmgType );
- }
- else
- if( HitFxInfo.bRadialDamage)
- {
- ApplyRadialFxGore( GoreManager, MonsterInfo, DmgType );
- }
- else
- {
- ApplyTakeHitFxGore( GoreManager, MonsterInfo, DmgType, HitLocation, HitDirection, HitBoneName );
- }
- }
- }
- }
- }
- /**
- * Apply any gibbing from radial damage
- *
- * @param ObliterateGibs If true this function will rip apart all limbs. If false, the default behavior is to select random bones to rip apart.
- */
- simulated function ApplyRadialFxGore( KFGoreManager GoreManager, KFCharacterInfo_Monster MonsterInfo, class<KFDamageType> DmgType )
- {
- local array<name> OutGibBoneList;
- local float NormalizedDistanceScale;
- local vector ExplosionOrigin;
- local int NumGibs;
- // Try to apply exlosion gore first
- if( DmgType.default.bCanGib)
- {
- ExplosionOrigin = HitFxRadialInfo.RadiusHurtOrigin;
- // Use distance from explosion along with some randomization to figure out
- // how many joints to dismember. Distance based calculation is skipped after 10 meters.
- NormalizedDistanceScale = VSize(Location - ExplosionOrigin)/1000.f;
- NumGibs = FCeil(FMax(1.f - NormalizedDistanceScale, 0.f) * rand(10)) + rand(3);
- NumGibs *= MonsterInfo.ExplosionGibScale;
- GetClosestHitBones(NumGibs, ExplosionOrigin, OutGibBoneList);
- // Gib-ify!
- // Spawn exlosion PS at the root bone
- GoreManager.CauseGibsAndApplyImpulse(
- self,
- DmgType,
- ExplosionOrigin,
- OutGibBoneList,
- MonsterInfo.ExplosionEffectTemplate,
- mesh.GetBoneLocation(mesh.GetBoneName(0)));
- }
- }
- /**
- * This function will dismember all rigid body bones in the mesh.
- */
- simulated function ApplyObliterationFxGore( KFGoreManager GoreManager, KFCharacterInfo_Monster MonsterInfo, class<KFDamageType> DmgType )
- {
- local array<name> OutGibBoneList;
- local vector ObliterationLocation;
- local int MaxNumGibs;
- if( DmgType.default.MaxObliterationGibs > 0 )
- {
- MaxNumGibs = DmgType.default.MaxObliterationGibs;
- }
- else
- {
- // Allowed to disconnect 100 bones (basically, all bones.)
- MaxNumGibs = 100;
- }
- // Try to apply exlosion gore first
- if( DmgType.default.bCanGib)
- {
- // Origin is the point where the explosive went off
- if(HitFxInfo.bRadialDamage == true)
- {
- ObliterationLocation = HitFxRadialInfo.RadiusHurtOrigin;
- }
- else if( DmgType.default.bUseHitLocationForGibImpulses )
- {
- ObliterationLocation = HitFxInfo.HitLocation;
- if( DmgType.default.bPointImpulseTowardsOrigin )
- {
- ObliterationLocation -= DecodeUnitVector(HitFxInfo.EncodedHitDirection) * DmgType.default.ImpulseOriginScale;
- }
- else
- {
- ObliterationLocation.Z = Location.Z - CylinderComponent.CollisionHeight * 0.25f;
- }
- }
- else
- {
- ObliterationLocation = self.Location;
- }
- GetClosestHitBones(MaxNumGibs, ObliterationLocation, OutGibBoneList);
- // Gib-ify!
- // Spawn exlosion PS at the root bone
- GoreManager.CauseGibsAndApplyImpulse(
- self,
- DmgType,
- ObliterationLocation,
- OutGibBoneList,
- MonsterInfo.ExplosionEffectTemplate,
- mesh.GetBoneLocation(mesh.GetBoneName(0)));
- }
- }
- /** Apply any gore or breaks from non radial damage */
- simulated function ApplyTakeHitFxGore(
- KFGoreManager GoreManager,
- KFCharacterInfo_Monster MonsterInfo,
- class<KFDamageType> DmgType,
- vector HitLocation,
- vector HitDirection,
- name HitBoneName)
- {
- local array<name> OutGibBoneList;
- local int JointIndex, ExplosionBreakIdx, BoneIdx;
- local vector ExplosionOrigin;
- local name GibBone;
- // Apply partial gore (if supported)
- GoreManager.ConditionalApplyPartialGore(self, DmgType, HitLocation, HitDirection, HitBoneName);
- // Apply any hit explosion gore specified for this bone
- for( JointIndex = 0; JointIndex < MonsterInfo.GoreJointSettings.length; JointIndex++ )
- {
- if( MonsterInfo.GoreJointSettings[JointIndex].HitBoneName == HitBoneName )
- {
- for( ExplosionBreakIdx = 0;
- ExplosionBreakIdx < MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore.length;
- ExplosionBreakIdx++ )
- {
- if( MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore[ExplosionBreakIdx].ConstrainToDamageGroups.length == 0 ||
- MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore[ExplosionBreakIdx].ConstrainToDamageGroups.Find(DGT_None) != INDEX_None ||
- MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore[ExplosionBreakIdx].ConstrainToDamageGroups.Find(DmgType.default.GoreDamageGroup) != INDEX_None)
- {
- // Origin is the place of hit
- ExplosionOrigin = mesh.GetBoneLocation(HitBoneName);
- // Only include bones that haven't been gibbed/broken already
- for( BoneIdx = 0;
- BoneIdx < MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore[ExplosionBreakIdx].BreakBones.length;
- BoneIdx++ )
- {
- GibBone = MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore[ExplosionBreakIdx].BreakBones[BoneIdx].BoneName;
- if( !mesh.IsBrokenConstraint(GibBone) )
- {
- OutGibBoneList.AddItem(GibBone);
- }
- }
- // Gib-ify!
- GoreManager.CauseGibsAndApplyImpulse(
- self,
- DmgType,
- ExplosionOrigin,
- OutGibBoneList,
- MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore[ExplosionBreakIdx].ParticleSystemTemplate,
- ExplosionOrigin,
- HitBoneName);
- }
- }
- }
- }
- }
- /** Gibbing and partial gore effects handled here */
- simulated function ApplyHeadChunkGore(class<KFDamageType> DmgType, vector HitLocation, vector HitDirection)
- {
- local KFGoreManager GoreManager;
- if( bCanCloak && IsAliveAndWell() )
- {
- return;
- }
- GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
- if( DmgType != none && GoreManager != none )
- {
- if( GoreManager.AllowMutilation() )
- {
- // Enable alternate bone weighting and gore skeleton
- if( !bIsGoreMesh )
- {
- SwitchToGoreMesh();
- }
- if ( !bPlayedDeath )
- {
- // @hack: force all gore as if it was a handgun so we only get head chunks
- DmgType = class'KFDT_Ballistic';
- GoreManager.ConditionalApplyPartialGore(self, DmgType, HitLocation, HitDirection, HitZones[HZI_HEAD].BoneName);
- }
- }
- }
- }
- /** Reliably play any gore effects related to a zone/limb being dismembered */
- simulated function HitZoneInjured(optional int HitZoneIdx=INDEX_None)
- {
- // --------------------------------------------------------------
- // Network: Server (Also called on clients after TearOff)
- // --------------------------------------------------------------
- if ( Role == ROLE_Authority && HitZoneIdx != INDEX_None )
- {
- if ( HitZoneIdx == HZI_Head )
- {
- CauseHeadTrauma();
- }
- // replicate to all clients
- InjuredHitZones = InjuredHitZones | (1 << HitZoneIdx);
- }
- // Play living head explosion effects. Some effects use the clientside HitFX system (e.g. PlayDismemberment),
- // but this should be used when we need a reliably replicated effect.
- if (WorldInfo.NetMode != NM_DedicatedServer && (!bTearOff && !bPlayedDeath))
- {
- if ( (InjuredHitZones & (1 << HZI_Head)) > 0 && !bDisableHeadless)
- {
- // Use HitFxInfo for "best guess" DT and CreationTime to fallback
- // on HeadAsplode when coming into relevancy.
- if ( `TimeSince(CreationTime) > 1.f )
- {
- PlayDismemberment(HZI_Head, HitFxInfo.DamageType);
- }
- // If there was no dismemberment, explode head instead
- if ( !HitZones[HZI_Head].bPlayedInjury )
- {
- PlayHeadAsplode();
- }
- }
- }
- }
- simulated function PlayHeadAsplode()
- {
- local KFGoreManager GoreManager;
- local name BoneName;
- if( HitZones[HZI_Head].bPlayedInjury )
- {
- return;
- }
- //Headless turned off, get out of here
- if (bDisableHeadless)
- {
- return;
- }
- // make sure this doesn't happen after death so that normal HitFX/Gore path is followed. Using
- // bTearOff since bPlayDying may not be set yet on the client
- // Let the head be blown off for a short time still after death
- if ( (bTearOff || bPlayedDeath) && TimeOfDeath > 0 && `TimeSince(TimeOfDeath) > 0.75 )
- {
- return;
- }
- // Enable alternate bone weighting and gore skeleton
- GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
- if( GoreManager != none && GoreManager.AllowHeadless() )
- {
- if( !bIsGoreMesh )
- {
- SwitchToGoreMesh();
- }
- }
- // Apply mutilation gore only if we were able to successfully switch to the gore mesh
- if( bIsGoreMesh && GoreManager != none )
- {
- BoneName = HitZones[HZI_Head].BoneName;
- GoreManager.CrushBone( self, BoneName );
- SoundGroupArch.PlayHeadPopSounds( self, mesh.GetBoneLocation(BoneName) );
- HitZones[HZI_Head].bPlayedInjury = true;
- SpawnHeadShotFX(KFPlayerReplicationInfo(HitFxInfo.DamagerPRI));
- }
- }
- /** Dismember this hit zone if it's not dismembered already
- * InDmgType and HitDirection are only ever used on the client after a death shot
- */
- simulated function bool PlayDismemberment(int InHitZoneIndex, class<KFDamageType> InDmgType, optional vector HitDirection)
- {
- local KFGoreManager GoreManager;
- local name BreakBoneName;
- // Bail out if the hit zone has already been dismembered
- if( HitZones[InHitZoneIndex].bPlayedInjury )
- {
- return false;
- }
- // This hit zone was injured (see CanInjureHitZone), but shouldn't be dismembered
- if ( !InDmgType.static.CanDismemberHitZone( HitZones[InHitZoneIndex].ZoneName ) )
- {
- return false;
- }
- // Enable alternate bone weighting and gore skeleton
- GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
- if( GoreManager != none && GoreManager.AllowMutilation() )
- {
- if( !bIsGoreMesh )
- {
- SwitchToGoreMesh();
- }
- // Apply mutilation gore only if we were able to successfully switch to the gore mesh
- if( bIsGoreMesh )
- {
- // Get the bone to dismember from the hit zone
- BreakBoneName = HitZones[InHitZoneIndex].BoneName;
- // If we're dead, allow damage type to override the bone
- if ( Health <= 0 && !IsZero(HitDirection) )
- {
- InDmgType.static.GetBoneToDismember(self, HitDirection, HitZones[InHitZoneIndex].ZoneName, BreakBoneName);
- }
- // Dismember
- GoreManager.CauseDismemberment(self, BreakBoneName, InDmgType);
- if (InHitZoneIndex == HZI_HEAD)
- {
- SpawnHeadShotFX(KFPlayerReplicationInfo(HitFxInfo.DamagerPRI));
- }
- PlayHitZoneGoreSounds(BreakBoneName, mesh.GetBoneLocation(BreakBoneName));
- HitZones[InHitZoneIndex].bPlayedInjury = true;
- // If we're still alive (non-ragdoll), start partial physics ragdoll
- // Note: bPlayedDeath may not be set yet on client, so use Health
- if ( Health > 0 && bHasBrokenConstraints )
- {
- InitPartialKinematics();
- }
- return true;
- }
- }
- return false;
- }
- /** Apply damage to a specific zone (useful for gore effects) */
- function TakeHitZoneDamage(float Damage, class<DamageType> DamageType, int HitZoneIdx, vector InstigatorLocation)
- {
- local float HeadHealthPercentage;
- if (HitZoneIdx > HitZones.Length)
- {
- return;
- }
- Super.TakeHitZoneDamage(Damage, DamageType, HitZoneIdx, InstigatorLocation);
- // When GoreHealth <= 0, check to see if this weapon can dismember limbs
- if ( HitZones[HitZoneIdx].GoreHealth <= 0 && CanInjureHitZone(DamageType, HitZoneIdx) )
- {
- HitZoneInjured(HitZoneIdx);
- }
- // Handle head injuries
- if ( HitZoneIdx == HZI_Head )
- {
- // Based on head health, calculate number of head chunks we're allowed to remove
- if( !bPlayedDeath && !bIsHeadless && !bTearOff )
- {
- HeadHealthPercentage = GetHeadHealthPercent();
- if( HeadHealthPercentage > 0.5 )
- {
- MaxHeadChunkGoreWhileAlive = 1;
- }
- else if ( HeadHealthPercentage > 0.25 )
- {
- MaxHeadChunkGoreWhileAlive = 2;
- }
- else if ( HeadHealthPercentage > 0.0 )
- {
- MaxHeadChunkGoreWhileAlive = 3;
- }
- }
- }
- }
- /** Returns the percentage of head health remaining on this zed */
- function float GetHeadHealthPercent()
- {
- local float HeadHealth, HeadHealthMax;
- HeadHealth = float(HitZones[HZI_Head].GoreHealth);
- HeadHealthMax = float(HitZones[HZI_Head].MaxGoreHealth);
- return HeadHealth / HeadHealthMax;
- }
- /** Called by KFPawnAnimInfo when determining whether an attack can be performed */
- simulated function bool ShouldPlayHeadlessMeleeAnims()
- {
- return bIsHeadless || bEmpPanicked;
- }
- /** Generally used to determine which/whether dialog/voice events should be played */
- simulated event bool HasMouth()
- {
- if( !bHasBrokenConstraints )
- {
- return true;
- }
- return !Mesh.IsBrokenConstraint( 'head' ) && !Mesh.IsBoneHidden( Mesh.MatchRefBone('gore_jaw') );
- }
- /** Breaks all joint constraints using dependent gore system */
- simulated function ForceBreakAllConstraints()
- {
- local int i;
- local KFGoreManager GoreManager;
- bHasBrokenConstraints = TRUE;
- // Enable alternate bone weighting and gore skeleton
- GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
- if( GoreManager == none )
- {
- return;
- }
- if( GoreManager.AllowMutilation() )
- {
- if( !bIsGoreMesh )
- {
- SwitchToGoreMesh();
- }
- for( i = 0; i < CharacterMonsterArch.GoreJointSettings.length; i++ )
- {
- if ( !CharacterMonsterArch.GoreJointSettings[i].bNonBreakableJoint )
- {
- GoreManager.BreakConstraint( self, CharacterMonsterArch.GoreJointSettings[i].HitBoneName );
- }
- }
- }
- }
- function NotifyMeleeAttackFinished();
- /** Checks if we're able to reduce or restore our collision size if we're going through
- * a choke point and we have a large collision cylinder */
- simulated function ChokePointTimer()
- {
- if( !IsTimerActive(nameof(ChokePointTimer)) )
- {
- SetTimer(0.3f, true, nameof(ChokePointTimer), self);
- }
- if( CurrentChokePointTrigger != none )
- {
- // Check if we want to restore our collision
- if( CurrentChokePointTrigger.CanRestoreChokeCollision(self) )
- {
- // If our enemy has entered the choke point and we can fit in the geometry, retore our collision
- if( CylinderComponent.CollisionRadius < CylinderComponent.default.CollisionRadius
- && !CheckEncroachingWorldGeometry() )
- {
- SetChokePointCollision(false);
- }
- }
- // if our collision is too large to fit through this chokepiont, reduce it
- else if( CylinderComponent.CollisionRadius > CurrentChokePointTrigger.MaxCollisionRadius )
- {
- SetChokePointCollision(true);
- }
- }
- // if we are out of a chokepoint and can restore our collision, do so
- else if( !CheckEncroachingWorldGeometry() )
- {
- // Only restore collision if it's been reduced
- if( CylinderComponent.CollisionRadius < CylinderComponent.default.CollisionRadius )
- {
- SetChokePointCollision(false);
- }
- ClearTimer(nameof(ChokePointTimer), self);
- }
- }
- event SetDamageInflation(float NewInflation)
- {
- RepDamageInflateParam = FloatToByte(NewInflation);
- SetHeadScale(1.0 + (GetCurrentInflation() / 2.0), CurrentHeadScale);
- HandleDamageInflation();
- }
- simulated function float GetCurrentInflation()
- {
- local float CurrentInflation;
- CurrentInflation = FClamp(ByteToFloat(RepInflateMatParam) + ByteToFloat(RepDamageInflateParam) - ByteToFloat(RepBleedInflateMatParam), -1.0, 1.0);
- //`log("*** Inflation" @ CurrentInflation);
- //RepInflateMatParam - From microwave affliction
- //RepDamageInflateParam - From modes where gametype modifies size (Shrinky Dinky, Beefcake, etc)
- //RepBleedInflateMatParam - From bleed affliction, set to negative here so we don't lose precision in byte conversion
- return CurrentInflation;
- }
- simulated function HandleDamageInflation()
- {
- if ( WorldInfo.NetMode != NM_DedicatedServer )
- {
- UpdateVisualInflation(GetCurrentInflation() * 2.0);
- }
- }
- simulated function UpdateBleedIncapFX()
- {
- local float CurrentStrength;
- if (WorldInfo.NetMode != NM_DedicatedServer)
- {
- CurrentStrength = ByteToFloat(RepBleedInflateMatParam);
- //We've gone past the threshold, create a PSC
- if ((CurrentStrength != 0 && IsAliveAndWell()) && BleedIncapPSC == none)
- {
- BleedIncapPSC = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment(BleedIncapFX, Mesh, class'KFSM_Stunned'.default.DazedFXSocketName, true);
- if (BleedIncapPSC != none)
- {
- BleedIncapPSC.SetAbsolute(false, true, false);
- BleedIncapPSC.SetRotation(rotator(vect(0, 0, 1)) + class'KFSM_Stunned'.default.DazedFXRelativeRotation);
- }
- }
- //We've underrun the threshold, remove PSC
- else if ((CurrentStrength == 0 || !IsAliveAndWell()) && BleedIncapPSC != none)
- {
- BleedIncapPSC.DeactivateSystem();
- DetachComponent(BleedIncapPSC);
- BleedIncapPSC = none;
- }
- }
- }
- private final simulated function SpawnHeadShotFX(KFPlayerReplicationInfo DamagerPRI)
- {
- local KFPlayerController KFPC;
- local HeadshotEffect SHeadshotEffect;
- local vector SpawnVector;
- if (DamagerPRI != none)
- {
- if (WorldInfo.NetMode != NM_DedicatedServer)
- {
- KFPC = KFPlayerController(WorldInfo.GetALocalPlayerController());
- if (KFPC == none || (KFPC.bHideRemotePlayerHeadshotEffects && DamagerPRI != KFPC.PlayerReplicationInfo))
- {
- return;
- }
- SHeadshotEffect = class'KFHeadShotEffectList'.static.GetUnlockedHeadshotEffect(DamagerPRI.GetHeadShotEffectID());
- if (SHeadshotEffect.Id != INDEX_NONE)
- {
- Mesh.GetSocketWorldLocationAndRotation(class'KFSM_Stunned'.default.DazedFXSocketName, SpawnVector);
- WorldInfo.MyEmitterPool.SpawnEmitter(SHeadshotEffect.EffectPS, SpawnVector);
- HeadShotAkComponent.PlayEvent(SHeadshotEffect.HeadshotSoundEffect, true, true);
- }
- }
- }
- }
- /*********************************************************************************************
- * @name Ephemeral Stats tracking
- ********************************************************************************************* */
- static function bool IsLargeZed(){ return default.bLargeZed; }
- static event bool IsABoss(){ return false; }
- /*********************************************************************************************
- * @name Perk related
- ********************************************************************************************* */
- simulated event UpdateSpottedStatus();
- static function bool IsStalkerClass(){ return default.bIsStalker${1}< ${3} > }
- static function bool IsCrawlerClass(){ return default.bIsCrawler${1}< ${3} > }
- static function bool IsFleshpoundClass(){ return default.bIsFleshpound${1}< ${3} > }
- static function bool IsClotClass(){ return default.bIsClot${1}< ${3} > }
- static function bool IsBloatClass(){ return default.bIsBloat${1}< ${3} > }
- function float GetPerkDoTScaler( optional Controller InstigatedBy, optional class<KFDamageType> KFDT )
- {
- local KFPlayerController KFPC;
- local KFPerk InstigatorPerk;
- local float DoTScaler;
- DoTScaler = 1.f;
- if( InstigatedBy != none )
- {
- KFPC = KFPlayerController(InstigatedBy);
- if( KFPC != none )
- {
- InstigatorPerk = KFPC.GetPerk();
- if( InstigatorPerk != none )
- {
- DoTScaler += InstigatorPerk.GetDoTScalerAdditions(KFDT);
- }
- }
- }
- return DoTScaler;
- }
- /*********************************************************************************************
- * @name Overhead Debug Text
- ********************************************************************************************* */
- /**
- * Sets debug text rendering
- * @param bTurnOn true to enable, false to disable over-NPC-head debug text rendering
- */
- function SetDebugTextRendering( bool bTurnOn )
- {
- local PlayerController PC;
- local KFHUDBase KFHud;
- bDebug_DrawOverheadInfo = bTurnOn;
- // final local player's hud object
- ForEach LocalPlayerControllers(class'PlayerController', PC)
- {
- KFHud = KFHUDBase(PC.MyHUD);
- }
- if ( KFHud != None )
- {
- // Add self to player HUD PostRenderedActor list
- KFHud.SetPostRenderingFor(bTurnOn, self);
- }
- }
- /** Called by HUD for actors in HUD's PostRenderedActor list */
- simulated event PostRenderFor( PlayerController PC, Canvas Canvas, vector CameraPosition, vector CameraDir )
- {
- local KFHUDBase PCHUD;
- local bool bShowAllCategories;
- local vector2d ScreenPos;
- PCHUD = KFHUDBase(PC.myHUD );
- if( PCHUD.ShouldDisplayDebug('All') || PCHUD.ShouldDisplayDebug('AllVerbose') )
- {
- bShowAllCategories = true;
- }
- if( MyKFAIC != none )
- {
- if( bShowAllCategories || PCHUD.ShouldDisplayDebug('AIMovement') || PCHUD.ShouldDisplayDebug('AIPathing') )
- {
- MyKFAIC.DrawDebugOverheadMovementPhaseData( PCHUD, ScreenPos );
- }
- if( bShowAllCategories || PCHUD.ShouldDisplayDebug('BehaviorTree') )
- {
- MyKFAIC.DrawBehaviorTreeIconOverhead( PCHUD );
- }
- }
- if( PC != none && PC.myHUD != none && bDebug_DrawOverheadInfo )
- {
- // Note: HUD type will either be KFHUDBase or KFDebugCameraHUD (for ToggleDebugCamera mode) which is a KFHDUBase subclass.
- // ...will need to make sure any other similar code works for both types.
- PCHUD = KFHUDBase(PC.myHUD );
- if( PCHUD != none )
- {
- DrawDebugOverheadText( PCHUD, ScreenPos );
- }
- }
- else if( PC != none && PC.myHUD != none && bDebug_DrawSprintingOverheadInfo )
- {
- PCHUD = KFHUDBase(PC.myHUD );
- if( PCHUD != none )
- {
- DrawDebugOverheadSprintingText( PCHUD );
- }
- }
- }
- function DrawDebugOverheadText( KFHUDBase HUD, Out Vector2d ScreenPos )
- {
- local Texture2D Icon;
- local PlayerController PC;
- local Canvas Canvas;
- local Vector CameraLoc, ScreenLoc;
- local Rotator CameraRot;
- local float X, Y;
- local float DOT;
- local array<string> OverheadTexts;
- local array<Color> OverheadColors;
- local int i;
- if( !IsAliveAndWell() )
- {
- return;
- }
- Canvas = HUD.Canvas;
- ScreenLoc = Canvas.Project( Location + vect(0,0,1) * GetCollisionHeight() * 1.5f );
- if( ScreenLoc.X < 0 || ScreenLoc.X >= HUD.Canvas.ClipX || ScreenLoc.Y < 0 && ScreenLoc.Y >= HUD.Canvas.ClipY )
- {
- return;
- }
- PC = HUD.PlayerOwner;
- Canvas.SetDrawColor(0,255,64);
- OverheadColors[OverheadColors.Length] = MakeColor(0,255,64);
- PC.GetPlayerViewPoint( CameraLoc, CameraRot );
- Dot = vector( CameraRot ) dot ( Location - CameraLoc );
- if( Dot < 0.5f )
- {
- return;
- }
- // Draw Icon (need some unique Zed icons)
- Icon = Texture2D'ENG_EditorResources_TEX.AI.S_AI';
- if (Icon != None)
- {
- Canvas.SetPos( ScreenLoc.X - Icon.SizeX / 2, ScreenLoc.Y - Icon.SizeY / 2, ScreenLoc.Z );
- Canvas.DrawTexture( Icon, 1.f );
- X = ScreenLoc.X + Icon.SizeX/2 + 5;
- Y = ScreenLoc.Y - Icon.SizeY/2;
- }
- else
- {
- X = ScreenLoc.X;
- Y = ScreenLoc.Y;
- }
- if( ScreenPos.X == 0 && ScreenPos.Y == 0 )
- {
- Canvas.SetPos( X, Y );
- }
- else
- {
- Canvas.SetPos( ScreenPos.X, ScreenPos.Y );
- }
- Canvas.Font = class'Engine'.Static.GetSmallFont();
- GetOverheadDebugText( HUD, OverheadTexts, OverheadColors );
- // Draw the AI Command info, etc
- if( MyKFAIC != none )
- {
- MyKFAIC.GetCommandStack( HUD, OverheadTexts, OverheadColors );
- }
- for (i = 0; i < OverheadTexts.length; i++)
- {
- // Set custom color if it exists
- if( OverheadColors[i] != MakeColor(0,0,0,0) )
- {
- Canvas.SetDrawColor(OverheadColors[i].R, OverheadColors[i].G, OverheadColors[i].B, 255);
- }
- else
- {
- Canvas.SetDrawColor(0, 255, 64, 255);
- }
- Canvas.DrawText(OverheadTexts[i]);
- }
- ScreenPos.X = Canvas.CurX;
- ScreenPos.Y = Canvas.CurY;
- if( HUD.ShouldDisplayDebug('AIMovement') )
- {
- DrawDebugSphere( Location - vect(0,0,1) * (GetCollisionHeight() - MaxStepHeight), 8, 10, 255, 255, 0, FALSE); // Yellow = MaxStepHeight
- DrawDebugSphere( Location + vect(0,0,1) * MaxJumpHeight, 15, 10, 0, 255, 0, FALSE); // Green = MaxJumpHeight
- }
- }
- function DrawDebugOverheadSprintingText( KFHUDBase HUD )
- {
- local Texture2D moveTypeIcon;
- local PlayerController plyCtrl;
- local Canvas displayCanvas;
- local Vector plyCameraLoc, plyScreenLoc;
- local Rotator plyCameraRot;
- local String displayStr;
- local float displayX, displayY;
- local float infrontDOT;
- local Color newTextColor;
- if( !IsAliveAndWell() )
- {
- return;
- }
- displayCanvas = HUD.Canvas;
- plyScreenLoc = displayCanvas.Project( Location + vect(0,0,1) * GetCollisionHeight() * 1.5f );
- if( plyScreenLoc.X < 0 || plyScreenLoc.X >= HUD.Canvas.ClipX || plyScreenLoc.Y < 0 && plyScreenLoc.Y >= HUD.Canvas.ClipY )
- {
- return;
- }
- plyCtrl = HUD.PlayerOwner;
- displayCanvas.SetDrawColor(255,255,255);
- plyCtrl.GetPlayerViewPoint( plyCameraLoc, plyCameraRot );
- infrontDOT = vector( plyCameraRot ) dot ( Location - plyCameraLoc );
- if( infrontDOT < 0.5f )
- {
- return;
- }
- if( bDebug_UseIconForShowingSprintingOverheadInfo )
- {
- if( bIsSprinting )
- {
- moveTypeIcon = MyKFAIC.MyAIDirector.GetDebugIsSprintingIcon();
- }
- else
- {
- moveTypeIcon = MyKFAIC.MyAIDirector.GetDebugIsWalkingIcon();
- }
- }
- else
- {
- moveTypeIcon = none;
- if( bIsSprinting )
- {
- displayStr = "S";
- newTextColor = class'HUD'.default.RedColor;
- }
- else
- {
- displayStr = "W";
- newTextColor = class'HUD'.default.GreenColor;
- }
- }
- if (moveTypeIcon != None)
- {
- displayCanvas.SetPos( plyScreenLoc.X - moveTypeIcon.SizeX / 2, plyScreenLoc.Y - moveTypeIcon.SizeY / 2, plyScreenLoc.Z );
- displayCanvas.DrawTexture( moveTypeIcon, 1.f );
- displayX = plyScreenLoc.X + moveTypeIcon.SizeX/2 + 5;
- displayY = plyScreenLoc.Y - moveTypeIcon.SizeY/2;
- }
- else
- {
- displayX = plyScreenLoc.X - GetCollisionRadius();
- displayY = plyScreenLoc.Y;
- }
- displayCanvas.SetPos( displayX, displayY );
- if( Len(displayStr) > 0 )
- {
- displayCanvas.Font = MyKFAIC.MyAIDirector.GetAiDebugScreenLargeFont();
- class'KFAIController'.static.DrawDebugText( HUD, displayStr, newTextColor );
- }
- }
- simulated function GetOverheadDebugText( KFHUDBase HUD, out array<string> OverheadTexts, out array<Color> OverheadColors )
- {
- local string DebugText;
- local KFGameInfo KFGI;
- local float HealthMod;
- local float HeadHealthMod;
- local bool bShowAll, bShowAllVerbose;
- if( HUD.ShouldDisplayDebug('All') )
- {
- bShowAll = true;
- }
- if( HUD.ShouldDisplayDebug('AllVerbose') )
- {
- bShowAll = true;
- bShowAllVerbose = true;
- }
- KFGI = KFGameInfo(WorldInfo.Game);
- if ( KFGI != none )
- {
- KFGI.DifficultyInfo.GetAIHealthModifier(self, KFGI.GetModifiedGameDifficulty(), KFGI.GetLivingPlayerCount(), HealthMod, HeadHealthMod);
- if( bShowAllVerbose || HUD.ShouldDisplayDebug('ZedHealthVerbose') )
- {
- DebugText = " Health: "$Health
- $" HeadHealth: "$HitZones[HZI_HEAD].GoreHealth$"\n"
- $" Starting Health: "$HealthMod*default.Health
- $" Starting HeadHealth: "$HeadHealthMod*Default.HitZones[HZI_HEAD].GoreHealth$"\n"
- $" Health Modifier: "$HealthMod
- $" Default Health: "$default.Health
- $" Default HeadHealth: "$Default.HitZones[HZI_HEAD].GoreHealth;
- }
- else if( bShowAll || HUD.ShouldDisplayDebug('ZedHealth') )
- {
- DebugText = " Health: "$Health
- $" HeadHealth: "$HitZones[HZI_HEAD].GoreHealth
- $" HeadHealth %: "$(HitZones[HZI_HEAD].GoreHealth/(HeadHealthMod*Default.HitZones[HZI_HEAD].GoreHealth) * 100);
- }
- }
- OverheadTexts[OverheadTexts.Length] = DebugText;
- if( bShowAll || HUD.ShouldDisplayDebug('AITargeting') )
- {
- if( MyKFAIC != none )
- {
- DebugText = "---------- AI Targeting ----------\n";
- if ( MyKFAIC.Enemy != None )
- {
- DebugText = DebugText@"ENEMY: "$MyKFAIC.Enemy.GetHumanReadableName()$" Enemy Dist: "$VSize(MyKFAIC.Enemy.Location - Location)$"\n";
- }
- else
- {
- DebugText = DebugText@"ENEMY: NO Enemy "$"\n";
- }
- if( MyKFAIC.Focus != none )
- {
- DebugText = DebugText@"FOCUS: "$MyKFAIC.Focus$"\n";
- DrawDebugLine(Mesh.GetBoneLocation(HeadBoneName), MyKFAIC.Focus.Location, 255, 255, 0, FALSE); // Yellow = focus
- }
- if( MyKFAIC.GetFocalPoint() != vect(0,0,0) )
- {
- DrawDebugLine(Mesh.GetBoneLocation(HeadBoneName), MyKFAIC.GetFocalPoint(), 255, 255, 0, FALSE); // Yellow = focal point
- }
- OverheadTexts[OverheadTexts.Length] = DebugText;
- }
- }
- if( bShowAll || HUD.ShouldDisplayDebug('AIMovement') )
- {
- DebugText = "---------- AI MOVEMENT ----------\n";
- DebugText = DebugText$"Velocity: "$VSize(Velocity)$" X: "$Velocity.X$" Y: "$Velocity.Y$" Z: "$Velocity.Z$" UU/S, "$VSize(Velocity)/100$"\n";
- DebugText = DebugText$"Acceleration: "$VSize(Acceleration)$" X: "$Acceleration.X$" Y: "$Acceleration.Y$" Z: "$Acceleration.Z$" Physics: "$GetPhysicsName()$"\n";
- //DebugText = DebugText$"SuperSpeed: "$IsUsingSuperSpeed()$" LastLOSOrRelevantTime: "$`TimeSince(LastLOSOrRelevantTime)$" LastRenderTime: "$`TimeSince(Mesh.LastRenderTime)$"\n";
- DebugText = DebugText$"SuperSpeed: "$IsUsingSuperSpeed()$" LastLOSOrRelevantTime: "$`TimeSince(LastLOSOrRelevantTime)$" LastRenderTime: "$`TimeSince(LastRenderTime)$"\n";
- if( MyKFAIC != none )
- {
- if( MyKFAIC.bPreparingMove )
- {
- DebugText = DebugText@"bPreparingMove:"$MyKFAIC.bPreparingMove;
- }
- //DebugText = DebugText@"AICommand: "$MyKFAIC.GetActiveCommand();
- }
- OverheadTexts[OverheadTexts.Length] = DebugText;
- }
- }
- /**
- * Dialog
- **/
- /** Returns (hardcoded) dialog event ID for when players kills this zed type */
- function int GetKillerDialogID()
- {
- return 65;//KILL_Generic
- }
- /** Returns (hardcoded) dialog event ID for when players spots this zed type */
- function int GetSpotterDialogID()
- {
- return 125;//SPOTZ_Generic
- }
- function UpdateDeadHorseStreak( bool bStillActive)
- {
- if( bStillActive )
- {
- DeadHorseHitStreakAmt++;
- }
- else
- {
- DeadHorseHitStreakAmt = 1;
- }
- LastDeadHorseHitTime = WorldInfo.TimeSeconds;
- }
- /** Returns (hardcoded) trader advice dialog ID */
- static function int GetTraderAdviceID()
- {
- return -1;
- }
- /** Play sounds when jumping and landing */
- function PlayLeapedDialog();
- function PlayLandedDialog();
- function MotivatePlayerToAttack( float Percentage, class<DamageType> AntiGriefDamageTypeClass )
- {
- local PlayerController PC;
- PC = PlayerController(Controller);
- if( PC != none && WorldInfo.TimeSeconds - LastAttackHumanWarningTime > 9 )
- {
- PC.ReceiveLocalizedMessage( class'KFLocalMessage_Priority', GMT_AttackHumanPlayers );
- LastAttackHumanWarningTime = WorldInfo.TimeSeconds;
- }
- TakeDamage( int(float(HealthMax) * 0.05f), none, Location + vRand()*5.f, vRand(), AntiGriefDamageTypeClass );
- }
- /*********************************************************************************************
- * @name UI / Localization
- ********************************************************************************************* */
- /**Looks up and returns localized name */
- static function string GetLocalizedName()
- {
- local string MonsterName;
- //check if we have a seasonal variant
- MonsterName = Localize("Zeds", String(default.LocalizationKey) $ GetSeasonalLocalizationSuffix(), "KFGame");
- //Has a question mark in it, which would indicate failure. return default.
- if (InStr(MonsterName, "?") >= 0)
- {
- MonsterName = Localize("Zeds", String(default.LocalizationKey), "KFGame");
- }
- return MonsterName;
- }
- static function string GetSeasonalLocalizationSuffix()
- {
- //Remove any year information, just get 1s digit
- switch (class'KFGameEngine'.static.GetSeasonalEventID() % 10)
- {
- case SEI_Spring:
- return "_Spring";
- case SEI_Summer:
- return "_Summer";
- case SEI_Fall:
- return "_Fall";
- case SEI_Winter:
- return "_Winter";
- default:
- return "";
- }
- return "";
- }
- /*********************************************************************************************
- * @name Armor
- ********************************************************************************************* */
- function ZedExplodeArmor(int ArmorZoneIdx, name ArmorZoneName)
- {
- if (ArmorInfo != none)
- {
- ArmorInfo.ExplodeArmor(ArmorZoneIdx, ArmorZoneName);
- }
- }
- /*********************************************************************************************
- * @name Achievements
- ********************************************************************************************* */
- native protected function bool ShouldGrandOnDeathAchievement();
- native protected function int GetZedOnDeathAchievement();
- native function DisablebOnDeathAchivement();
- DefaultProperties
- {
- // ---------------------------------------------
- // AI / Navigation
- PeripheralVision=-1.f // 360
- Alertness=1.f
- SightRadius=16384.f
- ControllerClass=class'KFGame.KFAIController_Monster'
- PursuitSpeedScale=1.f
- PathSearchType=PST_Constraint
- AccelConvergeFalloffDistance=400.f
- bAllowAccelSmoothing=false
- MatchEnemySpeedAtDistance=200.f
- MinimumEnemySpeedToMatch=280.f
- bModifyReachSpecCost=true
- bDebug_DrawSprintingOverheadInfo=false
- LedgeCheckThreshold=350.0f
- bDebug_UseIconForShowingSprintingOverheadInfo=true
- CollisionRadiusForReducedZedOnZedPinchPointCollisionState=1
- Begin Object Name=KFPawnSkeletalMeshComponent
- WireframeColor=(R=255,G=255,B=0,A=255)
- bUseAsOccluder=TRUE
- End Object
- // ---------------------------------------------
- // Special Moves
- Begin Object Name=SpecialMoveHandler_0
- SpecialMoveClasses(SM_MeleeAttack) =class'KFGame.KFSM_MeleeAttack'
- SpecialMoveClasses(SM_MeleeAttackDoor) =class'KFSM_DoorMeleeAttack'
- SpecialMoveClasses(SM_GrappleAttack) =class'KFGame.KFSM_GrappleCombined'
- SpecialMoveClasses(SM_Stumble) =class'KFGame.KFSM_Stumble'
- SpecialMoveClasses(SM_RecoverFromRagdoll)=class'KFGame.KFSM_RecoverFromRagdoll'
- SpecialMoveClasses(SM_Knockdown) =class'KFSM_RagdollKnockdown'
- SpecialMoveClasses(SM_DeathAnim) =class'KFSM_DeathAnim'
- SpecialMoveClasses(SM_Stunned) =class'KFSM_Stunned'
- SpecialMoveClasses(SM_Frozen) =class'KFSM_Frozen'
- SpecialMoveClasses(SM_Taunt) =class'KFGame.KFSM_Zed_Taunt'
- SpecialMoveClasses(SM_WalkingTaunt) =class'KFGame.KFSM_Zed_WalkingTaunt'
- SpecialMoveClasses(SM_BossTheatrics) =class'KFGame.KFSM_Zed_Boss_Theatrics'
- End Object
- IncapSettings(AF_Poison)=(Cooldown=5.0, Duration=5.0,)
- IncapSettings(AF_Microwave)=(Cooldown=5.0, Duration=5.0,)
- IncapSettings(AF_Freeze)=(Cooldown=5.0)
- IncapSettings(AF_Snare)=(Cooldown=5.0, Duration=5.0,)
- // ---------------------------------------------
- // Movement / Physics
- bCanCrouch=false
- AccelRate=+02048.000000
- JumpZ=750.f
- HiddenGroundSpeed=600.f // same speed for all monsters
- bCanStrafe=true
- MaxJumpHeight=128.f
- MaxTurningRadius=64.f
- BumpDamageType=class'KFDT_NPCBump'
- BumpFrequency=0.5f
- bLimitFallAccel=TRUE
- SpeedAdjustTransitionRate=200.f
- InitialGroundSpeedModifier=1.0
- // ---------------------------------------------
- // Spawning
- MinSpawnSquadSizeType=EST_Small
- // ---------------------------------------------
- // Gameplay
- DifficultySettings=class'KFMonsterDifficultyInfo'
- Begin Object Class=KFMeleeHelperAI Name=MeleeHelper_0
- MaxHitRange=180.f
- BaseDamage=6.f
- End Object
- MeleeAttackHelper=MeleeHelper_0
- bHasExtraSprintJumpVelocity=false
- bCanRage=false
- bCanGrabAttack=false
- bCanMeleeAttack=true
- ReachedEnemyThresholdScale=1.f
- ZedBumpDamageScale=1.f
- HeadlessBleedOutTime=5.f
- ParryResistance=1
- DifficultyDamageMod=1.0
- GameResistancePct=1.f
- //NapalmCheckInterval=0.5f
- // Blocking
- MinBlockFOV=0.1f
- BlockSprintSpeedModifier=0.75f
- bIsStalkerClass=false
- bIsCrawlerClass=false
- bIsFleshpoundclass=false
- bIsClotClass=false
- bIsBloatClass=false
- bDisableGoreMeshWhileAlive=false
- DamageInflationPercent = 1.0
- IntendedDamageInflationPercent = 1.0
- DamageInflationRate=1.0
- RandomColorIdx=-1
- // ---------------------------------------------
- // Hit Zones
- HitZones.Empty
- HitZones.Add((ZoneName=head, BoneName=Head, Limb=BP_Head, GoreHealth=20, DmgScale=1.1, SkinID=1))
- HitZones.Add((ZoneName=neck, BoneName=Neck, Limb=BP_Head, GoreHealth=20))
- HitZones.Add((ZoneName=chest, BoneName=Spine2, Limb=BP_Torso, GoreHealth=150))
- HitZones.Add((ZoneName=heart, BoneName=Spine2, Limb=BP_Special, GoreHealth=150))
- HitZones.Add((ZoneName=lupperarm, BoneName=LeftArm, Limb=BP_LeftArm, GoreHealth=50))
- HitZones.Add((ZoneName=lforearm, BoneName=LeftForearm, Limb=BP_LeftArm, GoreHealth=15))
- HitZones.Add((ZoneName=lhand, BoneName=LeftForearm, Limb=BP_LeftArm, GoreHealth=20))
- HitZones.Add((ZoneName=rupperarm, BoneName=RightArm, Limb=BP_RightArm, GoreHealth=50))
- HitZones.Add((ZoneName=rforearm, BoneName=RightForearm, Limb=BP_RightArm, GoreHealth=15))
- HitZones.Add((ZoneName=rhand, BoneName=RightForearm, Limb=BP_RightArm, GoreHealth=20))
- HitZones.Add((ZoneName=stomach, BoneName=Spine1, Limb=BP_Torso, GoreHealth=150))
- HitZones.Add((ZoneName=abdomen, BoneName=Hips, Limb=BP_Torso, GoreHealth=150))
- HitZones.Add((ZoneName=lthigh, BoneName=LeftUpLeg, Limb=BP_LeftLeg, GoreHealth=75))
- HitZones.Add((ZoneName=lcalf, BoneName=LeftLeg, Limb=BP_LeftLeg, GoreHealth=25))
- HitZones.Add((ZoneName=lfoot, BoneName=LeftLeg, Limb=BP_LeftLeg, GoreHealth=15))
- HitZones.Add((ZoneName=rthigh, BoneName=RightUpLeg, Limb=BP_RightLeg, GoreHealth=75))
- HitZones.Add((ZoneName=rcalf, BoneName=RightLeg, Limb=BP_RightLeg, GoreHealth=25))
- HitZones.Add((ZoneName=rfoot, BoneName=RightLeg, Limb=BP_RightLeg, GoreHealth=15))
- // Pad the arrays so we can modify them with the live update system if needed
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- LiveDamageTypeModifiers.Add(())
- WeakSpotSocketNames.Add(FX_Dazed) // Head
- // List of BodySetups turned to phycsi for arm injury
- ArmPhysicsBoneList=("RightShoulder","RightArm","RightForeArm","RightHand")
- BleedIncapFX=ParticleSystem'FX_Gameplay_EMIT_THREE.FX_Incap_Bleed_01'
- // ---------------------------------------------
- // Animation
- bCanHeadTrack=true
- `if(`notdefined(ShippingPC))
- DebugRadarTexture=Texture2D'GP_Debug.SineWaveMarker_TEX';
- `endif
- OnDeathAchievementID = INDEX_NONE
- bKnockdownWhenJumpedOn = True
- // ---------------------------------------------
- // sounds
- Begin Object Class=AkComponent name=SprintAkComponent0
- BoneName=dummy
- bStopWhenOwnerDestroyed=true
- bForceOcclusionUpdateInterval=true
- OcclusionUpdateInterval=0.2f
- End Object
- SprintAkComponent=SprintAkComponent0
- Components.Add(SprintAkComponent0)
- Begin Object Class=AkComponent name=HeadshotAkComponent0
- BoneName=head
- bStopWhenOwnerDestroyed=false
- bForceOcclusionUpdateInterval=true
- OcclusionUpdateInterval=0.2f
- End Object
- HeadShotAkComponent=HeadshotAkComponent0
- Components.Add(HeadshotAkComponent0)
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement