435 lines
15 KiB
Ucode
435 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))
|
|
|
|
|
|
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'
|
|
}
|