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

415 lines
14 KiB
Ucode

//=============================================================================
// KFPawn_ZedBloat
//=============================================================================
// Bloat
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
//=============================================================================
class KFPawn_ZedBloat extends KFPawn_Monster;
/** Socket used to attach puke effect */
var protected const name PukeSocketName;
/** How far away the bloat's vomit can damage enemies */
var protected const float VomitRange;
/** At a base level how much damage this zed's vomit will do */
var protected const int VomitDamage;
/** Any pawn in this range will take damage when the bloat explodes */
var protected const float ExplodeRange;
/** Set to TRUE when bloat has exploded */
var protected bool bHasExploded;
/** Projectile to spawn for puke mine attack */
var protected const class<KFProjectile> PukeMineProjectileClass;
/** Rotation offsets to use when spawning our 3 puke mins on death */
var protected array<rotator> DeathPukeMineRotations;
/** Number of puke mines to spawn when bloat is killed, as long as it wasn't obliterated */
var protected byte NumPukeMinesToSpawnOnDeath;
/** Pre-death location and rotation */
var protected vector OldLocation;
var protected rotator OldRotation;
/** Set to TRUE when a non-explosive hitzone was injured */
var transient protected bool bWasDismembered;
simulated function PostBeginPlay()
{
super.PostBeginPlay();
// Set the number of puke mines we're allowed to spawn on death
if( WorldInfo.Game != none )
{
NumPukeMinesToSpawnOnDeath = class<KFDifficulty_Bloat>(DifficultySettings).static.GetPukeMinesToSpawnOnDeath( self, WorldInfo.Game );
}
}
/** Script AnimNotify which makes the Bloat begin to discharge vomit */
function ANIMNOTIFY_PukeAttack()
{
Puke();
}
/** Guess */
function Puke()
{
local Pawn P;
local Vector PukeLocation, PukeDirection;
local Rotator PukeRotation;
local vector HitLocation, HitNormal, EndTrace, Momentum;
local Actor HitActor;
Mesh.GetSocketWorldLocationAndRotation( 'PukeSocket', PukeLocation, PukeRotation );
PukeDirection = Vector(Rotation);
PukeDirection.Z = 0.f;
foreach WorldInfo.AllPawns(class'Pawn', P, PukeLocation, VomitRange)
{
if( CanPukeOnTarget(P, PukeLocation, PukeDirection) )
{
DealPukeDamage( P, PukeLocation );
}
}
// Extra handling for fracture mesh actors, etc
EndTrace = PukeLocation + PukeDirection*VomitRange;
HitActor = Trace( HitLocation, HitNormal, EndTrace, PukeLocation, true, vect(10,10,10) );
if( HitActor != none && HitActor.bCanBeDamaged && Pawn(HitActor) == none )
{
Momentum = EndTrace - PukeLocation;
Momentum.Z = 0.f;
Momentum = Normal(Momentum);
HitActor.TakeDamage( VomitDamage, Controller, HitLocation, Momentum, class'KFDT_BloatPuke',, self );
}
}
function DealPukeDamage( Pawn Victim, Vector Origin )
{
local Vector VectToEnemy;
VectToEnemy = Victim.Location - Origin;
VectToEnemy.Z = 0.f;
VectToEnemy = Normal( VectToEnemy );
Victim.TakeDamage( GetRallyBoostDamage(VomitDamage), Controller, Victim.Location, VectToEnemy, class'KFDT_BloatPuke',, self );
}
function bool CanPukeOnTarget( Pawn PukeTarget, Vector PukeLocation, Vector PukeDirection)
{
local Vector VectToEnemy;
local Box ActorBox;
if( PukeTarget != none )
{
VectToEnemy = PukeTarget.Location - PukeLocation;
VectToEnemy.Z = 0.f;
VectToEnemy = Normal( VectToEnemy );
PukeTarget.GetComponentsBoundingBox( ActorBox );
if( ( !PukeTarget.bWorldGeometry || PukeTarget.bCanBeDamaged ) &&
ActorBox.Min.Z < PukeLocation.Z && VectToEnemy dot PukeDirection > 0.5f ) // (0.5 = vomitarc)
{
// use the bounding box for the world trace to account for meshes with way off center origins
if( PukeTarget.FastTrace( (ActorBox.Min + ActorBox.Max) * 0.5, PukeLocation,, true) )
{
return true;
}
}
}
return false;
}
/** Override so you can always injure a bloats stomach and cause an explosion */
function bool CanInjureHitZone(class<DamageType> DamageType, int HitZoneIdx)
{
if( HitExplosiveBone( HitZones[HitZoneIdx].BoneName ) )
{
return true;
}
if( super.CanInjureHitZone(DamageType, HitZoneIdx) )
{
bWasDismembered = true;
return true;
}
return false;
}
/** Overridden to only return TRUE if a non-explosive bone was hit */
simulated function bool HasInjuredHitZones()
{
return bWasDismembered || IsHeadless();
}
/** This pawn has died. */
function bool Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
OldLocation = Location;
OldRotation = Rotation;
return super.Died( Killer, damageType, HitLocation );
}
// Override to deal explosive damage for the killing shot of an explosive bone
function TakeHitZoneDamage(float Damage, class<DamageType> DamageType, int HitZoneIdx, vector InstigatorLocation)
{
local int HitZoneIndex;
local name HitBoneName;
super.TakeHitZoneDamage( Damage, DamageType, HitZoneIdx, InstigatorLocation );
// Only deal explosive damage on the killing shot
if( Role == ROLE_Authority && bPlayedDeath && !bHasExploded && TimeOfDeath == WorldInfo.TimeSeconds )
{
HitZoneIndex = HitFxInfo.HitBoneIndex;
if ( HitZoneIndex != 255 && (InjuredHitZones & (1 << HitZoneIndex)) > 0 ) // INDEX_None -> 255 after byte conversion
{
HitBoneName = HitZones[HitZoneIndex].BoneName;
if( HitExplosiveBone(HitBoneName) )
{
DealExplosionDamage();
bHasExploded = true;
// Spawn some puke mines
SpawnPukeMinesOnDeath();
SoundGroupArch.PlayObliterationSound(self, false);
}
}
}
}
/** Check if the bone we hit can cause an explosion */
function bool HitExplosiveBone( name HitBoneName )
{
local byte JointIndex;
local KFCharacterInfo_Monster MonsterInfo;
MonsterInfo = GetCharacterMonsterInfo();
if ( MonsterInfo != none )
{
for( JointIndex = 0; JointIndex < MonsterInfo.GoreJointSettings.length; JointIndex++ )
{
if( MonsterInfo.GoreJointSettings[JointIndex].HitBoneName == HitBoneName &&
MonsterInfo.GoreJointSettings[JointIndex].HitExplosionGore.length > 0 )
{
return true;
}
}
}
return false;
}
function DealExplosionDamage()
{
local Pawn P;
local vector HitLocation, HitNormal;
local Actor HitActor;
// @note - At low ranges CollidingActors (no VisibleCollidingActors) is okay,
// but AllPawns is constant and much faster 99% of the time.
foreach WorldInfo.AllPawns( class'Pawn', P, Location, ExplodeRange )
{
if ( P != Instigator )
{
// Trace to make sure there are no obstructions. ie acquiring someone through a wall
HitActor = Instigator.Trace(HitLocation, HitNormal, P.Location, Location, true);
if ( HitActor == none || HitActor == P )
{
DealPukeDamage(P, Location);
}
}
}
}
/** Spawns a puke mine at the specified location and rotation. Network: SERVER */
function SpawnPukeMine( vector SpawnLocation, rotator SpawnRotation )
{
local KFProjectile PukeMine;
PukeMine = Spawn( PukeMineProjectileClass, ,, SpawnLocation, SpawnRotation,, true );
if( PukeMine != none )
{
PukeMine.Init( vector(SpawnRotation) );
}
}
/** Spawns several puke mines when dying */
function SpawnPukeMinesOnDeath()
{
local int i;
// Spawn puke mines
while( NumPukeMinesToSpawnOnDeath > 0 && DeathPukeMineRotations.Length > 0 )
{
i = Rand( DeathPukeMineRotations.Length );
SpawnPukeMine( OldLocation, Normalize(OldRotation + DeathPukeMineRotations[i]) );
DeathPukeMineRotations.Remove( i, 1 );
--NumPukeMinesToSpawnOnDeath;
}
}
/**
* Dialog
**/
/** Returns (hardcoded) dialog event ID for when trader gives advice to player who was killed by this zed type */
static function int GetTraderAdviceID()
{
return 41;//TRAD_AdviceBloat
}
DefaultProperties
{
LocalizationKey=KFPawn_ZedBloat
Begin Object Name=KFPawnSkeletalMeshComponent
// Enabling kinematic for physics interaction while alive. (see also MinDistFactorForKinematicUpdate)
bUpdateKinematicBonesFromAnimation=true
bPerBoneMotionBlur=false
End Object
// ---------------------------------------------
// Stats
XPValues(0)=17
XPValues(1)=22
XPValues(2)=30
XPValues(3)=34
//BaseEyeHeight=1.f
// ---------------------------------------------
// Content
MonsterArchPath="ZED_ARCH.ZED_Bloat_Archetype"
PawnAnimInfo=KFPawnAnimInfo'ZED_Bloat_ANIM.Bloat_AnimGroup'
DifficultySettings=class'KFDifficulty_Bloat'
// ---------------------------------------------
// Special Moves
Begin Object Name=SpecialMoveHandler_0
SpecialMoveClasses(SM_Evade)=class'KFSM_Evade'
SpecialMoveClasses(SM_Evade_Fear)=class'KFSM_Evade_Fear'
SpecialMoveClasses(SM_Block)=class'KFSM_Block'
End Object
// for reference: Vulnerability=(default, head, legs, arms, special)
IncapSettings(AF_Stun)= (Vulnerability=(0.5, 1.0, 0.5, 0.5, 0.5), Cooldown=5.0, Duration=1.5)
IncapSettings(AF_Knockdown)=(Vulnerability=(1.0), Cooldown=1.0)
IncapSettings(AF_Stumble)= (Vulnerability=(0.4), Cooldown=1.0)
IncapSettings(AF_GunHit)= (Vulnerability=(0.35), Cooldown=0.1)
IncapSettings(AF_MeleeHit)= (Vulnerability=(2.0), Cooldown=0.3)
IncapSettings(AF_Poison)= (Vulnerability=(0.15), Cooldown=20.5, Duration=5.0)
IncapSettings(AF_Microwave)=(Vulnerability=(4.0), Cooldown=5.0, Duration=8.0)
IncapSettings(AF_FirePanic)=(Vulnerability=(1), Cooldown=5.0, Duration=3.7) //duration 8
IncapSettings(AF_EMP)= (Vulnerability=(2.5), Cooldown=5.0, Duration=3.0)
IncapSettings(AF_Freeze)= (Vulnerability=(1.0), Cooldown=3.0, Duration=2.0)
IncapSettings(AF_Snare)= (Vulnerability=(1.0, 2.0, 1.0, 1.0), Cooldown=5.5, Duration=3.0)
IncapSettings(AF_Bleed)= (Vulnerability=(1.0))
IncapSettings(AF_Shrink)= (Vulnerability=(1.0))
ShrinkEffectModifier = 0.25f
Begin Object Name=Afflictions_0
FireFullyCharredDuration=3.5
End Object
ParryResistance=3
// ---------------------------------------------
// Gameplay
VomitRange=350.f
VomitDamage=12
ExplodeRange=500.f
Begin Object Name=MeleeHelper_0
BaseDamage=14.f
MaxHitRange=250.f
MomentumTransfer=25000.f
MyDamageType=class'KFDT_Slashing_ZedWeak'
End Object
Health=405
HeadlessBleedOutTime=6.f
// Override Head GoreHealth (aka HeadHealth)
HitZones[HZI_HEAD]=(ZoneName=head, BoneName=Head, Limb=BP_Head, GoreHealth=75, DmgScale=1.0001, SkinID=1)
HitZones.Add((ZoneName=rknife, BoneName=RightForearm, Limb=BP_RightArm, GoreHealth=20, DmgScale=0.2, SkinID=2))
HitZones.Add((ZoneName=lknife, BoneName=LeftForearm, Limb=BP_LeftArm, GoreHealth=20, DmgScale=0.2, SkinID=2))
DoshValue=17
Mass=130.f
// Penetration
PenetrationResistance=3.0
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Submachinegun', DamageScale=(0.35))) //0.25
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_AssaultRifle', DamageScale=(0.35))) //0.25
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Shotgun', DamageScale=(0.25)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Handgun', DamageScale=(0.35))) //0.2
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_Rifle', DamageScale=(0.30)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Slashing', DamageScale=(0.3)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Bludgeon', DamageScale=(0.3)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Fire', DamageScale=(1.0))) //1.2 //1.6
DamageTypeModifiers.Add((DamageType=class'KFDT_Microwave', DamageScale=(0.8)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Explosive', DamageScale=(0.5)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Piercing', DamageScale=(0.25)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Toxic', DamageScale=(0.25)))
//Special Case damage resistance
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_9mm', DamageScale=(0.65)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_AR15', DamageScale=(0.40)))
DamageTypeModifiers.Add((DamageType=class'KFDT_Ballistic_HRGTeslauncher', DamageScale=(0.58)))
// ---------------------------------------------
// Block Settings
MinBlockFOV=0.1f
// ---------------------------------------------
// Movement / Physics
Begin Object Name=CollisionCylinder
CollisionRadius=+0055.000000 // Mesh is pulled back by 20 UU
End Object
RotationRate=(Pitch=50000,Yaw=20000,Roll=50000)
GroundSpeed=150.0f
SprintSpeed=210.0f //260
PhysRagdollImpulseScale=1.5f
KnockdownImpulseScale=2f //1.5
// ---------------------------------------------
// AI / Navigation
ControllerClass=class'KFAIController_ZedBloat'
BumpDamageType=class'KFDT_NPCBump_Large'
DamageRecoveryTimeHeavy=0.85f
DamageRecoveryTimeMedium=1.0f
// ---------------------------------------------
// Puke Mines
PukeMineProjectileClass=class'KFGameContent.KFProj_BloatPukeMine'
DeathPukeMineRotations(0)=(Pitch=7000,Yaw=10480,Roll=0)
DeathPukeMineRotations(1)=(Pitch=7000,Yaw=32767,Roll=0)
DeathPukeMineRotations(2)=(Pitch=7000,Yaw=-10480,Roll=0)
// ---------------------------------------------
`if(`notdefined(ShippingPC))
DebugRadarTexture=Texture2D'UI_ZEDRadar_TEX.MapIcon_Bloat';
`endif
// ---------------------------------------------
// Spawning
MinSpawnSquadSizeType=EST_Large
bIsBloatClass=true
// 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')
}