1
0
KF2-Dev-Scripts/KFGameContent/Classes/KFPawn_ZedDAR.uc
2023-09-21 22:31:11 +03:00

444 lines
15 KiB
Ucode

//=============================================================================
// KFPawn_ZedDAR
//=============================================================================
// Base pawn for evil DAR robots.
//=============================================================================
// Killing Floor 2
// Copyright (C) 2018 Tripwire Interactive LLC
//=============================================================================
class KFPawn_ZedDAR extends KFPawn_Monster
abstract;
var protected KFAIController_ZedDAR MyDARController;
var class<KFProjectile> RangedProjectileClass;
var const name FiringSocketLName;
var const name FiringSocketRName;
var name FiringSocketName;
var protected transient bool bHasExploded;
/** Explosion template for backpack/suicide */
var protected KFGameExplosion ExplosionTemplate;
/** HitZoneIndex of backpack zone */
const ChestBombZoneIndex = 3;
var transient bool bChestArmorDestroyedThisFrame;
function PossessedBy(Controller C, bool bVehicleTransition)
{
Super.PossessedBy(C, bVehicleTransition);
MyDARController = KFAIController_ZedDAR(C);
}
simulated function StartRangedAttack()
{
local KFSM_RangedAttack RangedSM;
if(MyDARController != none)
{
RangedSM = KFSM_RangedAttack(SpecialMoves[SpecialMove]);
MyDARController.FireRangedAttack(RangedProjectileClass, RangedSM.GetFireOffset());
}
}
function OnStackingAfflictionChanged(byte Id)
{
Super.OnStackingAfflictionChanged(Id);
if (MyKFAIC == none || !IsAliveAndWell())
{
return;
}
if (bEMPDisrupted)
{
if (IsDoingSpecialMove(SM_StandAndShootAttack))
{
EndSpecialMove();
}
}
}
function AdjustPlayHitForArmor(out float InDamage, out TraceHitInfo InHitInfo)
{
local int HitZoneIdx;
// If the gore health is empty, then the armor has been completely reduced (and fallen off)
// so play the normal hit otherwise mark it as taken by the armor
HitZoneIdx = GetHitZoneIndex(InHitInfo.BoneName);
if (HitZoneIdx == INDEX_NONE || HitZones[HitZoneIdx].GoreHealth > 0)
{
super.AdjustPlayHitForArmor(InDamage, InHitInfo);
}
}
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[3];
}
return super.GetHitZoneSkinTypeEffects(HitZoneIdx);
}
function CauseHeadTrauma(float BleedOutTime = 5.f)
{
// DAR doesn't bleed out from losing its head.
}
function ZedExplodeArmor(int ArmorZoneIdx, name ArmorZoneName)
{
super.ZedExplodeArmor(ArmorZoneIdx, ArmorZoneName);
if (MyDARController != none)
{
MyDARController.OnArmorLoss(ArmorZoneName);
}
if (ArmorZoneName == 'front')
{
bChestArmorDestroyedThisFrame = true;
}
}
function SetSprinting(bool bNewSprintStatus)
{
if (bNewSprintStatus)
{
if(CanDoSpecialMove(SM_SprintStart))
{
DoSpecialMove(SM_SprintStart, true,, SpecialMoves[SM_SprintStart].PackFlagsBase(self));
}
}
else
{
super.SetSprinting(bNewSprintStatus);
}
}
simulated function NotifySpecialMoveEnded(KFSpecialMove FinishedMove, ESpecialMove SMHandle)
{
super.NotifySpecialMoveEnded(FinishedMove, SMHandle);
if (SMHandle == SM_SprintStart)
{
// Finish sprinting setup
super.SetSprinting(true);
}
}
function PlayHit(float Damage, Controller InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, TraceHitInfo HitInfo)
{
super.PlayHit(Damage, InstigatedBy, HitLocation, damageType, Momentum, HitInfo);
if (Role == ROLE_Authority && TimeOfDeath == WorldInfo.TimeSeconds && bChestArmorDestroyedThisFrame)
{
bChestArmorDestroyedThisFrame = false;
TriggerExplosion(true);
}
}
/** Plays hit effects on dead zeds, this includes dismemberment and obliteration */
simulated function PlayDeadHitEffects(vector HitLocation, vector HitDirection, int HitZoneIndex, name HitZoneName, name HitBoneName, class<KFDamageType> DmgType, optional bool bUseHitImpulse)
{
local class<KFProj_PinningBullet> PinProjectileClass;
local KFPawn DeadPawn;
local KFGoreManager GoreManager;
local bool bIsDismemberingHit;
local bool bWasObliterated;
// If ragdoll and gore is not allowed for dead bodies, check the time of death
// to see when the pawn died, and skip if he has been dead for a while
if (bAllowRagdollAndGoreOnDeadBodies || `TimeSince(TimeOfDeath) <= 3.f )
{
// If this zone is 'injured' try to dismember it
if ((InjuredHitZones & (1 << HitZoneIndex)) > 0 && !HitZones[HitZoneIndex].bPlayedInjury)
{
bIsDismemberingHit = PlayDismemberment(HitZoneIndex, DmgType, HitDirection);
// If there was no dismemberment, explode head instead
if (!bIsDismemberingHit && (InjuredHitZones & (1 << HZI_Head)) > 0)
{
PlayHeadAsplode();
// Set bIsDismemberingHit to true to add an impulse to the neck
bIsDismemberingHit = true;
}
}
GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
// If the gore manager doesn't allow mutilation, then we don't allow anything other than basic effects.
if (GoreManager.AllowMutilation() && (HitFxInfo.bObliterated || HitZoneIndex == 3) && `TimeSince(TimeOfDeath) < 0.25f && !bUseDamageInflation)
{
bWasObliterated = true;
bIsDismemberingHit = true;
HandlePartialGoreAndGibs(DmgType, HitLocation, HitDirection, HitBoneName, true);
GoreManager = KFGoreManager(WorldInfo.MyGoreEffectManager);
GoreManager.SpawnObliterationBloodEffect(self);
}
else
{
// Handle damage types from projectiles that can pin zeds to walls
PinProjectileClass = DmgType.static.GetPinProjectileClass();
if (PinProjectileClass != none)
{
DeadPawn = self;
PinProjectileClass.static.CreatePin(DeadPawn, HitLocation, HitDirection, HitBoneName);
}
HandlePartialGoreAndGibs(DmgType, HitLocation, HitDirection, HitBoneName, false);
}
// Apply an impulse to attached limbs and ragdoll
HandleRagdollImpulseEffects(HitLocation, HitDirection, HitZoneName, HitBoneName, DmgType, bIsDismemberingHit, bUseHitImpulse);
}
// Play blood effects. Apply more blood if this was a dismembering hit
ApplyBloodDecals(HitZoneIndex, HitLocation, HitDirection, HitZoneName, HitBoneName, DmgType, bIsDismemberingHit, bWasObliterated);
}
function bool CanInjureHitZone(class<DamageType> DamageType, int HitZoneIdx)
{
if (HitZoneIdx == ChestBombZoneIndex && (!bPlayedDeath || WorldInfo.TimeSeconds == TimeOfDeath))
{
return true;
}
return super.CanInjureHitZone(DamageType, HitZoneIdx);
}
/** Reliably play any gore effects related to a zone/limb being dismembered */
simulated function HitZoneInjured(optional int HitZoneIdx = INDEX_None)
{
Super.HitZoneInjured(HitZoneIdx);
// Server only since we use a replicated explosion actor
if (Role == ROLE_Authority && HitZoneIdx == ChestBombZoneIndex)
{
TriggerExplosion(true);
}
}
/** Called when husk backpack is exploded or when husk suicides */
simulated function TriggerExplosion(optional bool bIgnoreHumans)
{
local KFExplosionActorReplicated ExploActor;
local Controller DamageInstigator, OldController;
local bool bExplodeOnDeath;
bExplodeOnDeath = WorldInfo.TimeSeconds == TimeOfDeath;
// Only living husks can explode... and only once
if (!bHasExploded)
{
OldController = Controller;
bHasExploded = true;
if (Role == ROLE_Authority)
{
// explode using the given template
ExploActor = Spawn(class'KFExplosionActorReplicated', self);
if (ExploActor != None)
{
DamageInstigator = (bIgnoreHumans && LastHitBy != none && KFPlayerController(LastHitBy) != none) ? LastHitBy : MyKFAIC;
ExploActor.InstigatorController = DamageInstigator;
ExploActor.Instigator = self;
// Force ourselves to get hit. These settings are not replicated,
// but they only really make a difference on the server anyway.
ExploActor.Attachee = self;
if (bIgnoreHumans)
{
ExplosionTemplate.ActorClassToIgnoreForDamage = class'KFPawn_Human';
}
else
{
ExplosionTemplate.ActorClassToIgnoreForDamage = none;
}
ExploActor.Explode(ExplosionTemplate, vect(0, 0, 1));
}
// Make sure we're dead!
if (!bPlayedDeath || bExplodeOnDeath)
{
Health = 0;
Died(DamageInstigator, ExplosionTemplate.MyDamageType, Location);
}
}
OnExploded(OldController);
}
}
/** Overriden in subclasses. Determines if we should explode on death in specific game modes */
function bool WeeklyShouldExplodeOnDeath()
{
return !bHasExploded;
}
/** Do any explosion death-related actions */
simulated function OnExploded(Controller SuicideController);
/**
* Return true if this pawn is ok with having the input head bone be broken
*
* @param BoneName the head bone to check to see if we all it to break.
*/
simulated function bool ShouldAllowHeadBoneToBreak(name BoneName)
{
return true;
}
defaultproperties
{
MinSpawnSquadSizeType=EST_Medium
bEnableAimOffset=true
Health=775//462 //562
DoshValue=17
Mass=85.f //65
// ---------------------------------------------
// Stats
XPValues(0)=15
XPValues(1)=20
XPValues(2)=27
XPValues(3)=31
// ---------------------------------------------
// Movement / Physics
GroundSpeed=170.0f
SprintSpeed=600.0f //450 //550
RotationRate=(Pitch=50000,Yaw=66000,Roll=50000)
Begin Object Name=MeleeHelper_0
BaseDamage=10.f
MaxHitRange=180.f
MomentumTransfer=25000.f
MyDamageType=class'KFDT_Slashing_ZedWeak'
End Object
ArmorInfoClass=class'KFZedArmorInfo_EvilDAR'
ArmorZoneStatus=3
PreviousArmorZoneStatus=3
OverrideArmorFXIndex=200
RepArmorPct[0]=255
RepArmorPct[1]=255
Begin Object Name=SpecialMoveHandler_0
SpecialMoveClasses(SM_SprintStart)=class'KFGame.KFSM_StartSprintAnim'
End Object
// Explosion light
Begin Object Class=PointLightComponent Name=ExplosionPointLight
LightColor = (R=128,G=200,B=255,A=255)
bCastPerObjectShadows = false
End Object
// Backpack/Suicide Explosion
Begin Object Class=KFGameExplosion Name=ExploTemplate0
Damage=25
DamageRadius=500
DamageFalloffExponent=0.5f
DamageDelay=0.f
bFullDamageToAttachee=true
// Damage Effects
MyDamageType = class'KFDT_EMP'
FractureMeshRadius = 200.0
FracturePartVel = 500.0
ExplosionEffects = KFImpactEffectInfo'FX_Impacts_ARCH.Explosions.EDar_EMP_Explosion'
ExplosionSound = AkEvent'WW_WEP_EXP_Grenade_EMP.Play_WEP_EXP_Grenade_EMP_Explosion'
// Dynamic Light
ExploLight=ExplosionPointLight
ExploLightStartFadeOutTime=0.0
ExploLightFadeOutTime=0.5
// Camera Shake
CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.HuskSuicide'
CamShakeInnerRadius=450
CamShakeOuterRadius=900
CamShakeFalloff=1.f
bOrientCameraShakeTowardsEpicenter=true
End Object
ExplosionTemplate=ExploTemplate0
// for reference: Vulnerability=(default, head, legs, arms, special)
IncapSettings(AF_Stun)= (Vulnerability=(0.5, 2.0, 0.5, 0.5, 2.0), Cooldown=5.0, Duration=1.5)
IncapSettings(AF_Knockdown)=(Vulnerability=(1.0), Cooldown=1.0)
IncapSettings(AF_Stumble)= (Vulnerability=(1.0), Cooldown=1.0) //0.4
IncapSettings(AF_GunHit)= (Vulnerability=(0.9), Cooldown=0.2)
IncapSettings(AF_MeleeHit)= (Vulnerability=(1.0), Cooldown=0.0)
IncapSettings(AF_FirePanic)=(Vulnerability=(0.5), Cooldown=6.0, Duration=5.0)
IncapSettings(AF_EMP)= (Vulnerability=(2.5), Cooldown=5.0, Duration=5.0)
IncapSettings(AF_Poison)= (Vulnerability=(0.05), Cooldown=20.5, Duration=5.0)
IncapSettings(AF_Microwave)=(Vulnerability=(3), Cooldown=6.5, Duration=4.0)
IncapSettings(AF_Freeze)= (Vulnerability=(0.9), Cooldown=1.0, Duration=4.2)
IncapSettings(AF_Snare)= (Vulnerability=(1.0, 1.0, 2.0, 1.0, 1.0), Cooldown=5.5, Duration=3.0)
IncapSettings(AF_Bleed)= (Vulnerability=(0.01))
IncapSettings(AF_Shrink)= (Vulnerability=(1.0))
ShrinkEffectModifier = 0.7f
ParryResistance=1
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Submachinegun', DamageScale=(1.05))) //0.75
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_AssaultRifle', DamageScale=(1.05)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Shotgun', DamageScale=(1.2)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Handgun', DamageScale=(1.0))) //0.7
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Rifle', DamageScale=(1.0))) //0.75
DamageTypeModifiers.Add((DamageType=class'KFDT_Slashing', DamageScale=(1.25))) //0.75
DamageTypeModifiers.Add((DamageType=class'KFDT_Bludgeon', DamageScale=(1.25))) //0.85
DamageTypeModifiers.Add((DamageType=class'KFDT_Fire', DamageScale=(1.5))) //0.5 //0.75
DamageTypeModifiers.Add((DamageType=class'KFDT_Microwave', DamageScale=(3.25))) //0.85 //2.0
DamageTypeModifiers.Add((DamageType=class'KFDT_Explosive', DamageScale=(2.5))) //0.6
DamageTypeModifiers.Add((DamageType=class'KFDT_Piercing', DamageScale=(0.85))) //0.5
DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic', DamageScale=(0.05))) //0.25
//special case
DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic_HRGHealthrower', DamageScale=(1.2)))
DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_Beam', DamageScale=(3.5)))
DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGeneratorSphereImpact', DamageScale=(2)))
DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_DefaultFiremodeZapDamage', DamageScale=(3.5)))
DamageTypeModifiers.Add((DamageType=class'KFDT_EMP_ArcGenerator_AltFiremodeZapDamage', DamageScale=(3.5)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic_MineReconstructorExplosion', DamageScale=(1.8)))
// Custom Hit Zones (HeadHealth, SkinTypes, etc...)
HitZones[HZI_HEAD]=(ZoneName=head, BoneName=Head, Limb=BP_Head, GoreHealth=350, DmgScale=1.001, SkinID=1) // KF1=200 //154
HitZones[3] =(ZoneName=heart, BoneName=Spine1, Limb=BP_Special, GoreHealth=150, DmgScale=3.5, SkinID=2) //1.4
WeakSpotSocketNames.Empty()
WeakSpotSocketNames.Add(FX_Armor_Chest) // Chest
StartSprintingSound=AkEvent'WW_ZED_Evil_DAR.Play_ZED_EvilDAR_SFX_Thruster_Start'
SprintLoopingSound=AkEvent'WW_ZED_Evil_DAR.Play_ZED_EvilDAR_SFX_Thruster_LP'
StopSprintingSound=AkEvent'WW_ZED_Evil_DAR.Play_ZED_EvilDAR_SFX_Thruster_Stop'
ZEDCowboyHatAttachName=Hat_Attach
// 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
MapReplacePawnClass.Add(class'KFPawn_ZedHansClot')
}