1
0
KF2-Dev-Scripts/KFGameContent/Classes/KFPawn_ZedBloatKing.uc
2020-12-13 18:01:13 +03:00

777 lines
25 KiB
Ucode

//=============================================================================
// KFPawn_ZedBloatKing
//=============================================================================
// Boss king variant of the bloat.
//=============================================================================
// Killing Floor 2
// Copyright (C) 2017 Tripwire Interactive LLC
//=============================================================================
class KFPawn_ZedBloatKing extends KFPawn_ZedBloat
implements(KFInterface_MonsterBoss);
//Localization
var localized array<string> BossCaptionStrings;
//Intro Camera
var bool bUseAnimatedCamera;
var vector AnimatedBossCameraOffset;
//List of things that are getting pulled, 0-5 players, 6-10 zeds
var KFPawn PullVictims[11];
//Explosion template for fart attack
var protected const KFGameExplosion FartExplosionTemplate;
var const ParticleSystem FartFXTemplate;
var const name FartFXSocket;
var repnotify byte FartFXNotify;
//Note: Fart attack happens on a timer without any special move
// It uses a base timer, then a variance to +/- it a bit at random
var Vector2D BaseFartAttackTimer;
var Vector2D VarianceFartAttackTimer;
var Vector2D RageFartAttackTimer;
var Vector2D RageVarianceFartAttackTimer;
var array<Vector2D> DifficultyFartAttackTimers;
var array<Vector2D> DifficultyVarianceFartTimers;
var array<Vector2D> DifficultyRageFartTimers;
var array<Vector2D> DifficultyVarianceRageFartTimers;
//Trigger used to track pawns that are within range of the KB
var KFTrigger_BloatKingGorge GorgeTrigger;
//Poop Monster spawn offset
var const float PoopMonsterOffset;
var ParticleSystem PoopMonsterFXTemplate;
var AkEvent PoopMonsterSFXTemplate;
var const name PoopMonsterFXSocket;
var repnotify byte PoopMonsterFXNotify;
//Delayed spawn tracking of poop monsters
var const float PoopMonsterSpawnDelay;
var int CurrentDelayedSpawns;
//Sprint speed specific to enrage
var const float RageSprintSpeedMultiplier;
//Health thresholds for triggering enrage
var array<float> EnrageHealthThresholds;
/** Footstep camera shake */
var protected const float FootstepCameraShakePitchAmplitude;
var protected const float FootstepCameraShakeRollAmplitude;
replication
{
if (Role == ROLE_Authority)
PullVictims, FartFXNotify, PoopMonsterFXNotify;
}
simulated event ReplicatedEvent(name VarName)
{
if (VarName == 'bIsEnraged')
{
SetEnraged(bIsEnraged);
}
else if (VarName == 'FartFXNotify')
{
PlayFartSpawnFX();
}
else if (VarName == 'PoopMonsterFXNotify')
{
PlayPoopSpawnFX();
}
else
{
super.ReplicatedEvent(VarName);
}
}
//-----------------------------------------------------------------------------
// Boss interface
//-----------------------------------------------------------------------------
simulated function KFPawn_Monster GetMonsterPawn()
{
return self;
}
simulated function string GetRandomBossCaption()
{
if (default.BossCaptionStrings.Length <= 0)
{
return "";
}
return default.BossCaptionStrings[Rand(default.BossCaptionStrings.Length)];
}
static simulated event bool IsABoss()
{
return true;
}
simulated function float GetHealthPercent()
{
return float(Health) / float(HealthMax);
}
/** Turn on the boss camera animation mode */
simulated function SetAnimatedBossCamera(bool bEnable, optional vector CameraOffset)
{
bUseAnimatedCamera = bEnable;
if (bUseAnimatedCamera)
{
AnimatedBossCameraOffset = CameraOffset;
}
else
{
AnimatedBossCameraOffset = vect(0, 0, 0);
}
}
/** Whether this pawn is in theatric camera mode */
simulated function bool UseAnimatedBossCamera()
{
return bUseAnimatedCamera;
}
/** The name of the socket to use as a camera base for theatric sequences */
simulated function name GetBossCameraSocket()
{
return 'TheatricCameraRootSocket';
}
/** The relative offset to use for the cinematic camera */
simulated function vector GetBossCameraOffset()
{
return AnimatedBossCameraOffset;
}
function OnZedDied(Controller Killer)
{
super.OnZedDied(Killer);
KFGameInfo(WorldInfo.Game).BossDied(Killer);
ClearFartTimer();
ClearTimer('AllowNextPoopMonster');
}
function KFAIWaveInfo GetWaveInfo(int BattlePhase, int Difficulty)
{
//@TODO: King Bloat boss wave functionality
return none;
}
function byte GetNumMinionsToSpawn()
{
//@TODO: King Bloat boss wave functionality
return 0;
}
//-----------------------------------------------------------------------------
// Pawn
//-----------------------------------------------------------------------------
function PossessedBy(Controller C, bool bVehicleTransition)
{
Super.PossessedBy(C, bVehicleTransition);
if(DifficultyFartAttackTimers.length > 0)
{
BaseFartAttackTimer = DifficultyFartAttackTimers[Min(WorldInfo.Game.GetModifiedGameDifficulty(), DifficultyFartAttackTimers.length)];
}
if (DifficultyVarianceFartTimers.length > 0)
{
VarianceFartAttackTimer = DifficultyVarianceFartTimers[Min(WorldInfo.Game.GetModifiedGameDifficulty(), DifficultyVarianceFartTimers.length)];
}
if (DifficultyRageFartTimers.length > 0)
{
RageFartAttackTimer = DifficultyRageFartTimers[Min(WorldInfo.Game.GetModifiedGameDifficulty(), DifficultyRageFartTimers.length)];
}
if (DifficultyVarianceRageFartTimers.length > 0)
{
RageVarianceFartAttackTimer = DifficultyVarianceRageFartTimers[Min(WorldInfo.Game.GetModifiedGameDifficulty(), DifficultyVarianceRageFartTimers.length)];
}
PlayBossMusic();
class'KFPawn_MonsterBoss'.static.PlayBossEntranceTheatrics(self);
SetFartAttackTimer();
GorgeTrigger = Spawn(class'KFTrigger_BloatKingGorge', self, , Location, Rotation);
GorgeTrigger.SetBase(self);
GorgeTrigger.SetRelativeLocation(vect(0, 0, 0));
GorgeTrigger.CylinderComponent.SetCylinderSize(Sqrt(class'KFSM_BloatKing_Gorge'.default.GorgeAttackRangeSq), CylinderComponent.CollisionHeight);
}
/** Play music for this boss (overridden for each boss) */
function PlayBossMusic()
{
if (KFGameInfo(WorldInfo.Game) != none)
{
KFGameInfo(WorldInfo.Game).ForceAbominationMusicTrack();
}
}
function bool CanObliterateDoors()
{
//King Bloat will always obliterate doors
return true;
}
event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
{
Super.TakeDamage(Damage, InstigatedBy, HitLocation, Momentum, DamageType, HitInfo, DamageCauser);
if (EnrageHealthThresholds.Length > 0 && (float(Health) / float(HealthMax)) < EnrageHealthThresholds[0])
{
if (IsCombatCapable())
{
EnrageHealthThresholds.Remove(0, 1);
KFAIController_ZedBloatKing(Controller).StartArmorEnrage();
}
}
}
function int GetHitZoneIndex(name BoneName)
{
if (BoneName == 'KBArmor')
{
//Known arbitrary, hacking some things in place
return OverrideArmorFXIndex;
}
return super.GetHitZoneIndex(BoneName);
}
simulated function KFSkinTypeEffects GetHitZoneSkinTypeEffects(int HitZoneIdx)
{
if (HitZoneIdx == OverrideArmorFXIndex)
{
return CharacterArch.ImpactSkins[2];
}
return super.GetHitZoneSkinTypeEffects(HitZoneIdx);
}
function ZedExplodeArmor(int ArmorZoneIdx, name ArmorZoneName)
{
super.ZedExplodeArmor(ArmorZoneIdx, ArmorZoneName);
Knockdown( Velocity != vect(0,0,0) ? -Velocity*2 : 3*(-vector(Rotation) * GroundSpeed), vect(1,1,1),,,,, Location );
//KFAIController_ZedBloatKing(Controller).StartArmorEnrage();
}
simulated event bool CanDoSpecialMove(ESpecialMove AMove, optional bool bForceCheck)
{
if (bIsEnraged && AMove == SM_Block)
{
return false;
}
return super.CanDoSpecialMove(AMove, bForceCheck);
}
/** Called by PawnAnimInfo when determining whether an attack with bSpecializedMode TRUE can be performed. This makes the
FleshPound use attacks from his PawnAnimInfo that are configured as "specialized" only if he's enraged. */
function bool ShouldPlaySpecialMeleeAnims()
{
return IsEnraged();
}
/** Return current rage status */
simulated event bool IsEnraged()
{
return bIsEnraged;
}
/** Enrage this FleshPound! */
simulated function bool SetEnraged(bool bNewEnraged)
{
local bool bSuccess;
bSuccess = super.SetEnraged(bNewEnraged);
if (bSuccess && Role == ROLE_Authority)
{
if (bIsEnraged)
{
DoSpecialMove(SM_Custom1, true, , SpecialMoveHandler.SpecialMoveClasses[SM_Custom1].static.PackFlagsBase(self));
//If we're higher then the average rage fart attack timer, reset it here to the new lower rage time
if (GetTimerCount(nameof(TimerFartAttack)) > Lerp(RageFartAttackTimer.X, RageFartAttackTimer.Y, GetHealthPercentage()))
{
ClearFartTimer();
SetFartAttackTimer();
}
if (KFAIController_ZedBloatKing(Controller) != None)
{
KFAIController_ZedBloatKing(Controller).SetEnrageTimer();
}
}
}
return bSuccess;
}
function AdjustMovementSpeed(float SpeedAdjust)
{
//Redo calculation here to account for the multiplier on sprint speed
if (bIsEnraged)
{
DesiredAdjustedGroundSpeed = default.GroundSpeed * SpeedAdjust * InitialGroundSpeedModifier;
if (IsDoingSpecialMove())
{
DesiredAdjustedSprintSpeed = fMax(default.SprintSpeed * SpeedAdjust * InitialGroundSpeedModifier * SpecialMoves[SpecialMove].GetSprintSpeedModifier() * default.RageSprintSpeedMultiplier, DesiredAdjustedGroundSpeed);
}
else
{
DesiredAdjustedSprintSpeed = fMax(default.SprintSpeed * SpeedAdjust * InitialGroundSpeedModifier * default.RageSprintSpeedMultiplier, DesiredAdjustedGroundSpeed);
}
NormalGroundSpeed = DesiredAdjustedGroundSpeed;
NormalSprintSpeed = DesiredAdjustedSprintSpeed;
}
else
{
super.AdjustMovementSpeed(SpeedAdjust);
}
}
/** 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);
}
/** Spawn a puke mine */
simulated function ANIMNOTIFY_PukeMineAttack()
{
local vector SpawnLocation;
local rotator SpawnRotation;
if (Role == ROLE_Authority)
{
Mesh.GetSocketWorldLocationAndRotation('PukeSocket', SpawnLocation, SpawnRotation);
SpawnPukeMine(SpawnLocation, SpawnRotation);
}
}
function SetFartAttackTimer()
{
local float HealthPct, TimerValue;
HealthPct = GetHealthPercentage();
if (bIsEnraged)
{
TimerValue = Lerp(RageFartAttackTimer.X, RageFartAttackTimer.Y, HealthPct) + ((FRand() - 0.5) * 2.0f) * Lerp(RageVarianceFartAttackTimer.X, RageVarianceFartAttackTimer.Y, HealthPct);
}
else
{
TimerValue = Lerp(BaseFartAttackTimer.X, BaseFartAttackTimer.Y, HealthPct) + ((FRand() - 0.5) * 2.0f) * Lerp(VarianceFartAttackTimer.X, VarianceFartAttackTimer.Y, HealthPct);
}
SetTimer(TimerValue, false, nameof(TimerFartAttack));
}
function ClearFartTimer()
{
ClearTimer(nameof(TimerFartAttack));
}
simulated function TimerFartAttack()
{
local Vector FartLocation;
local Rotator FartRotation;
if (!IsAliveAndWell())
{
ClearTimer(nameof(TimerFartAttack));
return;
}
// Explode using the given template
Mesh.GetSocketWorldLocationAndRotation(default.FartFXSocket, FartLocation, FartRotation);
// Notify to trigger client spawn
++FartFXNotify;
// Explosion, fart visual FX
PlayFartSpawnFX();
// Puke mine using the same tech for the standard puke mines
if (bIsEnraged)
{
SpawnPukeMine(FartLocation, FartRotation);
}
//Reset timer for next attack
SetFartAttackTimer();
}
simulated function PlayFartSpawnFX()
{
local Vector SocketLocation;
local Rotator SocketRotation;
local KFExplosion_BloatKingFart ExploActor;
if (Role == ROLE_Authority)
{
SpawnPoopMonster();
}
ExploActor = Spawn(class'KFExplosion_BloatKingFart', self, , Location, Rotation);
if (ExploActor != none)
{
ExploActor.InstigatorController = Controller;
ExploActor.Instigator = self;
ExploActor.Explode(FartExplosionTemplate);
}
if (WorldInfo.NetMode != NM_DedicatedServer)
{
Mesh.GetSocketWorldLocationAndRotation(default.FartFXSocket, SocketLocation, SocketRotation);
WorldInfo.MyEmitterPool.SpawnEmitter(default.FartFXTemplate, SocketLocation, SocketRotation);
}
}
function AddGorgeVictim(KFPawn NewPawn)
{
local bool bHuman;
local int i;
bHuman = KFPawn_Human(NewPawn) != none;
NewPawn.SetCollision(true, false);
NewPawn.setPhysics(PHYS_Interpolating);
for (i = bHuman ? 0 : 6; i < bHuman ? 6 : 11; ++i)
{
if (PullVictims[i] == none)
{
PullVictims[i] = NewPawn;
return;
}
}
}
function RemoveGorgeVictim(KFPawn OldPawn)
{
local int i;
OldPawn.SetCollision(true, true);
OldPawn.SetPhysics(PHYS_Walking);
for (i = 0; i < 11; ++i)
{
if (PullVictims[i] == OldPawn)
{
PullVictims[i] = none;
return;
}
}
}
function AllowNextPoopMonster()
{
if (CurrentDelayedSpawns > 0)
{
//Clear timer to force it to be out of the way when the call to spawn next monster checks for active timer
ClearTimer(NameOf(AllowNextPoopMonster));
CurrentDelayedSpawns--;
SpawnPoopMonster();
}
}
function SpawnPoopMonster()
{
local KFPawn_Monster NewZed;
local Vector X,Y,Z;
if (IsDoingSpecialMove(SM_BossTheatrics))
{
return;
}
if (IsTimerActive(NameOf(AllowNextPoopMonster)))
{
++CurrentDelayedSpawns;
return;
}
GetAxes(Rotation, X, Y, Z);
NewZed = Spawn(class'KFPawn_ZedBloatKingSubspawn', , , Location + (X * PoopMonsterOffset), Rotation, , true);
if (NewZed != None)
{
NewZed.SetPhysics(PHYS_Falling);
KFGameInfo(WorldInfo.Game).SetMonsterDefaults(NewZed);
NewZed.SpawnDefaultController();
NewZed.Knockdown(, vect(1, 1, 1), NewZed.Location, 1000, 100);
if (KFAIController(NewZed.Controller) != None)
{
KFGameInfo(WorldInfo.Game).GetAIDirector().AIList.AddItem(KFAIController(NewZed.Controller));
KFAIController(NewZed.Controller).SetTeam(1);
}
++PoopMonsterFXNotify;
PlayPoopSpawnFX();
}
SetTimer(PoopMonsterSpawnDelay, false, NameOf(AllowNextPoopMonster));
}
simulated function PlayPoopSpawnFX()
{
local Vector SocketLocation;
local Rotator SocketRotation;
if (WorldInfo.NetMode != NM_DedicatedServer)
{
Mesh.GetSocketWorldLocationAndRotation(default.PoopMonsterFXSocket, SocketLocation, SocketRotation);
WorldInfo.MyEmitterPool.SpawnEmitter(default.PoopMonsterFXTemplate, SocketLocation, SocketRotation);
PlaySoundBase(default.PoopMonsterSFXTemplate, true, true, true, SocketLocation, true, SocketRotation);
}
}
//-----------------------------------------------------------------------------
// Pawn Removals
//-----------------------------------------------------------------------------
function CauseHeadTrauma(float BleedOutTime = 5.f)
{
return;
}
simulated function bool PlayDismemberment(int InHitZoneIndex, class<KFDamageType> InDmgType, optional vector HitDirection)
{
return false;
}
simulated function PlayHeadAsplode()
{
return;
}
simulated function ApplyHeadChunkGore(class<KFDamageType> DmgType, vector HitLocation, vector HitDirection)
{
return;
}
simulated function string GetIconPath()
{
return "ZED_Patriarch_UI.ZED-VS_Icon_Boss";
}
defaultproperties
{
LocalizationKey=KFPawn_ZedBloatKing
//-----------------------------------------------------
// Gameplay
ArmorZoneStatus=7 // 0b00000111
PreviousArmorZoneStatus=7
bCanRage=true
//There can be as many thresholds here as you want. Just do them in order from highest -> lowest
EnrageHealthThresholds=(0.8,0.6,0.4,0.2)
//Fart is base timer +/- some random amount of the variance, giving you a range of say 25-35 seconds with a base of 30 and variance of 5
// These values are X = 0 health, Y = 100% health
DifficultyFartAttackTimers(0)=(X=5.0,Y=9.0) // Normal
DifficultyFartAttackTimers(1)=(X=4.5,Y=8.5) // Hard
DifficultyFartAttackTimers(2)=(X=4.0,Y=8.0) // Suicidal
DifficultyFartAttackTimers(3)=(X=4.0,Y=7.0) // Hell On Earth
DifficultyVarianceFartTimers(0)=(X=3.0,Y=4.0) // Normal
DifficultyVarianceFartTimers(1)=(X=2.5,Y=3.5) // Hard
DifficultyVarianceFartTimers(2)=(X=2.0,Y=3.0) // Suicidal
DifficultyVarianceFartTimers(3)=(X=2.0,Y=3.0) // Hell On Earth
DifficultyRageFartTimers(0)=(X=1.75,Y=2.5) // Normal
DifficultyRageFartTimers(1)=(X=1.25,Y=2.0) // Hard
DifficultyRageFartTimers(2)=(X=0.75,Y=1.5) // Suicidal
DifficultyRageFartTimers(3)=(X=0.75,Y=1.5) // Hell On Earth
DifficultyVarianceRageFartTimers(0)=(X=1.65,Y=2.1) // Normal
DifficultyVarianceRageFartTimers(1)=(X=1.15,Y=1.6) // Hard
DifficultyVarianceRageFartTimers(2)=(X=0.65,Y=1.1) // Suicidal
DifficultyVarianceRageFartTimers(3)=(X=0.65,Y=1.1) // Hell On Earth
//Offset from center of the BK to spawn the poop monster
PoopMonsterOffset=-100
PoopMonsterFXTemplate=ParticleSystem'CHR_Bile_EMIT.FX_Bile_Birth_01'
PoopMonsterSFXTemplate=AkEvent'WW_ZED_Abomination.Play_Abomination_Bile_Spawn'
PoopMonsterFXSocket=Poop_Attach
//Amount of time between spawns
PoopMonsterSpawnDelay=2.f
// Used for special crawler gas AOE attack "explosion" template FART ATTACK
Begin Object Class=KFGameExplosion Name=ExploTemplate0
Damage=5
DamageRadius=450 //600
DamageFalloffExponent=0.f
DamageDelay=0.f
MyDamageType=class'KFDT_Toxic_BloatKingFart'
// Damage Effects
KnockDownStrength=0
KnockDownRadius=0
FractureMeshRadius=0
FracturePartVel=0
ExplosionEffects=KFImpactEffectInfo'ZED_BloatKing_ARCH.FX_GasAoE_Explosion_01'
ExplosionSound=AkEvent'WW_ZED_Abomination.Play_Abomination_AOE_Explo'
MomentumTransferScale=100
// Dynamic Light
ExploLight=none
// Camera Shake
CamShake=CameraShake'FX_CameraShake_Arch.Grenades.Default_Grenade'
CamShakeInnerRadius=350 //450
CamShakeOuterRadius=600 //900
CamShakeFalloff=1.f
bOrientCameraShakeTowardsEpicenter=true
End Object
FartExplosionTemplate =ExploTemplate0
FartFXTemplate=ParticleSystem'ZED_BloatKing_EMIT.FX_Bloatking_Fart_01'
FartFXSocket=Poop_Attach
// ---------------------------------------------
// 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=900
// ---------------------------------------------
// Special Moves
Begin Object Name=SpecialMoveHandler_0
SpecialMoveClasses(SM_GrappleAttack)=class'KFSM_BloatKing_Gorge'
SpecialMoveClasses(SM_Custom1)=class'KFSM_BloatKing_Enrage'
End Object
//-----------------------------------------------------
// Content
MonsterArchPath="ZED_ARCH.ZED_BloatKing_Archetype"
PawnAnimInfo=KFPawnAnimInfo'ZED_BloatKing_ANIM.BloatKing_AnimGroup'
DifficultySettings=class'KFDifficulty_BloatKing'
OverrideArmorFXIndex=200
PukeMineProjectileClass=class'KFGameContent.KFProj_BloatKingPukeMine'
//-----------------------------------------------------
// AI / Nav
ControllerClass=class'KFAIController_ZedBloatKing'
Begin Object Name=MeleeHelper_0
BaseDamage=55.f
MaxHitRange=375.f
MomentumTransfer=40000.f
MyDamageType=class'KFDT_Bludgeon_BloatKing'
End Object
// Stats
XPValues(0)=1291
XPValues(1)=1694
XPValues(2)=1790
XPValues(3)=1843
Health=9000 //11250
DoshValue=500
Mass=400.f
RotationRate=(Pitch=50000,Yaw=20000,Roll=50000)
GroundSpeed=450.0f //160 //210 //231 //255
SprintSpeed=450.0f //260 //210 410 //315 //330 //345 //380
RageSprintSpeedMultiplier=1.3f //1.25 1.45 //1.55
VomitRange=400.f
VomitDamage=20
ArmorInfoClass=class'KFZedArmorInfo_BloatKing'
RepArmorPct[0]=255
RepArmorPct[1]=255
RepArmorPct[2]=255
HitZones[HZI_HEAD]=(ZoneName=head, BoneName=Head, Limb=BP_Head, GoreHealth=MaxInt, DmgScale=1.2, SkinID=1)
HitZones[2]=(ZoneName=chest, BoneName=Spine2, Limb=BP_Torso, GoreHealth=150, DmgScale=1.2)
HitZones[10]=(ZoneName=stomach, BoneName=Spine1, Limb=BP_Torso, GoreHealth=150, DmgScale=1.2)
HitZones[11]=(ZoneName=abdomen, BoneName=Hips, Limb=BP_Torso, GoreHealth=150, DmgScale=1.2)
// for reference: Vulnerability=(default, head, legs, arms, special)
// cutting everything in half to make it harder to stumble except the head.
IncapSettings(AF_Stun)= (Vulnerability=(0.1, 0.55, 0.1, 0.1, 0.55), Cooldown=17.0, Duration=1.25)
IncapSettings(AF_Knockdown)=(Vulnerability=(0.1, 0.4, 0.1, 0.1, 0.25), Cooldown=20.0)
IncapSettings(AF_Stumble)= (Vulnerability=(0.1, 0.3, 0.1, 0.1, 0.4), Cooldown=10.0)
IncapSettings(AF_GunHit)= (Vulnerability=(0.1, 0.1, 0.1, 0.1, 0.5), Cooldown=1.7)
IncapSettings(AF_MeleeHit)= (Vulnerability=(0.1, 0.95, 0.1, 0.1, 0.75), Cooldown=2.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.5), 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)
// 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)))
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)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Bludgeon', DamageScale=(1.0)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Fire', DamageScale=(0.5)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Microwave', DamageScale=(0.9)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Explosive', DamageScale=(0.4)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Piercing', DamageScale=(0.5)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic', DamageScale=(0.05))
DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic_BloatKingFart', DamageScale=(0.00)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic_BloatKingPukeMine', DamageScale=(0.0)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic_BloatKingSubspawnExplosion', DamageScale=(0.75)))
//Special cases
DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic_HRGHealthrower', DamageScale=(1.0)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_MicrowaveRifle', DamageScale=(0.7)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRGTeslauncher', DamageScale=(0.6)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Minigun', DamageScale=(0.6)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic_MineReconstructorExplosion', DamageScale=(1.2)))
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_Ballistic_HRGScorcherLightingImpact', DamageScale=(0.4)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Fire_HRGScorcherDoT', DamageScale=(0.4)))
ParryResistance=4
// Penetration
PenetrationResistance=10.0
bCanBePinned=false
}