//============================================================================= // KFPawn_ZedPatriarch //============================================================================= // Patriarch Boss Pawn Class //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC //============================================================================= class KFPawn_ZedPatriarch extends KFPawn_MonsterBoss; struct Patriarch_MortarTarget { var KFPawn TargetPawn; var vector TargetVelocity; }; /********************************************************************************************* * Content **********************************************************************************************/ /** Events for looping ambient breathing like Darth Vader */ var AkEvent AmbientBreathingEvent; var AkEvent LowHealthAmbientBreathingEvent; /** Gameplay-driven Ak objects, instanced at runtime */ var AkComponent CloakedAkComponent; var AkEvent CloakedLoop; var AkEvent CloakedLoopEnd; /** Materials used for cloaking/visible states */ var MaterialInstanceConstant BodyMaterial; var MaterialInstanceConstant BodyAltMaterial; var MaterialInstanceConstant SpottedMaterial; var MaterialInstanceConstant CloakedBodyMaterial; var MaterialInstanceConstant CloakedBodyAltMaterial; /** Skel control for spinning the minigun barrel */ var KFSkelControl_SpinBone BarrelSpinSkelCtrl; /** Skel control for gun arm tracking */ var SkelControlLookAt GunTrackingSkelCtrl; /** Healing syringes */ var array HealingSyringeMeshes; var array HealingSyringeMICs; var int CurrentSyringeMeshNum; var int ActiveSyringe; var float SyringeInjectTimeDuration; var float SyringeInjectTimeRemaining; /** Mech colors for each phase */ var LinearColor MechColors[4]; /** Mech color when dead */ var LinearColor DeadMechColor; /** Boil colors for each phase */ var LinearColor BoilColors[4]; /** Boil color when dead */ var LinearColor DeadBoilColor; /** Enables or disables boil and boil light pulsing */ var bool bPulseBoils; /** The rate at which the boils pulse */ var float BoilPulseRate; /** The current boil pulse brightness */ var float BoilPulseAccum; /** Light above the boils on the arm to cast around it */ var float BoilLightBrightness[4]; var name BoilLightSocketName; var transient PointLightComponent BoilLightComponent; /** Effect played upon cloaking/uncloaking */ var ParticleSystem CloakFX; var Name CloakFXSocketName; /** Cloak damage shimmer */ var float CloakShimmerAmount; var float LastCloakShimmerTime; /** Battle phase damage FX */ var name BattleDamageFXSocketName_LeftHip; var name BattleDamageFXSocketName_LeftKnee; var name BattleDamageFXSocketName_LeftFoot; var name BattleDamageFXSocketName_LeftArm; var name BattleDamageFXSocketName_Weapon; var name BattleDamageFXSocketName_LowerSpike; var name BattleDamageFXSocketName_UpperSpike; var name BattleDamageFXSocketName_BackSpike; var ParticleSystemComponent BattleDamagePSC_LeftHip; var ParticleSystemComponent BattleDamagePSC_LeftKnee; var ParticleSystemComponent BattleDamagePSC_LeftFoot; var ParticleSystemComponent BattleDamagePSC_LeftArm; var ParticleSystemComponent BattleDamagePSC_Weapon; var ParticleSystemComponent BattleDamagePSC_LowerSpike; var ParticleSystemComponent BattleDamagePSC_UpperSpike; var ParticleSystemComponent BattleDamagePSC_BackSpike; var ParticleSystem BattleDamageFX_Sparks_LowDmg; var ParticleSystem BattleDamageFX_Sparks_MidDmg; var ParticleSystem BattleDamageFX_Sparks_HighDmg; var ParticleSystem BattleDamageFX_Tentacle_LowDmg; var ParticleSystem BattleDamageFX_Tentacle_MidDmg; var ParticleSystem BattleDamageFX_Tentacle_HighDmg; var ParticleSystem BattleDamageFX_Smoke_HighDmg; /** Interval between dialog play attempts */ var float TickDialogInterval; /** Footstep camera shake */ var protected const float FootstepCameraShakePitchAmplitude; var protected const float FootstepCameraShakeRollAmplitude; /********************************************************************************************* * General Gameplay **********************************************************************************************/ /** Info for patriarch battle phases */ struct PatriarchBattlePhaseInfo { /** Whether or not to desire sprinting behavior this battle phase */ var bool bAllowedToSprint; /** How long to wait before each sprinting attack */ var float SprintCooldownTime; /** Whether or not we can do the tentacle grab attack this battle phase */ var bool bCanTentacleGrab; /** Cooldown time before the next attempt to do a tentacle grab */ var float TentacleGrabCooldownTime; /** Whether or not we can use our missile launcher attack this battle phase */ var bool bCanUseMissiles; /** Cooldown time before the next attempt to do a missile attack */ var float MissileAttackCooldownTime; /** Whether or not we can use our mortar attack this battle phase */ var bool bCanUseMortar; /** Cooldown time before the next attempt to do a mortar attack */ var float MortarAttackCooldownTime; /** Whether or not we can do a mortar barrage attack this battle phase */ var bool bCanDoMortarBarrage; /** Whether or not we can do a charge attack this battle phase */ var bool bCanChargeAttack; /** Cooldown time before the next attempt to do a charge attack */ var float ChargeAttackCooldownTime; /** Maximum number of attacks to do before ending rage mode */ var int MaxRageAttacks; /** Damage done by tentacle attack this battle phase */ var int TentacleDamage; /** How much time since the last successful melee attack before activating minigun this battle phase */ var float MinigunAttackCooldownTime; /** Whether or not we can summon minions this battle phase */ var bool bCanSummonMinions; /** Per-phase, per-difficulty flag that when set to TRUE allows the Patriarch to move with his minigun */ var array bCanMoveWhenMinigunning; /** Heal amount (MaxHealth x HealAmount) per difficulty level */ var array HealAmounts; structdefaultproperties { bAllowedToSprint=false } }; /** Previous battle phase, for turning FX on/off */ var int LastFXBattlePhase; /** Configuration for the Patriarch battle phases */ var array BattlePhases; /** Cooldown time before the next attempt to sprint */ var float SprintCooldownTime; /** Cooldown time before the next attempt to do a tentacle grab */ var float TentacleGrabCooldownTime; /** Cooldown time before the next attempt to do a missile attack */ var float MissileAttackCooldownTime; /** Cooldown time before the next attempt to do a mortar attack */ var float MortarAttackCooldownTime; /** Cooldown time before the next attempt to do a charge attack */ var float ChargeAttackCooldownTime; /** How much time since the last successful melee attack before activating minigun this battle phase */ var float MinigunAttackCooldownTime; /** Maximum number of attacks to do before ending rage mode */ var int MaxRageAttacks; /** Damage done by tentacle attack this battle phase */ var int TentacleDamage; /** Damagetype used for tentacle attacks */ var class TentacleDamageType; /** Damagetype used when bumping other zeds */ var class HeavyBumpDamageType; /** Used for the minigun's fan fire attack */ var bool bSprayingFire; /** Turns barrel spin skel controller on and off */ var bool bSpinBarrels; /** Minigun barrel spin rotation rate */ var float BarrelSpinSpeed; /** Allows gun tracking on the server if server aim precision is necessary (player-controlled, etc) */ var protected const bool bUseServerSideGunTracking; /** Turns gun tracking on and off */ var bool bGunTracking; /** The target to use for tracking */ var repnotify Actor GunTarget; /** The projectile class used for the missile attack */ var class MissileProjectileClass; /** The projectile class used for the mortar attack */ var class MortarProjectileClass; /** Targets chosen for mortar attack */ var array MortarTargets; /** Mortar distance values */ var float MinMortarRangeSQ; var float MaxMortarRangeSQ; /********************************************************************************************* * Flee and heal mode **********************************************************************************************/ /** Modifier to scale up sprint speed if fleeing */ var float FleeSprintSpeedModifier; /** Percent cloaked [0-1.0] */ var float CloakPercent; /** Cloak speeds */ var float CloakSpeed; var float DeCloakSpeed; /** Fleeing and attempting to heal */ var repnotify bool bInFleeAndHealMode; /** Number of times we've bumped into enemies when trying to heal */ var int NumFleeAndHealEnemyBumps; /** Last time we bumped into an enemy */ var float LastFleeAndHealEnemyBumpTime; /** Whether we've healed this battle phase or not */ var bool bHealedThisPhase; replication { if( bNetDirty ) bInFleeAndHealMode, GunTarget; } /** * Check on various replicated data and act accordingly. */ simulated event ReplicatedEvent( name VarName ) { switch( VarName ) { case nameOf(bIsCloakingSpottedByTeam): UpdateGameplayMICParams(); break; case nameOf(bIsCloaking): ClientCloakingStateUpdated(); break; case nameOf(GunTarget): SetGunTracking( GunTarget != none ); break; } Super.ReplicatedEvent( VarName ); } simulated event PostBeginPlay() { Super.PostBeginPlay(); // Give Patriarch his weapon AddDefaultInventory(); // Set weapon state to active if( Weapon != none ) { Weapon.GotoState('Active'); } // Set fire anim blend rates if( WeaponAttachment != none ) { WeaponAttachment.ShootBlendInTime = 0.f; WeaponAttachment.ShootBlendOutTime = 0.01f; } // PostBeginPlay is called before we do our first audio update, so we need to set a valid initial position so the ambient sound works right AmbientAkComponent.CachedObjectPosition = Location; SetPawnAmbientSound( AmbientBreathingEvent ); // Start the dialog timer if( WorldInfo.NetMode != NM_Client ) { SetTimer( 2.f, false, nameOf(Timer_TickPatriarchDialog) ); } } /** Overloaded to support loading the alternate body mic */ simulated function SetCharacterArch( KFCharacterInfoBase Info, optional bool bForce ) { local int i; local KFCharacterInfo_Monster MonsterInfo; super.SetCharacterArch( Info ); // Set our secondary material, attach our healing syringes if( WorldInfo.NetMode != NM_DedicatedServer && Mesh != None ) { // Attach healing syringes for( i = 0; i < 3; ++i ) { HealingSyringeMICs[i] = HealingSyringeMeshes[i].CreateAndSetMaterialInstanceConstant( 0 ); HealingSyringeMeshes[i].SetShadowParent( Mesh ); Mesh.AttachComponent( HealingSyringeMeshes[i], Name("SyringeAttach0"$i+1) ); } // Attach boil light Mesh.AttachComponentToSocket( BoilLightComponent, BoilLightSocketName ); UpdateBattlePhaseLights(); BoilLightComponent.SetEnabled( true ); MonsterInfo = KFCharacterInfo_Monster(Info); if (MonsterInfo != none) { if (MaterialInstanceConstant(MonsterInfo.Skins[0]) != none) { BodyMaterial = MaterialInstanceConstant(MonsterInfo.Skins[0]); } if (MaterialInstanceConstant(MonsterInfo.Skins[1]) != none) { BodyAltMaterial = MaterialInstanceConstant(MonsterInfo.Skins[1]); } } } } /** Cache skel controls */ simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp) { Super.PostInitAnimTree(SkelComp); if( WorldInfo.NetMode != NM_DedicatedServer ) { BarrelSpinSkelCtrl = KFSkelControl_SpinBone(SkelComp.FindSkelControl('BarrelSpin')); BarrelSpinSkelCtrl.SetSkelControlActive( false ); } if( WorldInfo.NetMode != NM_DedicatedServer || bUseServerSideGunTracking ) { GunTrackingSkelCtrl = SkelControlLookAt(SkelComp.FindSkelControl('GunTracking')); GunTrackingSkelCtrl.SetSkelControlActive( false ); } } /** Called from Possessed event when this controller has taken control of a Pawn */ function PossessedBy( Controller C, bool bVehicleTransition ) { Super.PossessedBy( C, bVehicleTransition ); SetPhaseCooldowns( 0 ); } /** Damagetype to use when bumping other zeds */ function class GetBumpAttackDamageType() { return HeavyBumpDamageType; } /** * Turn on or off flee and heal mode * Network: ALL */ simulated function SetFleeAndHealMode( bool bNewFleeAndHealStatus ) { bInFleeAndHealMode = bNewFleeAndHealStatus; if( Role == ROLE_Authority ) { if( bNewFleeAndHealStatus ) { SprintSpeed = default.SprintSpeed * FleeSprintSpeedModifier; SetTimer(0.25f, true, nameof(FleeAndHealBump)); } else { SprintSpeed = default.SprintSpeed; ClearTimer(nameof(FleeAndHealBump)); } // Initialize bump variables NumFleeAndHealEnemyBumps = 0; LastFleeAndHealEnemyBumpTime = WorldInfo.TimeSeconds; bForceNetUpdate = true; } if( !bNewFleeAndHealStatus ) { bHealedThisPhase = false; } } /** Summon some children */ function SummonChildren() { local KFAIWaveInfo MinionWave; local KFGameInfo MyKFGameInfo; MyKFGameInfo = KFGameInfo(WorldInfo.Game); // Force frustration mode on MyKFGameInfo.GetAIDirector().bForceFrustration = true; // Select the correct batch of zeds to spawn during this battle phase MinionWave = GetWaveInfo(CurrentBattlePhase, MyKFGameInfo.GetModifiedGameDifficulty()); if( MinionWave != none ) { if( MyKFGameInfo.SpawnManager != none ) { MyKFGameInfo.SpawnManager.LeftoverSpawnSquad.Length = 0; MyKFGameInfo.SpawnManager.SummonBossMinions( MinionWave.Squads, GetNumMinionsToSpawn() ); } } } /** Returns whether we are allowed to summon children or not */ function bool CanSummonChildren() { return BattlePhases[CurrentBattlePhase-1].bCanSummonMinions; } /** Heal animnotify to move the syringe */ simulated function ANIMNOTIFY_GrabSyringe() { if( WorldInfo.NetMode != NM_DedicatedServer ) { CurrentSyringeMeshNum = CurrentBattlePhase-2; Mesh.DetachComponent( HealingSyringeMeshes[CurrentSyringeMeshNum] ); Mesh.AttachComponent( HealingSyringeMeshes[CurrentSyringeMeshNum], 'Syringe' ); } } /** Syringe spawn notification */ simulated function ANIMNOTIFY_SpawnedKActor( KFKActorSpawnable NewKActor, AnimNodeSequence AnimSeqInstigator ) { local MaterialInstanceConstant SyringeMIC; // Make sure we have a valid current syringe if( CurrentSyringeMeshNum < 0 ) { return; } SyringeMIC = NewKActor.StaticMeshComponent.CreateAndSetMaterialInstanceConstant( 0 ); SyringeMIC.SetScalarParameterValue( 'Scalar_GlowIntensity', 0.02f + fClamp(0.98f*SyringeInjectTimeRemaining/SyringeInjectTimeDuration, 0.f, 0.98f) ); NewKActor.StaticMeshComponent.SetLightingChannels(HealingSyringeMeshes[CurrentSyringeMeshNum].LightingChannels); NewKActor.StaticMeshComponent.bCastDynamicShadow = false; // Remove the mesh from the hand and clear all references Mesh.DetachComponent( HealingSyringeMeshes[CurrentSyringeMeshNum] ); HealingSyringeMeshes[CurrentSyringeMeshNum] = none; HealingSyringeMICs[CurrentSyringeMeshNum] = none; CurrentSyringeMeshNum = -1; } /** Spawns a KActor and deletes the syringe mesh, called on death */ simulated function BreakOffSyringe( int SyringeNum ) { local KFKActorSpawnable NewKActor; local vector BoneLoc, LinearVel, AngularVel; local quat BoneQuat; local rotator BoneRot; local Name SyringeBoneName; SyringeBoneName = Name( "SyringeAttach0"$SyringeNum+1 ); BoneLoc = Mesh.GetBoneLocation( SyringeBoneName ); BoneQuat = Mesh.GetBoneQuaternion( SyringeBoneName ); BoneRot = QuatToRotator( BoneQuat ); NewKActor = Spawn( class'KFKActorSpawnable', self,, BoneLoc, BoneRot + HealingSyringeMeshes[SyringeNum].Rotation ); if ( NewKActor != None ) { NewKActor.StaticMeshComponent.SetStaticMesh( HealingSyringeMeshes[SyringeNum].StaticMesh ); NewKActor.LifeSpan = 30.f * fClamp( WorldInfo.DestructionLifetimeScale, 0.1f, 2.f ); // Set initial linear velocity LinearVel.X = RandRange( -300, -100 ); LinearVel.Y = RandRange( -300, -100 ); NewKActor.StaticMeshComponent.SetRBLinearVelocity( Velocity + QuatRotateVector(BoneQuat, LinearVel) ); // Set initial angular velocity AngularVel.X = RandRange( 3000, 6000 ); AngularVel.Y = RandRange( 3000, 6000 ); AngularVel.Z = RandRange( 3000, 6000 ); NewKActor.StaticMeshComponent.SetRBAngularVelocity( QuatRotateVector(BoneQuat, AngularVel) ); NewKActor.StaticMeshComponent.WakeRigidBody(); // Copy display settings from attachment mesh NewKActor.StaticMeshComponent.SetLightingChannels( HealingSyringeMeshes[SyringeNum].LightingChannels ); NewKActor.StaticMeshComponent.bCastDynamicShadow = true; NewKActor.StaticMeshComponent.bAllowPerObjectShadows = true; NewKActor.StaticMeshComponent.PerObjectShadowCullDistance = 4000; } // Remove the mesh from the hand and clear all references Mesh.DetachComponent( HealingSyringeMeshes[SyringeNum] ); HealingSyringeMeshes[SyringeNum] = none; HealingSyringeMICs[SyringeNum] = none; } /** If the Patriarch repeatedly bumps into players during his flee and heal phase, just heal */ simulated event Bump( Actor Other, PrimitiveComponent OtherComp, Vector HitNormal ) { local KFPawn KFP; super.Bump( Other, OtherComp, HitNormal ); if( Role == ROLE_Authority && bInFleeAndHealMode && MyKFAIC != none && !IsDoingSpecialMove() && Other.GetTeamNum() != GetTeamNum() ) { KFP = KFPawn( Other ); if( KFP != none ) { if( `TimeSince(LastFleeAndHealEnemyBumpTime) > 1.f ) { ++NumFleeAndHealEnemyBumps; LastFleeAndHealEnemyBumpTime = WorldInfo.TimeSeconds; // If we've bumped into players enough times, just force a heal if( NumFleeAndHealEnemyBumps > 2 ) { NumFleeAndHealEnemyBumps = 0; KFAIController_ZedPatriarch(MyKFAIC).ForceHeal(); } } } } } /** When fleeing, plow through any other zeds */ function FleeAndHealBump() { local KFPawn KFP; local vector ClosestPoint; local float ClosestDist; local KFAIController_ZedPatriarch KFAICP; if( MyKFAIC == none || MyKFAIC.Enemy == none || MyKFAIC.RouteGoal == none || IsDoingSpecialMove(SM_Heal) ) { return; } KFAICP = KFAIController_ZedPatriarch(MyKFAIC); foreach WorldInfo.AllPawns( class'KFPawn', KFP, Location, 300.f ) { // Heavy bump guys that are between us and our prey! if( KFP != self && KFP.IsAliveAndWell() ) { ClosestDist = PointDistToSegment( KFP.Location, Location, KFAICP.RouteGoal.Location, ClosestPoint ); if( ClosestDist < GetCollisionRadius() * 1.5 ) { KFAICP.DoHeavyBump( KFP, Normal(KFP.Location - Location) ); } } } } /** If true Patriarch will favor sprinting in this phase. Even if it's false he may sprint under certain circumstances, but when it's true he'll try and sprint almost all the time */ function bool DesireSprintingInThisPhase() { return BattlePhases[CurrentBattlePhase - 1].bAllowedToSprint; } /** Increment Patriarch to the next battle phase */ function IncrementBattlePhase() { CurrentBattlePhase++; bHealedThisPhase = true; SetPhaseCooldowns( CurrentBattlePhase - 1 ); OnBattlePhaseChanged(); bForceNetUpdate = true; } /** Sets various material scalars and FX based on the phase of battle */ simulated function OnBattlePhaseChanged() { if( WorldInfo.NetMode == NM_DedicatedServer || Health <= 0 ) { return; } super.OnBattlePhaseChanged(); UpdateBattlePhaseLights(); UpdateBattlePhaseMaterials(); UpdateBattlePhaseFX(); } /** Set the correct phase based cooldown for this battle phase */ function SetPhaseCooldowns( int BattlePhase ) { SprintCooldownTime = BattlePhases[BattlePhase].SprintCooldownTime; TentacleGrabCooldownTime = BattlePhases[BattlePhase].TentacleGrabCooldownTime; MinigunAttackCooldownTime = BattlePhases[BattlePhase].MinigunAttackCooldownTime; MissileAttackCooldownTime = BattlePhases[BattlePhase].MissileAttackCooldownTime; ChargeAttackCooldownTime = BattlePhases[BattlePhase].ChargeAttackCooldownTime; TentacleDamage = BattlePhases[BattlePhase].TentacleDamage; MaxRageAttacks = BattlePhases[BattlePhase].MaxRageAttacks; } /** Starts the weapon cooldown time */ function StartWeaponCooldown() { if( Controller != none && KFAIController_ZedPatriarch(Controller) != none ) { KFAIController_ZedPatriarch(Controller).LastSuccessfulAttackTime = WorldInfo.TimeSeconds; } } /** Used by AI to determine if we can charge attack this phase */ function bool CanChargeAttack() { return !bIsCloaking && BattlePhases[CurrentBattlePhase-1].bCanChargeAttack; } /** Used by AI to determine if we can tentacle grab this phase */ function bool CanTentacleGrab() { return BattlePhases[CurrentBattlePhase-1].bCanTentacleGrab; } /** Used by AI to determine if we can missile attack this phase */ function bool CanMissileAttack() { return BattlePhases[CurrentBattlePhase-1].bCanUseMissiles; } /** Used by AI to determine if we can mortar attack this phase */ function bool CanMortarAttack() { return BattlePhases[CurrentBattlePhase-1].bCanUseMortar; } /** Used by AI to determine if we can mortar barrage this phase */ function bool CanDoMortarBarrage() { return BattlePhases[CurrentBattlePhase-1].bCanDoMortarBarrage; } /** Only allow blocking when uncloaked/not fleeing */ function bool CanBlock() { local KFAIController_ZedPatriarch MyPatController; if( bIsCloaking || bInFleeAndHealMode || !super.CanBlock() ) { return false; } if( !IsHumanControlled() ) { MyPatController = KFAIController_ZedPatriarch( Controller ); if( MyPatController.bFleeing || MyPatController.bWantsToFlee ) { return false; } } return true; } /** Only allow movement with the minigun if any conditions are met */ simulated function bool CanMoveWhenMinigunning() { local KFGameReplicationInfo KFGRI; // See if this battle phase allows it KFGRI = KFGameReplicationInfo( WorldInfo.GRI ); if( KFGRI != none && BattlePhases[CurrentBattlePhase-1].bCanMoveWhenMinigunning[KFGRI.GetModifiedGameDifficulty()] ) { return true; } // Allow moving when there's only one player left return LocalIsOnePlayerLeftInTeamGame(); } /** Toggles barrel spinning on and off */ simulated function SpinMinigunBarrels( bool bEnableSpin ) { if( WorldInfo.NetMode != NM_DedicatedServer ) { bSpinBarrels = bEnableSpin; if( bEnableSpin && BarrelSpinSkelCtrl != none ) { BarrelSpinSkelCtrl.SetSkelControlActive( true ); } } } /** Toggles gun tracking on and off */ simulated function SetGunTracking( bool bEnableTracking ) { if( WorldInfo.NetMode != NM_DedicatedServer ) { bGunTracking = bEnableTracking; GunTrackingSkelCtrl.SetSkelControlActive( bEnableTracking ); } if( Role == ROLE_Authority ) { if( bEnableTracking && Controller != None && Controller.Enemy != none ) { GunTarget = Controller.Enemy; } else { GunTarget = none; } } bForceNetUpdate = true; } /** Overloaded to use the minigun muzzle location */ simulated event Vector GetWeaponStartTraceLocation(optional Weapon CurrentWeapon) { local vector SocketLoc; Mesh.GetSocketWorldLocationAndRotation( 'LeftMuzzleFlash', SocketLoc ); return SocketLoc; } /** Overloaded to support spray fire for minigun */ simulated function Rotator GetAdjustedAimFor( Weapon W, vector StartFireLoc ) { local vector SocketLoc, EndTrace; local rotator ActualAimRot, SocketRot; // If spraying, use the rotation of the muzzle flash bone if( bSprayingFire ) { Mesh.GetSocketWorldLocationAndRotation( 'LeftMuzzleFlash', SocketLoc, SocketRot ); return SocketRot; } ActualAimRot = super.GetAdjustedAimFor( W, StartFireLoc ); EndTrace = StartFireLoc + vector(ActualAimRot) * W.GetTraceRange(); Mesh.GetSocketWorldLocationAndRotation( 'LeftMuzzleFlash', SocketLoc, SocketRot ); // If the rotation of the barrel is not close enough to believably make the shot, use its rotation instead if( vector(SocketRot) dot Normal(EndTrace - StartFireLoc) < 0.96f ) { return SocketRot; } return ActualAimRot; } /** Retrieves the projectile class used for the missile attack. Called from SpecialMove */ function class GetMissileClass() { return MissileProjectileClass; } /** Retrieves the aim direction and target location for each missile. Called from SpecialMove */ function GetMissileAimDirAndTargetLoc( int MissileNum, vector MissileLoc, rotator MissileRot, out vector AimDir, out vector TargetLoc ) { local vector X,Y,Z; local int EnemyIndex; local KFAIController_ZedPatriarch MyPatController; local KFPawn EnemyPawn; MyPatController = KFAIController_ZedPatriarch( Controller ); if( MyPatController == none ) { return; } // Make sure we have an enemy! if( MyPatController.Enemy == none ) { MyPatController.ForceSetEnemy( MyPatController.GetClosestEnemy() ); } // Abort if still no enemy if( MyPatController.Enemy == none ) { EndSpecialMove(); return; } EnemyPawn = KFPawn( MyPatController.Enemy ); // If this enemy isn't visible, fire at its last known location if( !MyPatController.CanSee(EnemyPawn) ) { EnemyIndex = MyPatController.RecentlySeenEnemyList.Find( 'TrackedEnemy', EnemyPawn ); if( EnemyIndex != INDEX_NONE ) { TargetLoc = MyPatController.RecentlySeenEnemyList[EnemyIndex].LastVisibleLocation; } else { EnemyIndex = MyPatController.HiddenEnemies.Find( 'TrackedEnemy', EnemyPawn ); if( EnemyIndex != INDEX_NONE ) { TargetLoc = MyPatController.HiddenEnemies[EnemyIndex].LastVisibleLocation; } else { TargetLoc = EnemyPawn.Location; } } } else { TargetLoc = EnemyPawn.Location; } // Try to aim somewhat for the feet TargetLoc += (vect(0,0,-1) * (EnemyPawn.GetCollisionHeight() * 0.25f)); // If no LOS, aim higher if( !FastTrace(TargetLoc, MissileLoc,, true) ) { TargetLoc = EnemyPawn.Location + (vect(0,0,1) * EnemyPawn.BaseEyeHeight); } // Nudge the spread a tiny bit to make the missiles less concentrated on a single point GetAxes( MissileRot, X,Y,Z ); AimDir = Normal( (TargetLoc - MissileLoc) + (Z*6.f) ); } /** Retrieves the projectile class used for the mortar attack. Called from SpecialMove */ function class GetMortarClass() { return MortarProjectileClass; } /** Tries to set our mortar targets */ function bool CollectMortarTargets( optional bool bInitialTarget, optional bool bForceInitialTarget ) { local int NumTargets, i; local KFPawn KFP; local float TargetDistSQ; local vector MortarVelocity, MortarStartLoc, TargetLoc, TargetProjection; local KFAIController_ZedPatriarch MyPatController; MyPatController = KFAIController_ZedPatriarch( Controller ); MortarStartLoc = Location + vect(0,0,1)*GetCollisionHeight(); NumTargets = bInitialTarget ? 0 : 1; for( i = 0; i < MyPatController.HiddenEnemies.Length; ++i ) { KFP = MyPatController.HiddenEnemies[i].TrackedEnemy; if( !KFP.IsAliveAndWell() || MortarTargets.Find('TargetPawn', KFP) != INDEX_NONE ) { continue; } // Make sure target is in range TargetLoc = KFP.Location + (vect(0,0,-1)*(KFP.GetCollisionHeight()*0.8f)); TargetProjection = MortarStartLoc - TargetLoc; TargetDistSQ = VSizeSQ( TargetProjection ); if( TargetDistSQ > MinMortarRangeSQ && TargetDistSQ < MaxMortarRangeSQ ) { TargetLoc += Normal(TargetProjection)*KFP.GetCollisionRadius(); if( SuggestTossVelocity(MortarVelocity, TargetLoc, MortarStartLoc, MortarProjectileClass.default.Speed, 500.f, 1.f, vect(0,0,0),, GetGravityZ()*0.8f) ) { // Make sure upward arc path is clear if( !FastTrace(MortarStartLoc + (Normal(vect(0,0,1) + (Normal(TargetLoc - MortarStartLoc)*0.9f))*fMax(VSize(MortarVelocity)*0.55f, 800.f)), MortarStartLoc,, true) ) { continue; } MortarTargets.Insert( NumTargets, 1 ); MortarTargets[NumTargets].TargetPawn = KFP; MortarTargets[NumTargets].TargetVelocity = MortarVelocity; if( bInitialTarget || NumTargets == 2 ) { return true; } NumTargets++; } } } // Fall back on visible enemies if( (bForceInitialTarget || !bInitialTarget) && NumTargets < 2 && MyPatController.RecentlySeenEnemyList.Length > 0 ) { for( i = 0; i < MyPatController.RecentlySeenEnemyList.Length && NumTargets < 3; ++i ) { KFP = MyPatController.RecentlySeenEnemyList[i].TrackedEnemy; if( !KFP.IsAliveAndWell() || MortarTargets.Find('TargetPawn', KFP) != INDEX_NONE ) { continue; } // Make sure target is in range TargetLoc = KFP.Location + (vect(0,0,-1)*(KFP.GetCollisionHeight()*0.8f)); TargetProjection = MortarStartLoc - TargetLoc; TargetDistSQ = VSizeSQ( TargetProjection ); if( TargetDistSQ > MinMortarRangeSQ && TargetDistSQ < MaxMortarRangeSQ ) { TargetLoc += Normal(TargetProjection)*KFP.GetCollisionRadius(); if( SuggestTossVelocity(MortarVelocity, TargetLoc, MortarStartLoc, MortarProjectileClass.default.Speed, 500.f, 1.f, vect(0,0,0),, GetGravityZ()*0.8f) ) { // Make sure upward arc path is clear if( !FastTrace(MortarStartLoc + (Normal(vect(0,0,1) + (Normal(TargetLoc - MortarStartLoc)*0.9f))*fMax(VSize(MortarVelocity)*0.55f, 800.f)), MortarStartLoc,, true) ) { continue; } MortarTargets.Insert( NumTargets, 1 ); MortarTargets[NumTargets].TargetPawn = KFP; MortarTargets[NumTargets].TargetVelocity = MortarVelocity; if( bInitialTarget ) { return true; } NumTargets++; } } } } return false; } /** Allows pawn to do any pre-mortar attack prep */ function PreMortarAttack(); /** Clears mortar targets */ function ClearMortarTargets() { MortarTargets.Length = 0; } /** Returns the mortar target for the associated projectile number */ function Patriarch_MortarTarget GetMortarTarget( int MortarNum ) { if( MortarNum >= MortarTargets.Length ) { return MortarTargets[Rand(MortarTargets.Length)]; } return MortarTargets[MortarNum]; } /** Retrieves the aim direction and target location for each mortar. Called from SpecialMove */ function GetMortarAimDirAndTargetLoc( int MissileNum, vector MissileLoc, rotator MissileRot, out vector AimDir, out vector TargetLoc, out float MissileSpeed ) { local Patriarch_MortarTarget MissileTarget; local vector X,Y,Z; GetAxes( MissileRot, X,Y,Z ); // Each missile can possibly target a separate player MissileTarget = GetMortarTarget(MissileNum); // Aim at the feet TargetLoc = MissileTarget.TargetPawn.Location + (vect(0,0,-1)*MissileTarget.TargetPawn.GetCollisionHeight()); // Nudge the spread a tiny bit to make the missiles less concentrated on a single point AimDir = Normal( vect(0,0,1) + Normal(MissileTarget.TargetVelocity) ); // Set the missile speed MissileSpeed = VSize( MissileTarget.TargetVelocity ); } /** Update our barrel spin skel control */ simulated event Tick( float DeltaTime ) { local float MinCloakPct; local float Intensity, BoilPulseSin; local LinearColor ActualBoilColor; super.Tick( DeltaTime ); if( WorldInfo.NetMode != NM_DedicatedServer ) { // Spin the minigun barrels if( BarrelSpinSkelCtrl != none ) { if( bSpinBarrels ) { if( BarrelSpinSpeed < 300000.f ) { BarrelSpinSpeed = fMin(BarrelSpinSpeed + (DeltaTime * 200000.f), 500000.f); BarrelSpinSkelCtrl.RotationRate.Roll = BarrelSpinSpeed; } } else if( BarrelSpinSpeed > 0.f ) { BarrelSpinSpeed = fMax(BarrelSpinSpeed - (DeltaTime * 150000.f), 0.f); BarrelSpinSkelCtrl.RotationRate.Roll = BarrelSpinSpeed; if( BarrelSpinSpeed == 0.f ) { BarrelSpinSkelCtrl.SetSkelControlActive( false ); } } } // Update syringe material scalars if( ActiveSyringe > -1 && HealingSyringeMICs[ActiveSyringe] != none && SyringeInjectTimeRemaining > 0.f ) { SyringeInjectTimeRemaining -= DeltaTime; Intensity = fClamp( SyringeInjectTimeRemaining/SyringeInjectTimeDuration, 0.f, 1.f ); HealingSyringeMICs[ActiveSyringe].SetScalarParameterValue( 'Scalar_GlowIntensity', Intensity ); } else { ActiveSyringe = -1; } if( bPulseBoils && !bIsCloaking ) { if( BoilPulseAccum > 1.f ) { BoilPulseAccum = -1.f; } BoilPulseSin = Abs( BoilPulseAccum ); ActualBoilColor = BoilColors[3] * BoilPulseSin; CharacterMICs[1].SetVectorParameterValue( 'Vector_GlowColor', ActualBoilColor ); BoilPulseAccum += DeltaTime * BoilPulseRate; } else { BoilPulseSin = 1.f; } if( CharacterMICs[0].Parent != SpottedMaterial ) { MinCloakPct = GetMinCloakPct(); if( !bIsCloaking ) { if( CloakPercent < 1.0f ) { CloakPercent = fMin( CloakPercent + DeltaTime*DeCloakSpeed, 1.f ); if( CloakPercent == 1.0f ) { UpdateGameplayMICParams(); } else { CharacterMICs[0].SetScalarParameterValue( 'Transparency', CloakPercent ); CharacterMICs[1].SetScalarParameterValue( 'Transparency', CloakPercent ); } if( bPulseBoils ) { BoilLightComponent.SetLightProperties( (BoilLightBrightness[CurrentBattlePhase-1] * BoilPulseSin) * CloakPercent ); } UpdateHealingSyringeTransparency(); } else if( bPulseBoils ) { BoilLightComponent.SetLightProperties( BoilLightBrightness[CurrentBattlePhase-1] * BoilPulseSin ); } } else if( CloakPercent > MinCloakPct ) { CloakPercent = fMax( CloakPercent - DeltaTime*CloakSpeed, MinCloakPct ); CharacterMICs[0].SetScalarParameterValue( 'Transparency', CloakPercent ); CharacterMICs[1].SetScalarParameterValue( 'Transparency', CloakPercent ); if( BoilLightComponent.bEnabled ) { BoilLightComponent.SetLightProperties( BoilLightBrightness[CurrentBattlePhase-1] * CloakPercent ); } UpdateHealingSyringeTransparency(); if( CloakPercent == 0.f && BoilLightComponent.bEnabled ) { BoilLightComponent.SetEnabled( false ); Mesh.DetachComponent( BoilLightComponent ); } } } } // Update our gun tracking skeletal controller UpdateGunTrackingSkelCtrl( DeltaTime ); } /** Updates our gun tracking skeletal control */ simulated function UpdateGunTrackingSkelCtrl( float DeltaTime ) { // Track the player with the gun arm if( GunTrackingSkelCtrl != none ) { if( bGunTracking && GunTarget != None ) { GunTrackingSkelCtrl.DesiredTargetLocation = GunTarget.Location; GunTrackingSkelCtrl.InterpolateTargetLocation( DeltaTime ); } else { GunTrackingSkelCtrl.SetSkelControlActive( false ); } } } /** Gets the minimum cloaked amount based on the viewer */ simulated protected function float GetMinCloakPct() { return 0.f; } /** Updates healing syringe transparency based on cloak settings */ simulated function UpdateHealingSyringeTransparency() { // Avoid arrays since this is called from ::Tick() if( HealingSyringeMICs[0] != none ) { HealingSyringeMICs[0].SetScalarParameterValue( 'Transparency', CloakPercent ); } if( HealingSyringeMICs[1] != none ) { HealingSyringeMICs[1].SetScalarParameterValue( 'Transparency', CloakPercent ); } if( HealingSyringeMICs[2] != none ) { HealingSyringeMICs[2].SetScalarParameterValue( 'Transparency', CloakPercent ); } } /********************************************************************************************* * Cloaking **********************************************************************************************/ simulated event NotifyGoreMeshActive() { // Set our secondary MIC if( WorldInfo.NetMode != NM_DedicatedServer && Mesh != None ) { CharacterMICs[0] = Mesh.CreateAndSetMaterialInstanceConstant( 0 ); CharacterMICs[1] = Mesh.CreateAndSetMaterialInstanceConstant( 1 ); Super.NotifyGoreMeshActive(); } } /** Toggle cloaking material */ function SetCloaked(bool bNewCloaking) { if( bCanCloak && bNewCloaking != bIsCloaking ) { if( bNewCloaking && (IsImpaired() || IsIncapacitated()) ) { return; } if( MaxHeadChunkGoreWhileAlive == 0 && bIsCloaking != bNewCloaking && IsAliveAndWell() ) { `DialogManager.PlaySpotCloakDialog( self, bNewCloaking ); } bIsCloaking = bNewCloaking; // Initial spotted callout should be slightly delayed if( bIsCloaking ) { bIsCloakingSpottedByLP = false; bIsCloakingSpottedByTeam = false; LastSpottedStatusUpdate = WorldInfo.TimeSeconds - 0.2f; } if( WorldInfo.NetMode != NM_DedicatedServer ) { if( bIsCloaking || bIsCloakingSpottedByLP || bIsCloakingSpottedByTeam ) { UpdateGameplayMICParams(); } } super.SetCloaked( bNewCloaking ); } } /** * bIsCloaking replicated state changed * Network: Local and Remote Clients */ simulated function ClientCloakingStateUpdated() { if( bIsCloaking ) { ClearBloodDecals(); UpdateGameplayMICParams(); // Initial spotted callout should be slightly delayed bIsCloakingSpottedByLP = false; bIsCloakingSpottedByTeam = false; LastSpottedStatusUpdate = WorldInfo.TimeSeconds - 0.2f; } else if( bIsCloakingSpottedByLP || bIsCloakingSpottedByTeam ) { UpdateGameplayMICParams(); } } /** Interrupt cloaking when bEmpDisrupted is set */ function OnStackingAfflictionChanged(byte Id) { local KFAIController_ZedPatriarch MyPatController; Super.OnStackingAfflictionChanged(Id); if( Role == ROLE_Authority && IsAliveAndWell() ) { if ( Id == AF_EMP ) { if( !bInFleeAndHealMode && !IsHumanControlled() ) { // Interrupt charge and set it on full cooldown MyPatController = KFAIController_ZedPatriarch( Controller ); if( !MyPatController.bWantsToFlee && !MyPatController.bFleeing ) { MyPatController.bSprintUntilAttack = false; MyPatController.LastSprintTime = WorldInfo.TimeSeconds; } MyPatController.CachedChargeTarget = none; MyPatController.bWantsToCharge = false; MyPatController.LastChargeAttackTime = WorldInfo.TimeSeconds; } // Decloak if we're cloaking if( bIsCloaking ) { SetCloaked( false ); } } } } /** Called on server when pawn should do EMP Wandering */ function CausePanicWander() { local KFAIController_ZedPatriarch MyPatController; if( bInFleeAndHealMode ) { return; } // Don't allow panic wander when attempting to flee if( !IsHumanControlled() ) { MyPatController = KFAIController_ZedPatriarch( Controller ); if( MyPatController.bWantsToFlee || MyPatController.bFleeing ) { return; } } super.CausePanicWander(); } /** * Called every 0.5f seconds to check if a cloaked zed has been spotted * Network: All but dedicated server */ simulated event UpdateSpottedStatus() { local bool bOldSpottedByLP; local KFPlayerController LocalPC; local KFPerk LocalPerk; local float DistanceSq, Range; if( WorldInfo.NetMode == NM_DedicatedServer ) { return; } bOldSpottedByLP = bIsCloakingSpottedByLP; bIsCloakingSpottedByLP = false; LocalPC = KFPlayerController(GetALocalPlayerController()); if( LocalPC != none ) { LocalPerk = LocalPC.GetPerk(); } if ( LocalPC != none && LocalPC.Pawn != None && LocalPC.Pawn.IsAliveAndWell() && LocalPerk != none && LocalPerk.bCanSeeCloakedZeds && `TimeSince( LastRenderTime ) < 1.f ) { DistanceSq = VSizeSq(LocalPC.Pawn.Location - Location); Range = LocalPerk.GetCloakDetectionRange(); if ( DistanceSq < Square(Range) ) { bIsCloakingSpottedByLP = true; if ( LocalPerk.IsCallOutActive() ) { // Beware of server spam. This RPC is marked unreliable and UpdateSpottedStatus has it's own cooldown timer LocalPC.ServerCallOutPawnCloaking(self); } } } // If spotted by team already, there is no point in trying to update the MIC here if ( !bIsCloakingSpottedByTeam ) { if ( bIsCloakingSpottedByLP != bOldSpottedByLP ) { UpdateGameplayMICParams(); } } } /** notification from player with CallOut ability */ function CallOutCloaking( optional KFPlayerController CallOutController ) { bIsCloakingSpottedByTeam = true; UpdateGameplayMICParams(); SetTimer(2.f, false, nameof(CallOutCloakingExpired)); } /** Call-out cloaking ability has timed out */ function CallOutCloakingExpired() { bIsCloakingSpottedByTeam = false; UpdateGameplayMICParams(); } /** Handle cloaking materials */ simulated function UpdateGameplayMICParams() { local int i; local bool bIsSpotted; local bool bWasCloaked; super.UpdateGameplayMICParams(); // Cannot cloak after patriarch has been gored if( WorldInfo.NetMode != NM_DedicatedServer ) { // visible by local player or team (must go after ServerCallOutCloaking) bIsSpotted = ( bIsCloakingSpottedByLP || bIsCloakingSpottedByTeam ); if( (!bIsCloaking || IsImpaired()) && CharacterMICs[0].Parent != BodyMaterial ) { bWasCloaked = CharacterMICs[0].Parent == SpottedMaterial || CharacterMICs[0].Parent == CloakedBodyMaterial; CharacterMICs[0].SetParent( BodyMaterial ); CharacterMICs[1].SetParent( BodyAltMaterial ); for( i = 0; i < HealingSyringeMICs.Length; ++i ) { if( HealingSyringeMICs[i] != none ) { HealingSyringeMICs[i].SetParent( default.HealingSyringeMeshes[i].Materials[0] ); } } Mesh.AttachComponentToSocket( BoilLightComponent, BoilLightSocketName ); BoilLightComponent.SetEnabled( true ); Mesh.CastShadow = true; Mesh.SetPerObjectShadows( true ); // Needed to avoid effects occurring on gore mesh swap if( bWasCloaked ) { SetDamageFXActive( true ); PlayStealthSoundLoopEnd(); DoCloakFX(); } //Update PAC meshes for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; ++i) { if (ThirdPersonAttachments[i] != none) { ThirdPersonAttachments[i].SetHidden(false); } } } else if ( bIsCloaking && bIsSpotted && CharacterMICs[0].Parent != SpottedMaterial ) { CloakPercent = 1.0f; CharacterMICs[0].SetParent( SpottedMaterial ); CharacterMICs[1].SetParent( SpottedMaterial ); for( i = 0; i < HealingSyringeMICs.Length; ++i ) { if( HealingSyringeMICs[i] != none ) { HealingSyringeMICs[i].SetParent( SpottedMaterial ); } } Mesh.CastShadow = false; Mesh.SetPerObjectShadows( false ); SetDamageFXActive( false ); //Update PAC meshes for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; ++i) { if (ThirdPersonAttachments[i] != none) { ThirdPersonAttachments[i].SetHidden(true); } } } else if( bIsCloaking && !bIsSpotted && CharacterMICs[0].Parent != CloakedBodyMaterial ) { CharacterMICs[0].SetParent( CloakedBodyMaterial ); CharacterMICs[1].SetParent( CloakedBodyAltMaterial ); for( i = 0; i < HealingSyringeMICs.Length; ++i ) { if( HealingSyringeMICs[i] != none ) { HealingSyringeMICs[i].SetParent( CloakedBodyAltMaterial ); } } PlayStealthSoundLoop(); DoCloakFX(); Mesh.CastShadow = false; Mesh.SetPerObjectShadows( false ); SetDamageFXActive( false ); //Update PAC meshes for (i = 0; i < `MAX_COSMETIC_ATTACHMENTS; ++i) { if (ThirdPersonAttachments[i] != none) { ThirdPersonAttachments[i].SetHidden(true); } } } } } simulated function DoCloakFX() { local ParticleSystemComponent CloakPSC; CloakPSC = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( CloakFX, Mesh, CloakFXSocketName, true ); CloakPSC.SetAbsolute( false, true, false ); } /** Updates battle damage on material instance based on battle phase */ simulated function UpdateBattlePhaseMaterials() { // No adjustments to spotted materials if( CharacterMICs[0] == none || CharacterMICs[1] == none || bIsCloakingSpottedByLP || bIsCloakingSpottedByTeam ) { return; } switch( CurrentBattlePhase ) { case 1: CharacterMICs[0].SetScalarParameterValue( 'Scalar_BattleGrime', 0.f ); CharacterMICs[0].SetScalarParameterValue( 'Scalar_GlowFlashing', 0.f ); CharacterMICs[0].SetVectorParameterValue( 'Vector_GlowColor', MechColors[0] ); CharacterMICs[1].SetScalarParameterValue( 'Scalar_BattleGrime', 0.f ); CharacterMICs[1].SetScalarParameterValue( 'Scalar_Damage_Blood_Contrast', 0.f ); CharacterMICs[1].SetScalarParameterValue( 'Scalar_GlowFlashing', 0.f ); CharacterMICs[1].SetVectorParameterValue( 'Vector_GlowColor', BoilColors[0] ); break; case 2: CharacterMICs[0].SetScalarParameterValue( 'Scalar_BattleGrime', 0.3f ); CharacterMICs[0].SetScalarParameterValue( 'Scalar_GlowFlashing', 0.25f ); CharacterMICs[0].SetVectorParameterValue( 'Vector_GlowColor', MechColors[1] ); CharacterMICs[1].SetScalarParameterValue( 'Scalar_BattleGrime', 0.25f ); CharacterMICs[1].SetScalarParameterValue( 'Scalar_Damage_Blood_Contrast', 1.f ); CharacterMICs[1].SetScalarParameterValue( 'Scalar_GlowFlashing', 0.f ); CharacterMICs[1].SetVectorParameterValue( 'Vector_GlowColor', BoilColors[1] ); break; case 3: CharacterMICs[0].SetScalarParameterValue( 'Scalar_BattleGrime', 0.7f ); CharacterMICs[0].SetScalarParameterValue( 'Scalar_GlowFlashing', 0.5f ); CharacterMICs[0].SetVectorParameterValue( 'Vector_GlowColor', MechColors[2] ); CharacterMICs[1].SetScalarParameterValue( 'Scalar_BattleGrime', 0.5f ); CharacterMICs[1].SetScalarParameterValue( 'Scalar_Damage_Blood_Contrast', 1.2f ); CharacterMICs[1].SetScalarParameterValue( 'Scalar_GlowFlashing', 0.f ); CharacterMICs[1].SetVectorParameterValue( 'Vector_GlowColor', BoilColors[2] ); break; case 4: CharacterMICs[0].SetScalarParameterValue( 'Scalar_BattleGrime', 1.1f ); CharacterMICs[0].SetScalarParameterValue( 'Scalar_GlowFlashing', 0.75f ); CharacterMICs[0].SetVectorParameterValue( 'Vector_GlowColor', MechColors[3] ); CharacterMICs[1].SetScalarParameterValue( 'Scalar_BattleGrime', 0.75f ); CharacterMICs[1].SetScalarParameterValue( 'Scalar_Damage_Blood_Contrast', 1.3f ); CharacterMICs[1].SetScalarParameterValue( 'Scalar_GlowFlashing', 0.f ); CharacterMICs[1].SetVectorParameterValue( 'Vector_GlowColor', BoilColors[3] ); bPulseBoils = true; break; }; } /** Updates dynamic lights based on battle phase */ simulated function UpdateBattlePhaseLights() { local LinearColor LinearBoilColor; local color BoilColor; local byte BattlePhaseArrayNum; if( Health <= 0 ) { return; } BattlePhaseArrayNum = CurrentBattlePhase-1; LinearBoilColor = BoilColors[BattlePhaseArrayNum] * 0.6f; BoilColor.R = 255.f * LinearBoilColor.R; BoilColor.G = 255.f * LinearBoilColor.G; BoilColor.B = 255.f * LinearBoilColor.B; BoilColor.A = 255; BoilLightComponent.SetLightProperties( BoilLightBrightness[BattlePhaseArrayNum], BoilColor ); } /** Updates battle damage emitters based on battle phase */ simulated function UpdateBattlePhaseFX() { if( Health <= 0 || WorldInfo.MyEmitterPool == none || CurrentBattlePhase == 1 || LastFXBattlePhase == CurrentBattlePhase ) { return; } switch( CurrentBattlePhase ) { case 2: BattleDamagePSC_LeftFoot = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Sparks_LowDmg, Mesh, BattleDamageFXSocketName_LeftFoot, true ); BattleDamagePSC_LeftArm = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Sparks_MidDmg, Mesh, BattleDamageFXSocketName_LeftArm, true ); BattleDamagePSC_UpperSpike = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Tentacle_LowDmg, Mesh, BattleDamageFXSocketName_UpperSpike, true ); break; case 3: DetachEmitter( BattleDamagePSC_LeftFoot ); DetachEmitter( BattleDamagePSC_LeftArm ); DetachEmitter( BattleDamagePSC_UpperSpike ); BattleDamagePSC_LeftFoot = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Sparks_HighDmg, Mesh, BattleDamageFXSocketName_LeftFoot, true ); BattleDamagePSC_LeftHip = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Sparks_HighDmg, Mesh, BattleDamageFXSocketName_LeftHip, true ); BattleDamagePSC_LeftArm = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Sparks_MidDmg, Mesh, BattleDamageFXSocketName_LeftArm, true ); BattleDamagePSC_Weapon = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Sparks_LowDmg, Mesh, BattleDamageFXSocketName_Weapon, true ); BattleDamagePSC_UpperSpike = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Tentacle_MidDmg, Mesh, BattleDamageFXSocketName_UpperSpike, true ); BattleDamagePSC_BackSpike = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Tentacle_MidDmg, Mesh, BattleDamageFXSocketName_BackSpike, true ); break; case 4: DetachEmitter( BattleDamagePSC_Weapon ); DetachEmitter( BattleDamagePSC_LeftArm ); DetachEmitter( BattleDamagePSC_UpperSpike ); DetachEmitter( BattleDamagePSC_BackSpike ); BattleDamagePSC_LeftKnee = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Sparks_HighDmg, Mesh, BattleDamageFXSocketName_LeftKnee, true ); BattleDamagePSC_LeftArm = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Smoke_HighDmg, Mesh, BattleDamageFXSocketName_LeftArm, true ); BattleDamagePSC_Weapon = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Smoke_HighDmg, Mesh, BattleDamageFXSocketName_Weapon, true ); BattleDamagePSC_LowerSpike = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Tentacle_MidDmg, Mesh, BattleDamageFXSocketName_LowerSpike, true ); BattleDamagePSC_UpperSpike = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Tentacle_HighDmg, Mesh, BattleDamageFXSocketName_UpperSpike, true ); BattleDamagePSC_BackSpike = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment( BattleDamageFX_Tentacle_MidDmg, Mesh, BattleDamageFXSocketName_BackSpike, true ); break; } if( bIsCloaking ) { SetDamageFXActive( false ); } LastFXBattlePhase = CurrentBattlePhase; } /** Disables or enables battle phase FX components */ simulated function SetDamageFXActive( bool bEnable ) { // Just turn off particle systems upon death, let them self-destroy if( Health <= 0 ) { if( BattleDamagePSC_LeftFoot != none ) { BattleDamagePSC_LeftFoot.SetActive( false ); } if( BattleDamagePSC_LeftKnee != none ) { BattleDamagePSC_LeftKnee.SetActive( false ); } if( BattleDamagePSC_LeftHip != none ) { BattleDamagePSC_LeftHip.SetActive( false ); } if( BattleDamagePSC_LeftArm != none ) { BattleDamagePSC_LeftArm.SetActive( false ); } if( BattleDamagePSC_Weapon != none ) { BattleDamagePSC_Weapon.SetActive( false ); } if( BattleDamagePSC_LowerSpike != none ) { BattleDamagePSC_LowerSpike.SetActive( false ); } if( BattleDamagePSC_UpperSpike != none ) { BattleDamagePSC_UpperSpike.SetActive( false ); } if( BattleDamagePSC_BackSpike != none ) { BattleDamagePSC_BackSpike.SetActive( false ); } return; } if( BattleDamagePSC_LeftFoot != none ) { BattleDamagePSC_LeftFoot.SetHidden( !bEnable ); } if( BattleDamagePSC_LeftKnee != none ) { BattleDamagePSC_LeftKnee.SetHidden( !bEnable ); } if( BattleDamagePSC_LeftHip != none ) { BattleDamagePSC_LeftHip.SetHidden( !bEnable ); } if( BattleDamagePSC_LeftArm != none ) { BattleDamagePSC_LeftArm.SetHidden( !bEnable ); } if( BattleDamagePSC_Weapon != none ) { BattleDamagePSC_Weapon.SetHidden( !bEnable ); } if( BattleDamagePSC_LowerSpike != none ) { BattleDamagePSC_LowerSpike.SetHidden( !bEnable ); } if( BattleDamagePSC_UpperSpike != none ) { BattleDamagePSC_UpperSpike.SetHidden( !bEnable ); } if( BattleDamagePSC_BackSpike != none ) { BattleDamagePSC_BackSpike.SetHidden( !bEnable ); } } /** Overridden to cause shimmer when taking damage */ simulated function PlayTakeHitEffects( vector HitDirection, vector HitLocation, optional bool bUseHitImpulse = true ) { super.PlayTakeHitEffects( HitDirection, HitLocation, bUseHitImpulse ); if( !bIsCloaking || CharacterMICs[0].Parent == SpottedMaterial || CloakPercent > CloakShimmerAmount || `TimeSince( LastCloakShimmerTime ) < 0.1f ) { return; } LastCloakShimmerTime = WorldInfo.TimeSeconds; CloakPercent = fClamp( CloakPercent + CloakShimmerAmount, 0.f, 0.8f ); } /** Allows us to spawn a custom cloaked impact effect */ simulated function KFSkinTypeEffects GetHitZoneSkinTypeEffects( int HitZoneIdx ) { if( bIsCloaking ) { return CharacterArch.ImpactSkins[4]; // 4 = Patriarch_Cloaked } return super.GetHitZoneSkinTypeEffects( HitZoneIdx ); } /** Overridden so that patty doesn't attempt dismemberment while he's alive */ function bool CanInjureHitZone(class DamageType, int HitZoneIdx) { local class KFDmgType; local name HitZoneName; if ( bPlayedDeath ) { KFDmgType = class(DamageType); HitZoneName = HitZones[HitZoneIdx].ZoneName; if ( KFDmgType != none && KFDmgType.static.CanDismemberHitZone( HitZoneName ) ) { return true; } } return false; } /* PlayDying() is called on server/standalone game when killed and also on net client when pawn gets bTearOff set to true (and bPlayedDeath is false) */ simulated function PlayDying( class DamageType, vector HitLoc ) { local int i; // Stop barrel spinning, gun tracking bSpinBarrels = false; SetGunTracking( false ); // Uncloak on death SetCloaked( false ); bCanCloak = false; // Break off syringes, if we have any still attached if( WorldInfo.NetMode != NM_DedicatedServer && CurrentBattlePhase < 4 ) { for( i = CurrentBattlePhase - 1; i < HealingSyringeMeshes.Length; i++ ) { // If patty is holding a syringe, the notify will pop and toss it if( i == CurrentSyringeMeshNum ) { continue; } // Ignore if mesh has already been detached if( HealingSyringeMeshes[i] == none ) { continue; } BreakOffSyringe( i ); } } Super.PlayDying(DamageType, HitLoc); // Empty mortar targets array ClearMortarTargets(); if( WorldInfo.NetMode != NM_DedicatedServer ) { SetDamageFXActive( false ); } // Hide the syringe bone Mesh.HideBoneByName( 'Syringe', PBO_None ); } /** Clean up function to terminate any effects on death */ simulated function TerminateEffectsOnDeath() { // Stop pulsing boils bPulseBoils = false; // Turn off damage FX if( CharacterMICs.Length > 0 && CharacterMICs[0] != none ) { CharacterMICs[0].SetVectorParameterValue( 'Vector_GlowColor', DeadMechColor ); CharacterMICs[0].SetScalarParameterValue( 'Scalar_GlowFlashing', 0.f ); } if( CharacterMICs.Length > 1 && CharacterMICs[1] != none ) { CharacterMICs[1].SetVectorParameterValue( 'Vector_GlowColor', DeadBoilColor ); CharacterMICs[1].SetScalarParameterValue( 'Scalar_GlowFlashing', 0.f ); } BoilLightComponent.SetEnabled( false ); DetachComponent( BoilLightComponent ); PlayStealthSoundLoopEnd(); super.TerminateEffectsOnDeath(); } /** Disable cloaking when crippled/headless */ function CauseHeadTrauma( float BleedOutTime=5.f ) { Super.CauseHeadTrauma( BleedOutTime ); if( bIsHeadless && IsAliveAndWell() && !IsDoingSpecialMove() ) { SetCloaked( false ); } } /********************************************************************************************* * Audio **********************************************************************************************/ /** Overridden to cause slight camera shakes when walking. */ simulated event PlayFootStepSound(int FootDown) { if( WorldInfo.NetMode != NM_DedicatedServer ) { if( IsHumanControlled() && IsLocallyControlled() ) { FootstepCameraShake.RotOscillation.Pitch.Amplitude = 0; FootstepCameraShake.RotOscillation.Roll.Amplitude = 0; } else { FootstepCameraShake.RotOscillation.Pitch.Amplitude = FootstepCameraShakePitchAmplitude; FootstepCameraShake.RotOscillation.Roll.Amplitude = FootstepCameraShakeRollAmplitude; FootstepCameraShakeInnerRadius = default.FootstepCameraShakeInnerRadius; FootstepCameraShakeOuterRadius = default.FootstepCameraShakeOuterRadius; if( !bIsSprinting || VSizeSQ(Velocity) < 10000.f ) { FootstepCameraShake.RotOscillation.Pitch.Amplitude *= 0.75f; FootstepCameraShake.RotOscillation.Roll.Amplitude *= 0.75f; } } } super.PlayFootStepSound( FootDown ); } /** Overloaded to take care of victory state */ simulated function SetWeaponAmbientSound(AkEvent NewAmbientSound, optional AkEvent FirstPersonAmbientSound) { if( Controller != none && Controller.IsInState('ZedVictory') ) { NewAmbientSound = none; FirstPersonAmbientSound = none; } super.SetWeaponAmbientSound( NewAmbientSound, FirstPersonAmbientSound ); } /** Returns (hardcoded) dialog event ID for when players spots this zed type */ function int GetSpotterDialogID() { if( bIsCloaking && MaxHeadChunkGoreWhileAlive == 0 ) { return 135;//SPOTZ_Cloaked } return 125;//SPOTZ_Generic } simulated function PlayStealthSoundLoop() { if( WorldInfo.NetMode != NM_DedicatedServer && !CloakedAkComponent.IsPlaying(CloakedLoop) ) { CloakedAkComponent.PlayEvent( CloakedLoop, true, true ); } } simulated function PlayStealthSoundLoopEnd() { if( WorldInfo.NetMode != NM_DedicatedServer && CloakedAkComponent.IsPlaying(CloakedLoop) ) { CloakedAkComponent.PlayEvent( CloakedLoopEnd, true, true ); } } function PlayMinigunWarnDialog() { `DialogManager.PlayPattyMinigunWarnDialog( self ); } function PlayMinigunAttackDialog() { `DialogManager.PlayPattyMinigunAttackDialog( self ); } function PlayGrabbedPlayerDialog( KFPawn_Human Target ) { `DialogManager.PlayPattyTentaclePullDialog( self ); `DialogManager.PlayPlayerGrabbedByPatriarchDialog( Target ); } /** Players dialog such as taunts at regular intervals */ function Timer_TickPatriarchDialog() { if( !IsAliveAndWell() ) { return; } if( !IsDoingSpecialMove() ) { `DialogManager.PlayPatriarchTickDialog( self ); } SetTimer( TickDialogInterval, false, nameOf(Timer_TickPatriarchDialog) ); } /** Play music for this boss (overridden for each boss) */ function PlayBossMusic() { if( KFGameInfo(WorldInfo.Game) != none ) { KFGameInfo(WorldInfo.Game).ForcePatriarchMusicTrack(); } } defaultproperties { LocalizationKey=KFPawn_ZedPatriarch // --------------------------------------------- // Stats XPValues(0)=1291 XPValues(1)=1694 XPValues(2)=1790 XPValues(3)=1843 // --------------------------------------------- // Content MonsterArchPath="ZED_ARCH.ZED_Patriarch_Archetype" PawnAnimInfo=KFPawnAnimInfo'ZED_Patriarch_ANIM.Patriarch_AnimGroup' CloakedBodyMaterial=MaterialInstanceConstant'ZED_Patriarch_MAT.ZED_Patriarch_Mech_Cloak_M' CloakedBodyAltMaterial=MaterialInstanceConstant'ZED_Patriarch_MAT.ZED_Patriarch_Cloak_M' SpottedMaterial=MaterialInstanceConstant'ZED_Stalker_MAT.ZED_Stalker_Visible_MAT' BodyMaterial=MaterialInstanceConstant'ZED_Patriarch_MAT.ZED_Patriarch_Mech_M' BodyAltMaterial=MaterialInstanceConstant'ZED_Patriarch_MAT.ZED_Patriarch_M' HeavyBumpDamageType=class'KFGameContent.KFDT_HeavyZedBump' TentacleDamageType=class'KFDT_Slashing_PatTentacle' DifficultySettings=class'KFDifficulty_Patriarch' // FX CloakFX=ParticleSystem'ZED_Patriarch_EMIT.FX_Patriarch_Cloaking_01' CloakFXSocketName=CloakFXSocket BattleDamageFX_Sparks_LowDmg=ParticleSystem'ZED_Patriarch_EMIT.FX_Pat_Sparks_LowD_01' BattleDamageFX_Sparks_MidDmg=ParticleSystem'ZED_Patriarch_EMIT.FX_Pat_sparks_MidD_01' BattleDamageFX_Sparks_HighDmg=ParticleSystem'ZED_Patriarch_EMIT.FX_Pat_Sparks_HighD_01' BattleDamageFX_Tentacle_LowDmg=ParticleSystem'ZED_Patriarch_EMIT.FX_Patriarch_tentacle_LowD_01' BattleDamageFX_Tentacle_MidDmg=ParticleSystem'ZED_Patriarch_EMIT.FX_Patriarch_tentacle_MidD_01' BattleDamageFX_Tentacle_HighDmg=ParticleSystem'ZED_Patriarch_EMIT.FX_Patriarch_tentacle_HighD_01' BattleDamageFX_Smoke_HighDmg=ParticleSystem'ZED_Patriarch_EMIT.FX_Pat_smoke_HighD_01' BattleDamageFXSocketName_LeftHip=FX_LeftHip BattleDamageFXSocketName_LeftKnee=FX_LeftKnee BattleDamageFXSocketName_LeftFoot=FX_LeftFoot BattleDamageFXSocketName_LeftArm=FX_LeftArm BattleDamageFXSocketName_Weapon=MissileCenter BattleDamageFXSocketName_LowerSpike=FX_Right_Arm_Spike BattleDamageFXSocketName_UpperSpike=FX_Upper_Back_Spike BattleDamageFXSocketName_BackSpike=FX_Back_Spike MechColors[0]=(R=0.f,G=0.f,B=0.f) MechColors[1]=(R=0.19,G=0.12f,B=0.f) MechColors[2]=(R=0.48f,G=0.076f,B=0.f) MechColors[3]=(R=0.79f,G=0.f,B=0.f) DeadMechColor=(R=0.05,G=0.f,B=0.f) BoilColors[0]=(R=0.f,G=0.28f,B=0.09f) BoilColors[1]=(R=0.72,G=0.73f,B=0.25f) BoilColors[2]=(R=0.54f,G=0.079f,B=0.f) BoilColors[3]=(R=0.85f,G=0.f,B=0.003f) DeadBoilColor=(R=0.05,G=0.f,B=0.f) BoilLightBrightness[0]=2.6f BoilLightBrightness[1]=2.7f BoilLightBrightness[2]=2.8f BoilLightBrightness[3]=2.9f BoilPulseRate=2.5f BoilPulseAccum=0.f Begin Object Class=PointLightComponent Name=BoilLightComponent0 FalloffExponent=10.f Brightness=2.f Radius=190.f LightColor=(R=50,G=200,B=50,A=255) CastShadows=false bCastPerObjectShadows=false bEnabled=false LightingChannels=(Indoor=true,Outdoor=true,bInitialized=true) End Object BoilLightComponent=BoilLightComponent0 BoilLightSocketName=BoilLightSocket Begin Object Name=KFPawnSkeletalMeshComponent // Enabling kinematic for physics interaction while alive. (see also MinDistFactorForKinematicUpdate) bUpdateKinematicBonesFromAnimation=true End Object Begin Object Class=StaticMeshComponent Name=KFSyringeStaticMeshComponent1 StaticMesh=StaticMesh'ZED_Patriarch_MESH.CHR_Patriarch_Syringe' Materials(0)=MaterialInstanceConstant'ZED_Patriarch_MAT.ZED_Patriarch_M' Rotation=(Pitch=16384) BlockRigidBody=false CastShadow=true bUseOnePassLightingOnTranslucency=true bCastDynamicShadow=true bAllowPerObjectShadows=true PerObjectShadowCullDistance=4000 //40m `if(`__TW_PER_OBJECT_SHADOW_BATCHING_) bAllowPerObjectShadowBatching=true `endif CollideActors=false AlwaysLoadOnClient=true AlwaysLoadOnServer=false MaxDrawDistance=4000 bAcceptsDynamicDecals=FALSE // Default to outdoor. If indoor, this will be set when TWIndoorLightingVolume::Touch() event is received at spawn. LightingChannels=(Outdoor=TRUE,bInitialized=TRUE) End Object HealingSyringeMeshes.Add(KFSyringeStaticMeshComponent1) Begin Object Class=StaticMeshComponent Name=KFSyringeStaticMeshComponent2 StaticMesh=StaticMesh'ZED_Patriarch_MESH.CHR_Patriarch_Syringe' Materials(0)=MaterialInstanceConstant'ZED_Patriarch_MAT.ZED_Patriarch_M' Rotation=(Pitch=16384) BlockRigidBody=false CastShadow=true bUseOnePassLightingOnTranslucency=true bCastDynamicShadow=true bAllowPerObjectShadows=true PerObjectShadowCullDistance=4000 //40m `if(`__TW_PER_OBJECT_SHADOW_BATCHING_) bAllowPerObjectShadowBatching=true `endif CollideActors=false AlwaysLoadOnClient=true AlwaysLoadOnServer=false MaxDrawDistance=4000 bAcceptsDynamicDecals=FALSE // Default to outdoor. If indoor, this will be set when TWIndoorLightingVolume::Touch() event is received at spawn. LightingChannels=(Outdoor=TRUE,bInitialized=TRUE) End Object HealingSyringeMeshes.Add(KFSyringeStaticMeshComponent2) Begin Object Class=StaticMeshComponent Name=KFSyringeStaticMeshComponent3 StaticMesh=StaticMesh'ZED_Patriarch_MESH.CHR_Patriarch_Syringe' Materials(0)=MaterialInstanceConstant'ZED_Patriarch_MAT.ZED_Patriarch_M' Rotation=(Pitch=16384) BlockRigidBody=false CastShadow=true bUseOnePassLightingOnTranslucency=true bCastDynamicShadow=true bAllowPerObjectShadows=true PerObjectShadowCullDistance=4000 //40m `if(`__TW_PER_OBJECT_SHADOW_BATCHING_) bAllowPerObjectShadowBatching=true `endif CollideActors=false AlwaysLoadOnClient=true AlwaysLoadOnServer=false MaxDrawDistance=4000 bAcceptsDynamicDecals=FALSE // Default to outdoor. If indoor, this will be set when TWIndoorLightingVolume::Touch() event is received at spawn. LightingChannels=(Outdoor=TRUE,bInitialized=TRUE) End Object HealingSyringeMeshes.Add(KFSyringeStaticMeshComponent3) // --------------------------------------------- // Audio CloakedLoop=AkEvent'WW_ZED_Patriarch.Play_Patriarch_Cloak' CloakedLoopEnd=AkEvent'WW_ZED_Patriarch.Stop_Patriarch_Cloak' Begin Object Class=AkComponent name=CloakedAkComponent0 BoneName=dummy bStopWhenOwnerDestroyed=true bForceOcclusionUpdateInterval=true OcclusionUpdateInterval=0.2f End Object CloakedAkComponent=CloakedAkComponent0 Components.Add( CloakedAkComponent0 ) // --------------------------------------------- // Effects Begin Object Class=CameraShake Name=FootstepCameraShake0 bSingleInstance=true OscillationDuration=0.25f RotOscillation={(Pitch=(Amplitude=120.f,Frequency=60.f), Roll=(Amplitude=60.f,Frequency=40.f))} End Object FootstepCameraShake=FootstepCameraShake0 FootstepCameraShakePitchAmplitude=120.f FootstepCameraShakeRollAmplitude=60.f FootstepCameraShakeInnerRadius=200 FootstepCameraShakeOuterRadius=1000 // --------------------------------------------- // Special Moves Begin Object Name=SpecialMoveHandler_0 SpecialMoveClasses(SM_Taunt)=class'KFSM_Patriarch_Taunt' SpecialMoveClasses(SM_Heal)=class'KFSM_Patriarch_Heal' SpecialMoveClasses(SM_HoseWeaponAttack)=class'KFSM_Patriarch_MinigunBarrage' SpecialMoveClasses(SM_GrappleAttack)=class'KFSM_Patriarch_Grapple' SpecialMoveClasses(SM_StandAndShootAttack)=class'KFSM_Patriarch_MissileAttack' SpecialMoveClasses(SM_SonicAttack)=class'KFSM_Patriarch_MortarAttack' SpecialMoveClasses(SM_Block)=class'KFSM_Block' End Object // for reference: Vulnerability=(default, head, legs, arms, special) IncapSettings(AF_Stun)= (Vulnerability=(0.1, 0.55, 0.1, 0.1, 0.55), Cooldown=17.0, Duration=1.25) //1.0 // 0.5, 0.55, 0.5, 0.4, 0.55 IncapSettings(AF_Knockdown)=(Vulnerability=(0.1, 0.4, 0.1, 0.1, 0.25), Cooldown=20.0) // 0.2, 0.2, 0.4, 0.2, 0.25 IncapSettings(AF_Stumble)= (Vulnerability=(0.1, 0.3, 0.1, 0.1, 0.4), Cooldown=10.0) // 0.2, 0.2, 0.2, 0.2, 0.4 IncapSettings(AF_GunHit)= (Vulnerability=(0.1, 0.1, 0.1, 0.1, 0.5), Cooldown=1.7) // 0.1, 0.1, 0.1, 0.1, 0.5 IncapSettings(AF_MeleeHit)= (Vulnerability=(0.1, 0.95, 0.1, 0.1, 0.75), Cooldown=2.0) //1.0 IncapSettings(AF_Poison)= (Vulnerability=(0)) IncapSettings(AF_Microwave)=(Vulnerability=(0.08), Cooldown=10.0, Duration=3.0) IncapSettings(AF_FirePanic)=(Vulnerability=(0.65), Cooldown=15.0, Duration=1.2) IncapSettings(AF_EMP)= (Vulnerability=(0.95), Cooldown=10.0, Duration=2.2) IncapSettings(AF_Freeze)= (Vulnerability=(0.5), Cooldown=10.0, Duration=1.0) IncapSettings(AF_Snare)= (Vulnerability=(1.0, 2.0, 1.0, 1.0, 2.0), Cooldown=10.5, Duration=3.0) IncapSettings(AF_Bleed)= (Vulnerability=(0.15), Cooldown=10.0) IncapSettings(AF_Shrink)= (Vulnerability=(1.0)) ShrinkEffectModifier = 0.15f Begin Object Class=Name=Afflictions_0 FireFullyCharredDuration=50.f FireCharPercentThreshhold=0.35f AfflictionClasses(AF_EMP)=class'KFAffliction_EMPDisrupt' AfflictionClasses(AF_FirePanic)=class'KFAffliction_Fire_Patriarch' End Object MissileProjectileClass=class'KFProj_Missile_Patriarch' MortarProjectileClass=class'KFProj_Mortar_Patriarch' MinMortarRangeSQ=160000.f MaxMortarRangeSQ=6000000.f ParryResistance=4 // --------------------------------------------- // Gameplay bLargeZed=true bCanCloak=true bCanGrabAttack=true bEnableAimOffset=true bUseServerSideGunTracking=true ActiveSyringe=-1 CurrentSyringeMeshNum=-1 SyringeInjectTimeDuration=0.16f Begin Object Name=MeleeHelper_0 BaseDamage=55.f MaxHitRange=375.f MomentumTransfer=40000.f MyDamageType=class'KFDT_Bludgeon_Patriarch' End Object Health=3750 DoshValue=500 Mass=400.f CloakPercent=1.0f DeCloakSpeed=4.5f CloakSpeed=3.f CloakShimmerAmount=0.6f FleeSprintSpeedModifier=1.25f // Resistant damage types DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Submachinegun', DamageScale=(0.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_AssaultRifle', DamageScale=(0.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Shotgun', DamageScale=(0.4))) //0.75 DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Handgun', DamageScale=(0.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Rifle', DamageScale=(0.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_Slashing', DamageScale=(1.0))) //0.5 DamageTypeModifiers.Add((DamageType=class'KFDT_Bludgeon', DamageScale=(1.0))) //0.5 DamageTypeModifiers.Add((DamageType=class'KFDT_Fire', DamageScale=(0.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_Microwave', DamageScale=(0.9))) //0.5 //1.0 DamageTypeModifiers.Add((DamageType=class'KFDT_Explosive', DamageScale=(0.4))) //0.6 0.5 DamageTypeModifiers.Add((DamageType=class'KFDT_Piercing', DamageScale=(0.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic', DamageScale=(0.05))) //special case DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_MicrowaveRifle', DamageScale=(0.7))) DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic_HRGHealthrower', DamageScale=(0.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRGTeslauncher', DamageScale=(0.6))) DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Minigun', DamageScale=(0.65))) DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic_MineReconstructorExplosion', DamageScale=(0.6))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_Beam', DamageScale=(1.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGeneratorSphereImpact', DamageScale=(2))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_DefaultFiremodeZapDamage', DamageScale=(1.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_AltFiremodeZapDamage', DamageScale=(1.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_Shrink_ShrinkRayGun', DamageScale=(3.0))) DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRGScorcherLightingImpact', DamageScale=(0.4))) DamageTypeModifiers.Add((DamageType=class'KFDT_Fire_HRGScorcherDoT', DamageScale=(0.4))) //DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRG_Vampire_BloodBallImpact', DamageScale=(0.3))) //DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRG_Vampire_BloodBallHeavyImpact', DamageScale=(0.3))) //DamageTypeModifiers.Add((DamageType=class'KFDT_Piercing_HRG_Vampire_CrystalSpike', DamageScale=(0.25))) //DamageTypeModifiers.Add((DamageType=class'KFDT_Bleeding_HRG_Vampire_BloodSuck', DamageScale=(0.5))) DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRG_Vampire_BloodBallImpact', DamageScale=(0.4))) DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRG_Vampire_BloodBallHeavyImpact', DamageScale=(0.4))) DamageTypeModifiers.Add((DamageType=class'KFDT_Piercing_HRG_Vampire_CrystalSpike', DamageScale=(0.3))) DamageTypeModifiers.Add((DamageType=class'KFDT_Bleeding_HRG_Vampire_BloodSuck', DamageScale=(0.7))) // --------------------------------------------- // Block Settings MinBlockFOV=0.1f // Penetration PenetrationResistance=4.0 // Custom Hit Zones (HeadHealth, SkinTypes, etc...) HeadlessBleedOutTime=6.f HitZones[HZI_HEAD]=(ZoneName=head, BoneName=head, Limb=BP_Head, GoreHealth=MaxInt, DmgScale=1.0, SkinID=1) //1 HitZones[1] =(ZoneName=jaw, BoneName=Jaw, Limb=BP_Head, GoreHealth=MaxInt, DmgScale=0.1, SkinID=1) //1 HitZones[2] =(ZoneName=chest, BoneName=Spine1, Limb=BP_Torso, GoreHealth=150, DmgScale=0.8, SkinID=1) //0.8 HitZones[3] =(ZoneName=stomach, BoneName=Spine, Limb=BP_Torso, GoreHealth=MaxInt, DmgScale=0.8, SkinID=1)) HitZones[4] =(ZoneName=lupperarm, BoneName=LeftArm, Limb=BP_LeftArm, GoreHealth=50, DmgScale=0.1, SkinID=2) //0.33 HitZones[5] =(ZoneName=lforearm, BoneName=LeftForearm, Limb=BP_LeftArm, GoreHealth=20, DmgScale=0.1, SkinID=2) //0.33 HitZones[7] =(ZoneName=rupperarm, BoneName=RightArm, Limb=BP_RightArm, GoreHealth=50, DmgScale=1.3, SkinID=3) HitZones[8] =(ZoneName=rforearm, BoneName=RightForearm, Limb=BP_RightArm, GoreHealth=20, DmgScale=1.0, SkinID=3) HitZones[9] =(ZoneName=rhand, BoneName=RightHand, Limb=BP_RightArm, GoreHealth=10, DmgScale=0.5, SkinID=1) //0.5 HitZones[10] =(ZoneName=stomach, BoneName=Spine, Limb=BP_Torso, GoreHealth=MaxInt, DmgScale=0.8, SkinID=1) //0.8 HitZones[12] =(ZoneName=lthigh, BoneName=LeftUpLeg, Limb=BP_LeftLeg, GoreHealth=100, DmgScale=0.1, SkinID=2) //0.33 HitZones[13] =(ZoneName=lcalf, BoneName=LeftLeg, Limb=BP_LeftLeg, GoreHealth=MaxInt, DmgScale=0.1, SkinID=2) //0.33 HitZones[14] =(ZoneName=lfoot, BoneName=LeftFoot, Limb=BP_LeftLeg, GoreHealth=10, DmgScale=0.1, SkinID=2) //0.33 HitZones[15] =(ZoneName=rthigh, BoneName=RightUpLeg, Limb=BP_RightLeg, GoreHealth=75, DmgScale=0.8, SkinID=1) HitZones[16] =(ZoneName=rcalf, BoneName=RightLeg, Limb=BP_RightLeg, GoreHealth=25, DmgScale=0.8, SkinID=1) // Tentacle gore HitZones.Add((ZoneName=heart, BoneName=FrontTentacle2, Limb=BP_Special, GoreHealth=50, DmgScale=1.3, SkinID=3)) HitZones.Add((ZoneName=heart, BoneName=FrontTentacle3, Limb=BP_Special, GoreHealth=50, DmgScale=1.3, SkinID=3)) HitZones.Add((ZoneName=heart, BoneName=FrontTentacle4, Limb=BP_Special, GoreHealth=50, DmgScale=1.3, SkinID=3)) HitZones.Add((ZoneName=heart, BoneName=FrontTentacle5, Limb=BP_Special, GoreHealth=50, DmgScale=1.3, SkinID=3)) HitZones.Add((ZoneName=heart, BoneName=FrontTentacle6, Limb=BP_Special, GoreHealth=50, DmgScale=1.3, SkinID=3)) HitZones.Add((ZoneName=heart, BoneName=FrontTentacle7, Limb=BP_Special, GoreHealth=50, DmgScale=1.5, SkinID=3)) WeakSpotSocketNames.Empty() // Ignore head WeakSpotSocketNames.Add(FX_Right_Arm_Spike) // Right arm WeakSpotSocketNames.Add(FX_Front_Spike) // Tentacle // --------------------------------------------- // Movement / Physics Begin Object Name=CollisionCylinder CollisionRadius=+0055.000000 End Object RotationRate=(Pitch=50000,Yaw=50000,Roll=50000) GroundSpeed=260.f SprintSpeed=650.f ReachedEnemyThresholdScale=1.f KnockdownImpulseScale=1.0f // --------------------------------------------- // AI / Navigation ControllerClass=class'KFGameContent.KFAIController_ZedPatriarch' BumpDamageType=class'KFDT_NPCBump_Large' DamageRecoveryTimeHeavy=0.65f DamageRecoveryTimeMedium=0.85f //0.09f DefaultInventory(0)=class'KFWeap_Minigun_Patriarch' // Summon squads by difficulty SummonWaves(0)=(PhaseOneWave=KFAIWaveInfo'GP_Spawning_ARCH.Special.Pat_Minions_Normal_One',PhaseTwoWave=KFAIWaveInfo'GP_Spawning_ARCH.Special.Pat_Minions_Normal_Two',PhaseThreeWave=KFAIWaveInfo'GP_Spawning_ARCH.Special.Pat_Minions_Normal_Three') SummonWaves(1)=(PhaseOneWave=KFAIWaveInfo'GP_Spawning_ARCH.Special.Pat_Minions_Hard_One',PhaseTwoWave=KFAIWaveInfo'GP_Spawning_ARCH.Special.Pat_Minions_Hard_Two',PhaseThreeWave=KFAIWaveInfo'GP_Spawning_ARCH.Special.Pat_Minions_Hard_Three') SummonWaves(2)=(PhaseOneWave=KFAIWaveInfo'GP_Spawning_ARCH.Special.Pat_Minions_Suicidal_One',PhaseTwoWave=KFAIWaveInfo'GP_Spawning_ARCH.Special.Pat_Minions_Suicidal_Two',PhaseThreeWave=KFAIWaveInfo'GP_Spawning_ARCH.Special.Pat_Minions_Suicidal_Three') SummonWaves(3)=(PhaseOneWave=KFAIWaveInfo'GP_Spawning_ARCH.Special.Pat_Minions_HOE_One',PhaseTwoWave=KFAIWaveInfo'GP_Spawning_ARCH.Special.Pat_Minions_HOE_Two',PhaseThreeWave=KFAIWaveInfo'GP_Spawning_ARCH.Special.Pat_Minions_HOE_Three') NumMinionsToSpawn=(X=6, Y=10) BattlePhases(0)={(bAllowedToSprint=true, SprintCooldownTime=3.f, bCanTentacleGrab=false, bCanUseMissiles=true, MissileAttackCooldownTime=10.f, bCanUseMortar=false, bCanChargeAttack=false, bCanChargeAttack=true, ChargeAttackCooldownTime=14.f, MinigunAttackCooldownTime=2.25f, bCanMoveWhenMinigunning={(false, false, false, false)}, // Normal,Hard,Suicidal,HoE HealAmounts={(0.75f, 0.85f, 0.95f, 0.99f)}, // Normal,Hard,Suicidal,HoE bCanSummonMinions=true)} BattlePhases(1)={(bAllowedToSprint=true, SprintCooldownTime=2.5f, bCanTentacleGrab=true, TentacleGrabCooldownTime=10.f, TentacleDamage=10, bCanUseMissiles=true, MissileAttackCooldownTime=8.f, bCanUseMortar=true, MortarAttackCooldownTime=10.f, bCanChargeAttack=true, ChargeAttackCooldownTime=10.f, MinigunAttackCooldownTime=2.0f, bCanMoveWhenMinigunning={(false, false, false, true)}, // Normal,Hard,Suicidal,HoE HealAmounts={(0.65f, 0.75f, 0.85f, 0.95f)}, // Normal,Hard,Suicidal,HoE MaxRageAttacks=4, bCanSummonMinions=true)} BattlePhases(2)={(bAllowedToSprint=true, SprintCooldownTime=2.f, bCanTentacleGrab=true, TentacleGrabCooldownTime=9.f, TentacleDamage=10, bCanUseMissiles=true, MissileAttackCooldownTime=7.f, bCanUseMortar=true, MortarAttackCooldownTime=9.f, bCanDoMortarBarrage=true, bCanChargeAttack=true, ChargeAttackCooldownTime=9.f, MinigunAttackCooldownTime=1.75f, bCanMoveWhenMinigunning={(false, false, true, true)}, // Normal,Hard,Suicidal,HoE HealAmounts={(0.55f, 0.65f, 0.75f, 0.85f)}, // Normal,Hard,Suicidal,HoE MaxRageAttacks=5, bCanSummonMinions=true)} BattlePhases(3)={(bAllowedToSprint=true, SprintCooldownTime=1.5f, bCanTentacleGrab=true, TentacleGrabCooldownTime=7.f, TentacleDamage=10, bCanUseMissiles=true, MissileAttackCooldownTime=5.f, bCanUseMortar=true, MortarAttackCooldownTime=7.f, bCanDoMortarBarrage=true, bCanChargeAttack=true, ChargeAttackCooldownTime=7.f, MinigunAttackCooldownTime=1.25f, bCanMoveWhenMinigunning={(false, true, true, true)}, // Normal,Hard,Suicidal,HoE MaxRageAttacks=6, bCanSummonMinions=false)} // --------------------------------------------- // Spawning MinSpawnSquadSizeType=EST_Boss LastFXBattlePhase=1 CurrentBattlePhase=1 TickDialogInterval=0.5f OnDeathAchievementID=KFACHID_QuickOnTheTrigger // Only used in Volter Castle for now when the spawn volume has bForceUseMapReplacePawn set to true // If we need to reuse it more we'll have to connect map to zed here (here defaults to this same class) MapReplacePawnClass.Add(class'KFPawn_ZedPatriarch') }