1
0
KF2-Dev-Scripts/KFGameContent/Classes/KFPawn_ZedPatriarch.uc

2347 lines
80 KiB
Ucode
Raw Permalink Normal View History

2020-12-13 15:01:13 +00:00
//=============================================================================
// 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<StaticMeshComponent> HealingSyringeMeshes;
var array<MaterialInstanceConstant> 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<bool> bCanMoveWhenMinigunning;
/** Heal amount (MaxHealth x HealAmount) per difficulty level */
var array<float> HealAmounts;
structdefaultproperties
{
bAllowedToSprint=false
}
};
/** Previous battle phase, for turning FX on/off */
var int LastFXBattlePhase;
/** Configuration for the Patriarch battle phases */
var array<PatriarchBattlePhaseInfo> 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<KFDamageType> TentacleDamageType;
/** Damagetype used when bumping other zeds */
var class<KFDamageType> 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<KFProj_Missile_Patriarch> MissileProjectileClass;
/** The projectile class used for the mortar attack */
var class<KFProj_Missile_Patriarch> MortarProjectileClass;
/** Targets chosen for mortar attack */
var array<Patriarch_MortarTarget> 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<KFDamageType> 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<KFProj_Missile_Patriarch> 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<KFProj_Missile_Patriarch> 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> DamageType, int HitZoneIdx)
{
local class<KFDamageType> KFDmgType;
local name HitZoneName;
if ( bPlayedDeath )
{
KFDmgType = class<KFDamageType>(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> 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)
2022-05-11 15:13:25 +00:00
IncapSettings(AF_Shrink)= (Vulnerability=(1.0))
ShrinkEffectModifier = 0.15f
2020-12-13 15:01:13 +00:00
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)))
2022-05-11 15:13:25 +00:00
DamageTypeModifiers.Add((DamageType=class'KFDT_Shrink_ShrinkRayGun', DamageScale=(3.0)))
2020-12-13 15:01:13 +00:00
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRGScorcherLightingImpact', DamageScale=(0.4)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Fire_HRGScorcherDoT', DamageScale=(0.4)))
2020-12-13 15:09:05 +00:00
//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)))
2020-12-13 15:01:13 +00:00
// ---------------------------------------------
// 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
2023-09-21 19:31:11 +00:00
// 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')
2020-12-13 15:01:13 +00:00
}