1492 lines
50 KiB
Ucode
1492 lines
50 KiB
Ucode
class KFMonsterOS extends KFMonster
|
|
hidecategories(AnimTweaks,DeRes,Force,Gib,Karma,Udamage,UnrealPawn)
|
|
Abstract;
|
|
|
|
#exec OBJ LOAD FILE=KFOldSchoolZeds_Textures.utx
|
|
#exec OBJ LOAD FILE=KFPatch2.utx
|
|
#exec OBJ LOAD FILE=KFOldSchoolZeds_Sounds.uax
|
|
#exec OBJ LOAD FILE=KF_M79Snd.uax
|
|
#exec OBJ LOAD FILE=KFCharacterModelsOldSchool.ukx
|
|
#exec OBJ LOAD FILE=KFBossOld.ukx
|
|
|
|
var KFGibOS HeadStubOS;
|
|
|
|
var class <KFGibOS> HeadStubClassOS;
|
|
var class <KFGibOS> MonsterHeadGibletOS;
|
|
var class <KFGibOS> MonsterThighGibletOS;
|
|
var class <KFGibOS> MonsterArmGibletOS;
|
|
var class <KFGibOS> MonsterLegGibletOS;
|
|
var class <KFGibOS> MonsterTorsoGibletOS;
|
|
var class <KFGibOS> MonsterLowerTorsoGibletOS;
|
|
|
|
var Vector LastBloodHitDirection;
|
|
|
|
// Zed specific vars moved to their respective classes
|
|
|
|
// Enabling these via casting from the mutator "works"
|
|
// But it does not pass to child classes for whatever reason
|
|
// (KFMonsterOS has it enabled, but zeds that spawn won't)
|
|
//var bool bEnableOldHeadshotBehavior;
|
|
//var bool bDisableHealthScaling;
|
|
//var bool bEnableRandomSkins;
|
|
|
|
// contains textures for mixed zed variants
|
|
var protected array<material> MixTexturePool;
|
|
var protected array<material> MixHairPool;
|
|
|
|
var private bool bHeadSpawned;
|
|
|
|
// Zeds shouldn't be doing this at all
|
|
function bool CanGetOutOfWay()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// shut these, since we dont' use them
|
|
simulated function SpawnSeveredGiblet(class<SeveredAppendage> GibClass, Vector Location, Rotator Rotation, float GibPerterbation, rotator SpawnRotation){}
|
|
simulated function SpawnGibs(Rotator HitRotation, float ChunkPerterbation){}
|
|
|
|
simulated function PostBeginPlay()
|
|
{
|
|
super.PostBeginPlay();
|
|
|
|
// N.B. this must be commented at some point!
|
|
// draw some accurate heads (i hope :D)
|
|
// it will be deleted on it's own
|
|
if (!bHeadSpawned)
|
|
{
|
|
spawn(class'DebugHeadHitbox', self);
|
|
bHeadSpawned = true;
|
|
}
|
|
|
|
SpawnClientExtendedZCollision();
|
|
}
|
|
|
|
// Fix client side projectors (i.e. ebr laser dot)
|
|
// NOTE: No special destroy code is needed. EZCollision is already
|
|
// destroyed on any zed that has it (not role-dependent).
|
|
final private function SpawnClientExtendedZCollision()
|
|
{
|
|
local vector AttachPos;
|
|
|
|
// Auth has already created this hitbox.
|
|
if (Role == ROLE_Authority)
|
|
return;
|
|
|
|
if (bUseExtendedCollision && MyExtCollision == none)
|
|
{
|
|
MyExtCollision = spawn(class'ClientExtendedZCollision', self);
|
|
// Slightly smaller version for non auth clients
|
|
MyExtCollision.SetCollisionSize(ColRadius * 0.9f, ColHeight * 0.9f);
|
|
|
|
MyExtCollision.bHardAttach = true;
|
|
AttachPos = Location + (ColOffset >> Rotation);
|
|
MyExtCollision.SetLocation(AttachPos);
|
|
MyExtCollision.SetPhysics(PHYS_None);
|
|
MyExtCollision.SetBase(self);
|
|
SavedExtCollision = MyExtCollision.bCollideActors;
|
|
}
|
|
}
|
|
|
|
function bool FlipOver()
|
|
{
|
|
if( Physics==PHYS_Falling )
|
|
SetPhysics(PHYS_Walking);
|
|
|
|
bShotAnim = true;
|
|
SetAnimAction('KnockDown');
|
|
Acceleration = vect(0, 0, 0);
|
|
Velocity.X = 0;
|
|
Velocity.Y = 0;
|
|
Controller.GoToState('WaitForAnim');
|
|
KFMonsterController(Controller).Focus = none;
|
|
KFMonsterController(Controller).FocalPoint = KFMonsterController(Controller).LastSeenPos;
|
|
KFMonsterController(Controller).bUseFreezeHack = true;
|
|
return true;
|
|
}
|
|
|
|
simulated function DoDamageFX( Name boneName, int Damage, class<DamageType> DamageType, Rotator r )
|
|
{
|
|
local float DismemberProbability;
|
|
local bool bDidSever;
|
|
local bool bExtraGib;
|
|
|
|
if ( FRand() > 0.3f || Damage > 30 || Health <= 0 )
|
|
{
|
|
HitFX[HitFxTicker].damtype = DamageType;
|
|
|
|
if( Health <= 0 )
|
|
{
|
|
switch( boneName )
|
|
{
|
|
case 'Bip01 L Foot':
|
|
boneName = 'Bip01 L Thigh';
|
|
break;
|
|
|
|
case 'Bip01 R Foot':
|
|
boneName = 'Bip01 R Thigh';
|
|
break;
|
|
|
|
case 'Bip01 R Hand':
|
|
boneName = 'rfarm';
|
|
break;
|
|
|
|
case 'Bip01 L Hand':
|
|
boneName = 'Bip01 L Forearm';
|
|
break;
|
|
|
|
case 'Bip01 R Clavicle':
|
|
case 'Bip01 L Clavicle':
|
|
boneName = 'Bip01 Spine';
|
|
break;
|
|
}
|
|
|
|
if( DamageType.default.bAlwaysSevers || (Damage == 1000) )
|
|
{
|
|
HitFX[HitFxTicker].bSever = true;
|
|
bDidSever = true;
|
|
if ( boneName == 'none' )
|
|
{
|
|
boneName = 'Bip01 R Forearm';
|
|
bExtraGib = true;
|
|
}
|
|
}
|
|
else if( (Damage*DamageType.default.GibModifier > 50+120*FRand()-9999999999) && (Damage >= 0) )
|
|
{
|
|
HitFX[HitFxTicker].bSever = true;
|
|
boneName = 'Bip01 R Forearm';
|
|
bExtraGib = true;
|
|
}
|
|
else
|
|
{
|
|
DismemberProbability = Abs( (Health - Damage*DamageType.default.GibModifier) / 130.0f );
|
|
switch( boneName )
|
|
{
|
|
case 'Bip01 L Thigh':
|
|
case 'Bip01 R Thigh':
|
|
case 'Bip01 R Forearm':
|
|
case 'Bip01 L Forearm':
|
|
case 'Bip01 Spine':
|
|
case 'Bip01 Head':
|
|
if( FRand() < DismemberProbability )
|
|
HitFX[HitFxTicker].bSever = true;
|
|
break;
|
|
|
|
|
|
case 'Bip01 Head':
|
|
boneName = 'Bip01 Head';
|
|
case 'Bip01 Head':
|
|
if( FRand() < DismemberProbability * 0.3 )
|
|
{
|
|
HitFX[HitFxTicker].bSever = true;
|
|
if ( FRand() < 0.65 )
|
|
bExtraGib = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( DamageType.default.bNeverSevers || class'GameInfo'.static.UseLowGore() ||
|
|
(Level.Game != none && Level.Game.PreventSever(self, boneName, Damage, DamageType)) )
|
|
{
|
|
HitFX[HitFxTicker].bSever = false;
|
|
bDidSever = false;
|
|
bExtraGib = false;
|
|
}
|
|
|
|
HitFX[HitFxTicker].bone = boneName;
|
|
HitFX[HitFxTicker].rotDir = r;
|
|
HitFxTicker = HitFxTicker + 1;
|
|
|
|
if( HitFxTicker > ArrayCount(HitFX)-1 )
|
|
HitFxTicker = 0;
|
|
|
|
if ( bExtraGib )
|
|
{
|
|
if ( FRand() < 0.25 )
|
|
{
|
|
DoDamageFX('Bip01 L Forearm',1000,DamageType,r);
|
|
DoDamageFX('Bip01 R Forearm',1000,DamageType,r);
|
|
}
|
|
else if ( FRand() < 0.35 )
|
|
DoDamageFX('Bip01 L Thigh',1000,DamageType,r);
|
|
else if ( FRand() < 0.5 )
|
|
DoDamageFX('Bip01 Head',1000,DamageType,r);
|
|
else
|
|
DoDamageFX('Bip01 Head',1000,DamageType,r);
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
|
|
{
|
|
local float frame, rate;
|
|
local name seq;
|
|
local LavaDeath LD;
|
|
local MiscEmmiter BE;
|
|
|
|
if(bDecapitated)
|
|
{
|
|
HideBone('bip01 head');
|
|
|
|
if (HeadStubOS == none && HeadStubClassOS != none)
|
|
{
|
|
HeadStubOS = Spawn(HeadStubClassOS,self);
|
|
AttachToBone( HeadStubOS,'Bip01 head');
|
|
}
|
|
}
|
|
|
|
if (Gored>0)
|
|
{
|
|
if (Gored == 1)
|
|
HideBone('Bip01 L Clavicle');
|
|
else if (Gored == 2)
|
|
HideBone('Bip01 R Clavicle');
|
|
else if (Gored == 3)
|
|
HideBone('Bip01 Spine2');
|
|
else if (Gored == 4)
|
|
HideBone('Bip01 Spine1');
|
|
else if (Gored == 5)
|
|
{
|
|
HideBone('Bip01');
|
|
bHidden = true;
|
|
}
|
|
}
|
|
|
|
AmbientSound = none;
|
|
bCanTeleport = false;
|
|
bReplicateMovement = false;
|
|
bTearOff = true;
|
|
bPlayedDeath = true;
|
|
StopBurnFX();
|
|
|
|
if (CurrentCombo != none)
|
|
CurrentCombo.Destroy();
|
|
|
|
HitDamageType = DamageType;
|
|
TakeHitLocation = HitLoc;
|
|
|
|
bSTUNNED = false;
|
|
bMovable = true;
|
|
|
|
if ( class<DamTypeBurned>(DamageType) != none || class<DamTypeFlamethrower>(DamageType) != none )
|
|
{
|
|
ZombieCrispUp();
|
|
}
|
|
|
|
ProcessHitFX() ;
|
|
|
|
if ( DamageType != none )
|
|
{
|
|
if ( DamageType.default.bSkeletize )
|
|
{
|
|
SetOverlayMaterial(DamageType.default.DamageOverlayMaterial, 4.0, true);
|
|
if (!bSkeletized)
|
|
{
|
|
if ( (Level.NetMode != NM_DedicatedServer) && (SkeletonMesh != none) )
|
|
{
|
|
if ( DamageType.default.bLeaveBodyEffect )
|
|
{
|
|
BE = spawn(class'MiscEmmiter',self);
|
|
|
|
if ( BE != none )
|
|
{
|
|
BE.DamageType = DamageType;
|
|
BE.HitLoc = HitLoc;
|
|
bFrozenBody = true;
|
|
}
|
|
}
|
|
|
|
GetAnimParams( 0, seq, frame, rate );
|
|
LinkMesh(SkeletonMesh, true);
|
|
Skins.Length = 0;
|
|
PlayAnim(seq, 0, 0);
|
|
SetAnimFrame(frame);
|
|
}
|
|
|
|
if (Physics == PHYS_Walking)
|
|
Velocity = Vect(0,0,0);
|
|
|
|
SetTearOffMomemtum(GetTearOffMomemtum() * 0.25);
|
|
bSkeletized = true;
|
|
|
|
if ( (Level.NetMode != NM_DedicatedServer) && (DamageType == class'FellLava') )
|
|
{
|
|
LD = spawn(class'LavaDeath', , , Location + vect(0, 0, 10), Rotation );
|
|
|
|
if ( LD != none )
|
|
LD.SetBase(self);
|
|
|
|
PlaySound( sound'KFOldSchoolZeds_Sounds.BExplosion5', SLOT_None, 1.5*TransientSoundVolume );
|
|
}
|
|
}
|
|
}
|
|
else if ( DamageType.default.DeathOverlayMaterial != none )
|
|
SetOverlayMaterial(DamageType.default.DeathOverlayMaterial, DamageType.default.DeathOverlayTime, true);
|
|
else if ( (DamageType.default.DamageOverlayMaterial != none) && (Level.DetailMode != DM_Low) && !Level.bDropDetail )
|
|
SetOverlayMaterial(DamageType.default.DamageOverlayMaterial, 2*DamageType.default.DamageOverlayTime, true);
|
|
}
|
|
|
|
|
|
AnimBlendParams(1, 0.0);
|
|
FireState = FS_None;
|
|
|
|
GotoState('ZombieDyingOS');
|
|
|
|
if ( BE != none )
|
|
return;
|
|
|
|
PlayDyingAnimation(DamageType, HitLoc);
|
|
}
|
|
|
|
State ZombieDyingDecay extends ZombieDying
|
|
{
|
|
ignores AnimEnd, Trigger, Bump, HitWall, HeadVolumeChange, PhysicsVolumeChange, Falling, BreathTimer, Died, RangedAttack, TakeDamage;
|
|
}
|
|
|
|
//Until I can fix Decay not being enabled, I'll make corpses decay & not eat bullets here
|
|
State ZombieDyingOS extends Dying
|
|
{
|
|
ignores AnimEnd, Trigger, Bump, HitWall, HeadVolumeChange, PhysicsVolumeChange, Falling, BreathTimer, Died, RangedAttack;
|
|
|
|
function bool CanGetOutOfWay()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
simulated function Landed(vector HitNormal)
|
|
{
|
|
SetPhysics(PHYS_None);
|
|
SetCollision(false, false, false);
|
|
|
|
if ( !IsAnimating(0) )
|
|
LandThump();
|
|
|
|
super.Landed(HitNormal);
|
|
}
|
|
|
|
simulated function Timer()
|
|
{
|
|
if( Level.NetMode==NM_DedicatedServer )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
|
|
if( Physics!=PHYS_None )
|
|
{
|
|
if( VSize(Velocity)>10 )
|
|
{
|
|
SetTimer(1,false);
|
|
return;
|
|
}
|
|
|
|
//This just spams the clients log, get rid of it
|
|
//Disable('TakeDamage');
|
|
SetPhysics(PHYS_None);
|
|
SetTimer(30,false);
|
|
|
|
if(PlayerShadow != none)
|
|
PlayerShadow.bShadowActive = false;
|
|
}
|
|
else if( (Level.TimeSeconds-LastRenderTime)>40 || Level.bDropDetail )
|
|
Destroy();
|
|
else SetTimer(5,false);
|
|
}
|
|
|
|
simulated function BeginState()
|
|
{
|
|
if( Controller!=none )
|
|
Controller.Destroy();
|
|
|
|
if( Level.NetMode==NM_DedicatedServer )
|
|
SetTimer(1,false);
|
|
|
|
//Don't eat bullets or block shit
|
|
bBlockHitPointTraces=false;
|
|
bBlockPlayers=false;
|
|
bBlockProjectiles=false;
|
|
SetTimer(5,false);
|
|
}
|
|
}
|
|
|
|
simulated function ProcessHitFX()
|
|
{
|
|
local Coords boneCoords;
|
|
local class<xEmitter> HitEffects[4];
|
|
local int i,j;
|
|
local float GibPerterbation;
|
|
|
|
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
|
|
{
|
|
SimHitFxTicker = HitFxTicker;
|
|
return;
|
|
}
|
|
|
|
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
|
|
{
|
|
j++;
|
|
if ( j > 30 )
|
|
{
|
|
SimHitFxTicker = HitFxTicker;
|
|
return;
|
|
}
|
|
|
|
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
|
|
continue;
|
|
|
|
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
|
|
|
|
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore())
|
|
{
|
|
AttachEffect( GibGroupClass.static.GetBloodEmitClass(), HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
|
|
|
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
|
|
|
|
if( !PhysicsVolume.bWaterVolume )
|
|
{
|
|
for( i = 0; i < ArrayCount(HitEffects); i++ )
|
|
{
|
|
if( HitEffects[i] == none )
|
|
continue;
|
|
|
|
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( HitFX[SimHitFxTicker].bSever )
|
|
{
|
|
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
|
|
bFlaming = HitFX[SimHitFxTicker].DamType.default.bFlaming;
|
|
|
|
switch( HitFX[SimHitFxTicker].bone )
|
|
{
|
|
case 'Bip01 L Thigh':
|
|
case 'Bip01 R Thigh':
|
|
Spawn(class'BrainSplashOS',,,boneCoords.Origin,self.Rotation);
|
|
SpawnGiblet( GetGibClass(EGT_Calf), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
|
|
SpawnGiblet( GetGibClass(EGT_Calf), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
|
|
PlaySound(sound'KFOldSchoolZeds_Sounds.Shared.Gibbing_Sound1', SLOT_Misc,255);
|
|
GibCountCalf -= 2;
|
|
break;
|
|
case 'Bip01 R Forearm':
|
|
case 'Bip01 L Forearm':
|
|
Spawn(class'BrainSplashOS',,,boneCoords.Origin,self.Rotation);
|
|
SpawnGiblet( GetGibClass(EGT_UpperArm), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
|
|
SpawnGiblet( GetGibClass(EGT_Forearm), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
|
|
PlaySound(sound'KFOldSchoolZeds_Sounds.Shared.Gibbing_Sound2', SLOT_Misc,255);
|
|
GibCountForearm--;
|
|
GibCountUpperArm--;
|
|
break;
|
|
case 'Bip01 Head':
|
|
Spawn(class'BrainSplashOS',,,boneCoords.Origin,self.Rotation);
|
|
SpawnGiblet( GetGibClass(EGT_Head), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
|
|
PlaySound(sound'KFOldSchoolZeds_Sounds.Shared.Gibbing_Sound2', SLOT_Misc,255);
|
|
GibCountTorso--;
|
|
break;
|
|
case 'Bip01 Spine':
|
|
case 'Bip01 Spine':
|
|
SpawnGiblet( GetGibClass(EGT_Torso), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
|
|
PlaySound(sound'KFOldSchoolZeds_Sounds.Shared.Gibbing_Sound3', SLOT_Misc,255);
|
|
GibCountTorso--;
|
|
bGibbed = true;
|
|
while( GibCountHead-- > 0 )
|
|
SpawnGiblet( GetGibClass(EGT_Head), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
|
|
while( GibCountForearm-- > 0 )
|
|
SpawnGiblet( GetGibClass(EGT_UpperArm), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
|
|
while( GibCountUpperArm-- > 0 )
|
|
SpawnGiblet( GetGibClass(EGT_Forearm), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
|
|
if ( !bFlaming && !Level.bDropDetail && (Level.DetailMode != DM_Low) && PlayerCanSeeMe() )
|
|
{
|
|
GibPerterbation = FMin(1.0, 1.5 * GibPerterbation);
|
|
PlaySound(sound'KFOldSchoolZeds_Sounds.Shared.Gibbing_Sound4', SLOT_Misc,255);
|
|
SpawnGiblet( GetGibClass(EGT_Calf), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
|
|
SpawnGiblet( GetGibClass(EGT_Calf), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
|
|
SpawnGiblet( GetGibClass(EGT_UpperArm), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
|
|
SpawnGiblet( GetGibClass(EGT_Forearm), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!bDecapitated)
|
|
HideBone(HitFX[SimHitFxTicker].bone);
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function DecapFX( Vector DecapLocation, Rotator DecapRotation, bool bSpawnDetachedHead, optional bool bNoBrainBits )
|
|
{
|
|
local float GibPerterbation;
|
|
local BrainSplashOS SplatExplosion;
|
|
|
|
if ( class'GameInfo'.static.UseLowGore() )
|
|
{
|
|
CuteDecapFX();
|
|
|
|
return;
|
|
}
|
|
|
|
bNoBrainBitEmitter = bNoBrainBits;
|
|
|
|
GibPerterbation = 0.060000;
|
|
|
|
if(bSpawnDetachedHead)
|
|
{
|
|
SpecialHideHead();
|
|
}
|
|
else
|
|
{
|
|
HideBone(HeadBone);
|
|
}
|
|
|
|
|
|
if ( !bSpawnDetachedHead && !bNoBrainBits && EffectIsRelevant(DecapLocation,false) )
|
|
{
|
|
SpawnGiblet( class'KFGibBrainOS',DecapLocation, self.Rotation, GibPerterbation) ;
|
|
SpawnGiblet( class'KFGibBrainbOS',DecapLocation, self.Rotation, GibPerterbation) ;
|
|
SpawnGiblet( class'KFGibBrainOS',DecapLocation, self.Rotation, GibPerterbation) ;
|
|
}
|
|
SplatExplosion = Spawn(class 'BrainSplashOS',self,, DecapLocation );
|
|
}
|
|
|
|
// swapped SeveredHead with HeadStubOS
|
|
simulated function SpecialHideHead()
|
|
{
|
|
local int BoneScaleSlot;
|
|
local coords boneCoords;
|
|
|
|
if (HeadStubOS == none && HeadStubClassOS != none)
|
|
{
|
|
boneScaleSlot = 4;
|
|
HeadStubOS = Spawn(HeadStubClassOS,self);
|
|
boneCoords = GetBoneCoords('Bip01 Head');
|
|
AttachToBone(HeadStubOS, 'Bip01 Head');
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
SetBoneScale(BoneScaleSlot, 0.0, 'Bip01 Head');
|
|
}
|
|
|
|
simulated function CuteDecapFX()
|
|
{
|
|
local int LeftRight;
|
|
|
|
LeftRight = 1;
|
|
|
|
if ( rand(10) > 5 )
|
|
{
|
|
LeftRight = -1;
|
|
}
|
|
|
|
NeckRot.Yaw = -clamp(rand(24000), 14000, 24000);
|
|
NeckRot.Roll = LeftRight * clamp(rand(8000), 2000, 8000);
|
|
NeckRot.Pitch = LeftRight * clamp(rand(12000), 2000, 12000);
|
|
// was 'neck'
|
|
SetBoneRotation('Bip01 Neck', NeckRot);
|
|
RemoveHead();
|
|
}
|
|
|
|
simulated function HideBone(name boneName)
|
|
{
|
|
local int BoneScaleSlot;
|
|
|
|
if( boneName == 'lthigh' )
|
|
boneScaleSlot = 0;
|
|
else if ( boneName == 'rthigh' )
|
|
boneScaleSlot = 1;
|
|
else if( boneName == 'rfarm' )
|
|
boneScaleSlot = 2;
|
|
else if ( boneName == 'lfarm' )
|
|
boneScaleSlot = 3;
|
|
else if ( boneName == 'head' )
|
|
boneScaleSlot = 4;
|
|
else if ( boneName == 'spine' )
|
|
boneScaleSlot = 5;
|
|
|
|
SetBoneScale(BoneScaleSlot, 0.0, BoneName);
|
|
}
|
|
|
|
simulated function Tick(float DeltaTime)
|
|
{
|
|
local PlayerController P;
|
|
local float DistSquared;
|
|
local float GibPerterbation;
|
|
local BrainSplashOS SplatExplosion;
|
|
local Vector SplatLocation;
|
|
local Rotator R;
|
|
local GibExplosionOS GibbedExplosion;
|
|
|
|
if (AnimAction == 'KnockDown')
|
|
{
|
|
Acceleration = vect(0,0,0);
|
|
Velocity = vect(0,0,0);
|
|
}
|
|
|
|
|
|
if( bDestroyNextTick && TimeSetDestroyNextTickTime < Level.TimeSeconds )
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
if ( Level.NetMode != NM_Client && CanSpeedAdjust() )
|
|
{
|
|
if ( Level.NetMode == NM_Standalone )
|
|
{
|
|
if ( Level.TimeSeconds - LastRenderTime > 5.0 )
|
|
{
|
|
P = Level.GetLocalPlayerController();
|
|
|
|
if ( P != none && P.Pawn != none )
|
|
{
|
|
if ( Level.TimeSeconds - LastViewCheckTime > 1.0 )
|
|
{
|
|
LastViewCheckTime = Level.TimeSeconds;
|
|
DistSquared = VSizeSquared(P.Pawn.Location - Location);
|
|
if( (!P.Pawn.Region.Zone.bDistanceFog || (DistSquared < Square(P.Pawn.Region.Zone.DistanceFogEnd))) &&
|
|
FastTrace(Location + EyePosition(), P.Pawn.Location + P.Pawn.EyePosition()) )
|
|
{
|
|
LastSeenOrRelevantTime = Level.TimeSeconds;
|
|
SetGroundSpeed(GetOriginalGroundSpeed());
|
|
}
|
|
else
|
|
{
|
|
SetGroundSpeed(default.GroundSpeed * (HiddenGroundSpeed / default.GroundSpeed));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LastSeenOrRelevantTime = Level.TimeSeconds;
|
|
SetGroundSpeed(GetOriginalGroundSpeed());
|
|
}
|
|
}
|
|
else if ( Level.NetMode == NM_DedicatedServer )
|
|
{
|
|
if ( Level.TimeSeconds - LastReplicateTime > 0.5 )
|
|
{
|
|
SetGroundSpeed(default.GroundSpeed * (300.0 / default.GroundSpeed));
|
|
}
|
|
else
|
|
{
|
|
LastSeenOrRelevantTime = Level.TimeSeconds;
|
|
SetGroundSpeed(GetOriginalGroundSpeed());
|
|
}
|
|
}
|
|
else if ( Level.NetMode == NM_ListenServer )
|
|
{
|
|
if ( Level.TimeSeconds - LastReplicateTime > 0.5 && Level.TimeSeconds - LastRenderTime > 5.0 )
|
|
{
|
|
P = Level.GetLocalPlayerController();
|
|
|
|
if ( P != none && P.Pawn != none )
|
|
{
|
|
if ( Level.TimeSeconds - LastViewCheckTime > 1.0 )
|
|
{
|
|
LastViewCheckTime = Level.TimeSeconds;
|
|
DistSquared = VSizeSquared(P.Pawn.Location - Location);
|
|
|
|
if ( (!P.Pawn.Region.Zone.bDistanceFog || (DistSquared < Square(P.Pawn.Region.Zone.DistanceFogEnd))) &&
|
|
FastTrace(Location + EyePosition(), P.Pawn.Location + P.Pawn.EyePosition()) )
|
|
{
|
|
LastSeenOrRelevantTime = Level.TimeSeconds;
|
|
SetGroundSpeed(GetOriginalGroundSpeed());
|
|
}
|
|
else
|
|
{
|
|
SetGroundSpeed(default.GroundSpeed * (300.0 / default.GroundSpeed));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LastSeenOrRelevantTime = Level.TimeSeconds;
|
|
SetGroundSpeed(GetOriginalGroundSpeed());
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bResetAnimAct && ResetAnimActTime<Level.TimeSeconds )
|
|
{
|
|
AnimAction = '';
|
|
bResetAnimAct = false;
|
|
}
|
|
|
|
if ( Controller != none )
|
|
{
|
|
LookTarget = Controller.Enemy;
|
|
}
|
|
|
|
if ( Role == ROLE_Authority && bDecapitated )
|
|
{
|
|
if ( BleedOutTime > 0 && Level.TimeSeconds - BleedOutTime >= 0 )
|
|
{
|
|
Died(LastDamagedBy.Controller,class'DamTypeBleedOut',Location);
|
|
BleedOutTime=0;
|
|
}
|
|
|
|
}
|
|
|
|
if ( Level.NetMode!=NM_DedicatedServer )
|
|
{
|
|
TickFX(DeltaTime);
|
|
if( LookTarget!=none && Health >= 1 )
|
|
{
|
|
R = Normalize(rotator(LookTarget.Location-Location)-Rotation);
|
|
R.Pitch = 0;
|
|
R.Roll = 0;
|
|
if( R.Yaw>18000 )
|
|
R.Yaw = 18000;
|
|
else if( R.Yaw<-18000 )
|
|
R.Yaw = -18000;
|
|
R.Pitch = R.Yaw;
|
|
R.Yaw = 0;
|
|
SetBoneDirection('Bip01 Head',R*-1.8,,0.5);
|
|
}
|
|
else SetBoneDirection('Bip01 Head',rot(0,0,0),,0.5);
|
|
|
|
if ( bBurnified && !bBurnApplied )
|
|
{
|
|
if ( !bGibbed )
|
|
{
|
|
StartBurnFX();
|
|
}
|
|
}
|
|
else if ( !bBurnified && bBurnApplied )
|
|
{
|
|
StopBurnFX();
|
|
}
|
|
|
|
if ( bAshen && Level.NetMode == NM_Client && !class'GameInfo'.static.UseLowGore() )
|
|
{
|
|
ZombieCrispUp();
|
|
bAshen = false;
|
|
}
|
|
|
|
if(bDecapitated && !bPlayBrainSplash )
|
|
{
|
|
SplatLocation = self.Location;
|
|
SplatLocation.z += CollisionHeight;
|
|
GibPerterbation = 0.060000;
|
|
HideBone('bip01 head');
|
|
if (HeadStubOS == none && HeadStubClassOS != none)
|
|
{
|
|
HeadStubOS = Spawn(HeadStubClassOS,self,'',Location);
|
|
AttachToBone( HeadStubOS,'Bip01 head');
|
|
}
|
|
if ( EffectIsRelevant(Location,false) )
|
|
{
|
|
SpawnGiblet( class'KFGibBrainOS',SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
SpawnGiblet( class'KFGibBrainbOS',SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
SpawnGiblet( class'KFGibBrainOS',SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
}
|
|
SplatExplosion = Spawn(class 'BrainSplashOS',self,, SplatLocation );
|
|
bPlayBrainSplash = true;
|
|
}
|
|
if (Gored>0 && !bPlayGoreSplash)
|
|
{
|
|
SplatLocation = self.Location;
|
|
SplatLocation.z += (CollisionHeight - (CollisionHeight * 0.5));
|
|
GibPerterbation = 0.060000;
|
|
|
|
if (Gored == 1)
|
|
HideBone('Bip01 L Clavicle');
|
|
else if (Gored == 2)
|
|
HideBone('Bip01 R Clavicle');
|
|
else if (Gored == 3)
|
|
{
|
|
HideBone('Bip01 Spine2');
|
|
|
|
if (GoredMat != none)
|
|
Skins[0] = GoredMat;
|
|
}
|
|
else if (Gored == 4)
|
|
HideBone('Bip01 Spine1');
|
|
|
|
|
|
if ( EffectIsRelevant(Location,false) && Gored < 5 )
|
|
{
|
|
Spawn(class'BrainSplashOS',,,SplatLocation,self.Rotation);
|
|
SpawnGiblet( class'KFGibBrainOS',SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
SpawnGiblet( class'KFGibBrainbOS',SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
SpawnGiblet( class'KFGibBrainOS',SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
}
|
|
else if ( EffectIsRelevant(Location,false) && Gored == 5 )
|
|
{
|
|
|
|
Spawn(class'BodySplashOS',,,SplatLocation,self.Rotation);
|
|
|
|
if (!bDecapitated)
|
|
SpawnGiblet(MonsterHeadGibletOS,SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
|
|
SpawnGiblet(MonsterTorsoGibletOS,SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
SpawnGiblet(MonsterLowerTorsoGibletOS,SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
|
|
SpawnGiblet(MonsterArmGibletOS,SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
SpawnGiblet(MonsterArmGibletOS,SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
|
|
SpawnGiblet(MonsterThighGibletOS,SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
SpawnGiblet(MonsterThighGibletOS,SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
|
|
SpawnGiblet(MonsterLegGibletOS,SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
SpawnGiblet(MonsterLegGibletOS,SplatLocation, self.Rotation, GibPerterbation ) ;
|
|
|
|
GibbedExplosion = Spawn(class'GibExplosionOS',self,, SplatLocation );
|
|
|
|
}
|
|
|
|
SplatExplosion = Spawn(class 'BrainSplashOS',self,, SplatLocation );
|
|
bPlayGoreSplash = true;
|
|
}
|
|
}
|
|
|
|
if ( DECAP )
|
|
{
|
|
if ( Level.TimeSeconds > (DecapTime + 2.0) && Controller != none )
|
|
{
|
|
DECAP = false;
|
|
MonsterController(Controller).ExecuteWhatToDoNext();
|
|
}
|
|
}
|
|
|
|
if ( BileCount > 0 && NextBileTime<level.TimeSeconds )
|
|
{
|
|
--BileCount;
|
|
NextBileTime+=BileFrequency;
|
|
TakeBileDamage();
|
|
}
|
|
|
|
if( bZapped && Role == ROLE_Authority )
|
|
{
|
|
RemainingZap -= DeltaTime;
|
|
|
|
if( RemainingZap <= 0 )
|
|
{
|
|
RemainingZap = 0;
|
|
bZapped = false;
|
|
ZappedBy = none;
|
|
ZapThreshold *= ZapResistanceScale;
|
|
}
|
|
}
|
|
|
|
if( !bZapped && TotalZap > 0 && ((Level.TimeSeconds - LastZapTime) > 0.1) )
|
|
{
|
|
TotalZap -= DeltaTime;
|
|
}
|
|
|
|
if( bZapped != bOldZapped )
|
|
{
|
|
if( bZapped )
|
|
{
|
|
SetZappedBehavior();
|
|
}
|
|
else
|
|
{
|
|
UnSetZappedBehavior();
|
|
}
|
|
|
|
bOldZapped = bZapped;
|
|
}
|
|
|
|
if( bHarpoonStunned != bOldHarpoonStunned )
|
|
{
|
|
if( bHarpoonStunned )
|
|
{
|
|
SetBurningBehavior();
|
|
}
|
|
else
|
|
{
|
|
UnSetBurningBehavior();
|
|
}
|
|
|
|
bOldHarpoonStunned = bHarpoonStunned;
|
|
}
|
|
}
|
|
|
|
//Slow rage from burn damage fix
|
|
function TakeFireDamage(int Damage,pawn Instigator)
|
|
{
|
|
local Vector DummyHitLoc,DummyMomentum;
|
|
|
|
TakeDamage(Damage, BurnInstigator, DummyHitLoc, DummyMomentum, FireDamageClass);
|
|
|
|
if ( BurnDown > 0 )
|
|
{
|
|
// Decrement the number of FireDamage calls left before our Zombie is extinguished :)
|
|
BurnDown --;
|
|
}
|
|
|
|
// Melt em' :)
|
|
if ( BurnDown < CrispUpThreshhold )
|
|
{
|
|
ZombieCrispUp();
|
|
}
|
|
|
|
if ( BurnDown == 0 )
|
|
{
|
|
bBurnified = false;
|
|
if( !bZapped )
|
|
{
|
|
SetGroundSpeed(GetOriginalGroundSpeed());
|
|
}
|
|
}
|
|
}
|
|
|
|
function DoorAttack(Actor A)
|
|
{
|
|
if ( bShotAnim || Physics == PHYS_Swimming)
|
|
return;
|
|
else if ( CanAttack(A) )
|
|
{
|
|
bShotAnim = true;
|
|
SetAnimAction('Claw');
|
|
PlaySound(sound'Claw2s', SLOT_None);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
function ClawDamageTarget()
|
|
{
|
|
local vector PushDir;
|
|
local float UsedMeleeDamage;
|
|
|
|
if( MeleeDamage > 1 )
|
|
{
|
|
UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
|
|
}
|
|
else
|
|
{
|
|
UsedMeleeDamage = MeleeDamage;
|
|
}
|
|
|
|
if(Controller!=none && Controller.Target!=none)
|
|
PushDir = (damageForce * Normal(Controller.Target.Location - Location));
|
|
else PushDir = damageForce * vector(Rotation);
|
|
|
|
if ( MeleeDamageTarget(UsedMeleeDamage, PushDir) )
|
|
PlayZombieAttackHitSound();
|
|
}
|
|
|
|
function PlayZombieAttackHitSound()
|
|
{
|
|
local int MeleeAttackSounds;
|
|
|
|
MeleeAttackSounds = rand(4);
|
|
|
|
switch(MeleeAttackSounds)
|
|
{
|
|
case 0:
|
|
PlaySound(sound'KFOldSchoolZeds_Sounds.Melee_Hit1', SLOT_Interact);
|
|
break;
|
|
case 1:
|
|
PlaySound(sound'KFOldSchoolZeds_Sounds.Melee_Hit2', SLOT_Interact);
|
|
break;
|
|
case 2:
|
|
PlaySound(sound'KFOldSchoolZeds_Sounds.Melee_Hit3', SLOT_Interact);
|
|
break;
|
|
case 3:
|
|
PlaySound(sound'KFOldSchoolZeds_Sounds.Melee_Hit4', SLOT_Interact);
|
|
break;
|
|
}
|
|
}
|
|
|
|
function BodyPartRemoval(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<DamageType> damageType)
|
|
{
|
|
local float Threshold;
|
|
|
|
if ( Health - Damage < 0)
|
|
{
|
|
if( DamageType.name != 'DamTypeShotgun' && DamageType.name != 'DamTypeDBShotgun' && DamageType.name != 'DamTypeAxe')
|
|
Threshold = 0.5;
|
|
else
|
|
Threshold = 0.25;
|
|
|
|
if ((Health - Damage) < (0-(Threshold * HealthMax)) && (Health - Damage) > (0-((Threshold * 1.2) * HealthMax)) )
|
|
Gored = rand(4)+1;
|
|
else
|
|
if ((Health - Damage) <= (0-(1.0 * HealthMax)))
|
|
{
|
|
if( damageType == class 'DamTypeFrag' || damageType == class 'DamTypeLAW' ||
|
|
damageType == class 'DamTypeM79Grenade' || damageType == class 'DamTypeM203Grenade' ||
|
|
damageType == class 'DamTypePipeBomb' || damageType == class 'DamTypeSealSquealExplosion')
|
|
Gored = 5;
|
|
}
|
|
if(Gored > 0)
|
|
{
|
|
if( instigatedBy!=none )
|
|
Died(instigatedBy.Controller,damageType,hitlocation);
|
|
else Died(none,damageType,hitlocation);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<DamageType> damageType, optional int HitIndex )
|
|
{
|
|
local bool bIsHeadshot;
|
|
local KFPlayerReplicationInfo KFPRI;
|
|
local float HeadShotCheckScale;
|
|
|
|
LastDamagedBy = instigatedBy;
|
|
LastDamagedByType = damageType;
|
|
HitMomentum = VSize(momentum);
|
|
LastHitLocation = hitlocation;
|
|
LastMomentum = momentum;
|
|
|
|
if ( KFPawn(instigatedBy) != none && instigatedBy.PlayerReplicationInfo != none )
|
|
{
|
|
KFPRI = KFPlayerReplicationInfo(instigatedBy.PlayerReplicationInfo);
|
|
}
|
|
|
|
// Scale damage if the Zed has been zapped
|
|
if( bZapped )
|
|
{
|
|
Damage *= ZappedDamageMod;
|
|
}
|
|
|
|
// Zeds and fire dont mix.
|
|
if ( class<KFWeaponDamageType>(damageType) != none && class<KFWeaponDamageType>(damageType).default.bDealBurningDamage )
|
|
{
|
|
if( BurnDown<=0 || Damage > LastBurnDamage )
|
|
{
|
|
// LastBurnDamage variable is storing last burn damage (unperked) received,
|
|
// which will be used to make additional damage per every burn tick (second).
|
|
LastBurnDamage = Damage;
|
|
|
|
// FireDamageClass variable stores damage type, which started zed's burning
|
|
// and will be passed to this function again every next burn tick (as damageType argument)
|
|
if ( class<DamTypeTrenchgun>(damageType) != none ||
|
|
class<DamTypeFlareRevolver>(damageType) != none ||
|
|
class<DamTypeMAC10MPInc>(damageType) != none)
|
|
{
|
|
FireDamageClass = damageType;
|
|
}
|
|
else
|
|
{
|
|
FireDamageClass = class'DamTypeFlamethrower';
|
|
}
|
|
}
|
|
|
|
if ( class<DamTypeMAC10MPInc>(damageType) == none )
|
|
{
|
|
Damage *= 1.5; // Increase burn damage 1.5 times, except MAC10.
|
|
}
|
|
|
|
// BurnDown variable indicates how many ticks are remaining for zed to burn.
|
|
// It is 0, when zed isn't burning (or stopped burning).
|
|
// So all the code below will be executed only, if zed isn't already burning
|
|
if( BurnDown<=0 )
|
|
{
|
|
if( HeatAmount>4 || Damage >= 15 )
|
|
{
|
|
bBurnified = true;
|
|
BurnDown = 10; // Inits burn tick count to 10
|
|
SetGroundSpeed(GroundSpeed *= 0.80); // Lowers movement speed by 20%
|
|
BurnInstigator = instigatedBy;
|
|
SetTimer(1.0,false); // Sets timer function to be executed each second
|
|
}
|
|
else HeatAmount++;
|
|
}
|
|
}
|
|
|
|
if ( !bDecapitated && class<KFWeaponDamageType>(damageType)!=none &&
|
|
class<KFWeaponDamageType>(damageType).default.bCheckForHeadShots )
|
|
{
|
|
HeadShotCheckScale = 1.0;
|
|
|
|
// Do larger headshot checks if it is a melee attach
|
|
if( class<DamTypeMelee>(damageType) != none )
|
|
{
|
|
HeadShotCheckScale *= 1.25;
|
|
}
|
|
|
|
bIsHeadShot = IsHeadShot(hitlocation, normal(momentum), HeadShotCheckScale);
|
|
bLaserSightedEBRM14Headshotted = bIsHeadshot && M14EBRBattleRifle(instigatedBy.Weapon) != none && M14EBRBattleRifle(instigatedBy.Weapon).bLaserActive;
|
|
}
|
|
else
|
|
{
|
|
bLaserSightedEBRM14Headshotted = bLaserSightedEBRM14Headshotted && bDecapitated;
|
|
}
|
|
|
|
if ( KFPRI != none )
|
|
{
|
|
if ( KFPRI.ClientVeteranSkill != none )
|
|
{
|
|
Damage = KFPRI.ClientVeteranSkill.Static.AddDamage(KFPRI, self, KFPawn(instigatedBy), Damage, DamageType);
|
|
}
|
|
}
|
|
|
|
if ( damageType != none && LastDamagedBy.IsPlayerPawn() && LastDamagedBy.Controller != none )
|
|
{
|
|
if ( KFMonsterController(Controller) != none )
|
|
{
|
|
KFMonsterController(Controller).AddKillAssistant(LastDamagedBy.Controller, FMin(Health, Damage));
|
|
}
|
|
}
|
|
|
|
if ( (bDecapitated || bIsHeadShot) && class<DamTypeBurned>(DamageType) == none && class<DamTypeFlamethrower>(DamageType) == none )
|
|
{
|
|
if(class<KFWeaponDamageType>(damageType)!=none)
|
|
Damage = Damage * class<KFWeaponDamageType>(damageType).default.HeadShotDamageMult;
|
|
|
|
if ( class<DamTypeMelee>(damageType) == none && KFPRI != none &&
|
|
KFPRI.ClientVeteranSkill != none )
|
|
{
|
|
Damage = float(Damage) * KFPRI.ClientVeteranSkill.Static.GetHeadShotDamMulti(KFPRI, KFPawn(instigatedBy), DamageType);
|
|
}
|
|
|
|
LastDamageAmount = Damage;
|
|
|
|
if( !bDecapitated )
|
|
{
|
|
if( bIsHeadShot )
|
|
{
|
|
// Play a sound when someone gets a headshot TODO: Put in the real sound here
|
|
if( bIsHeadShot )
|
|
{
|
|
PlaySound(sound'KF_EnemyGlobalSndTwo.Impact_Skull', SLOT_None,2.0,true,500);
|
|
}
|
|
HeadHealth -= LastDamageAmount;
|
|
if( HeadHealth <= 0 || Damage > Health )
|
|
{
|
|
RemoveHead();
|
|
}
|
|
}
|
|
|
|
// Award headshot here, not when zombie died.
|
|
if( bDecapitated && Class<KFWeaponDamageType>(damageType) != none && instigatedBy != none && KFPlayerController(instigatedBy.Controller) != none )
|
|
{
|
|
bLaserSightedEBRM14Headshotted = M14EBRBattleRifle(instigatedBy.Weapon) != none && M14EBRBattleRifle(instigatedBy.Weapon).bLaserActive;
|
|
Class<KFWeaponDamageType>(damageType).Static.ScoredHeadshot(KFSteamStatsAndAchievements(PlayerController(instigatedBy.Controller).SteamStatsAndAchievements), self.Class, bLaserSightedEBRM14Headshotted);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Client check for Gore FX
|
|
BodyPartRemoval(Damage,instigatedBy,hitlocation,momentum,damageType);
|
|
|
|
if( Health-Damage > 0 && DamageType!=class'DamTypeFrag' && DamageType!=class'DamTypePipeBomb'
|
|
&& DamageType!=class'DamTypeM79Grenade' && DamageType!=class'DamTypeM32Grenade'
|
|
&& DamageType!=class'DamTypeM203Grenade' && DamageType!=class'DamTypeDwarfAxe'
|
|
&& DamageType!=class'DamTypeSPGrenade' && DamageType!=class'DamTypeSealSquealExplosion'
|
|
&& DamageType!=class'DamTypeSeekerSixRocket')
|
|
{
|
|
Momentum = vect(0,0,0);
|
|
}
|
|
|
|
if(class<DamTypeVomit>(DamageType)!=none) // Same rules apply to zombies as players.
|
|
{
|
|
BileCount=7;
|
|
BileInstigator = instigatedBy;
|
|
LastBileDamagedByType=class<DamTypeVomit>(DamageType);
|
|
if(NextBileTime< Level.TimeSeconds )
|
|
NextBileTime = Level.TimeSeconds+BileFrequency;
|
|
}
|
|
|
|
if ( KFPRI != none && Health-Damage <= 0 && KFPRI.ClientVeteranSkill != none && KFPRI.ClientVeteranSkill.static.KilledShouldExplode(KFPRI, KFPawn(instigatedBy)) )
|
|
{
|
|
Super(pawn).takeDamage(Damage + 600, instigatedBy, hitLocation, momentum, damageType);
|
|
HurtRadius(500, 1000, class'DamTypeFrag', 100000, Location);
|
|
}
|
|
else
|
|
{
|
|
Super(pawn).takeDamage(Damage, instigatedBy, hitLocation, momentum, damageType);
|
|
}
|
|
|
|
if( bIsHeadShot && Health <= 0 )
|
|
{
|
|
KFGameType(Level.Game).DramaticEvent(0.03);
|
|
}
|
|
|
|
bBackstabbed = false;
|
|
}
|
|
|
|
function PlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HitIdx )
|
|
{
|
|
local Vector HitNormal;
|
|
local Vector HitRay ;
|
|
local Name HitBone;
|
|
local float HitBoneDist;
|
|
local PlayerController PC;
|
|
local bool bShowEffects, bRecentHit;
|
|
local BloodSpurtOS BloodHit;
|
|
|
|
bRecentHit = Level.TimeSeconds - LastPainTime < 0.2;
|
|
|
|
LastDamageAmount = Damage;
|
|
|
|
if(KFMonsterController(Controller).bUseFreezeHack == false)
|
|
OldPlayHit(Damage, InstigatedBy, HitLocation, DamageType,Momentum);
|
|
|
|
|
|
if ( Damage <= 0 )
|
|
return;
|
|
|
|
if( Health>0 && Damage>(float(default.Health)/1.5) )
|
|
FlipOver();
|
|
|
|
PC = PlayerController(Controller);
|
|
bShowEffects = ( (Level.NetMode != NM_Standalone) || (Level.TimeSeconds - LastRenderTime < 2.5)
|
|
|| ((InstigatedBy != none) && (PlayerController(InstigatedBy.Controller) != none))
|
|
|| (PC != none) );
|
|
if ( !bShowEffects )
|
|
return;
|
|
|
|
if ( BurnDown > 0 && !bBurnified )
|
|
{
|
|
bBurnified = true;
|
|
}
|
|
|
|
HitRay = vect(0,0,0);
|
|
|
|
if( InstigatedBy != none )
|
|
HitRay = Normal(HitLocation-(InstigatedBy.Location+(vect(0,0,1)*InstigatedBy.EyeHeight)));
|
|
|
|
if( DamageType.default.bLocationalHit )
|
|
{
|
|
CalcHitLoc( HitLocation, HitRay, HitBone, HitBoneDist );
|
|
|
|
if ( bZapped && DamageType.name != 'DamTypeZEDGun' )
|
|
{
|
|
PlaySound(class'ZedGunProjectile'.default.ExplosionSound,,class'ZedGunProjectile'.default.ExplosionSoundVolume);
|
|
Spawn(class'ZedGunProjectile'.default.ExplosionEmitter,,,HitLocation + HitNormal*20,rotator(HitNormal));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HitLocation = Location ;
|
|
HitBone = FireRootBone;
|
|
HitBoneDist = 0.0f;
|
|
}
|
|
|
|
if( DamageType.default.bAlwaysSevers && DamageType.default.bSpecial )
|
|
HitBone = 'head';
|
|
|
|
if( InstigatedBy != none )
|
|
HitNormal = Normal( Normal(InstigatedBy.Location-HitLocation) + VRand() * 0.2 + vect(0,0,2.8) );
|
|
else
|
|
HitNormal = Normal( Vect(0,0,1) + VRand() * 0.2 + vect(0,0,2.8) );
|
|
|
|
if ( DamageType.default.bCausesBlood && DamageType != class 'Burned' )
|
|
{
|
|
if ( class'GameInfo'.static.UseLowGore() )
|
|
{
|
|
if ( class'GameInfo'.static.NoBlood() )
|
|
BloodHit = BloodSpurtOS(Spawn( GibGroupClass.default.NoBloodHitClass,InstigatedBy,, HitLocation ));
|
|
else
|
|
BloodHit = BloodSpurtOS(Spawn( GibGroupClass.default.LowGoreBloodHitClass,InstigatedBy,, HitLocation ));
|
|
}
|
|
else BloodHit = BloodSpurtOS(Spawn(GibGroupClass.default.BloodHitClass,InstigatedBy,, HitLocation, Rotator(HitNormal)));
|
|
|
|
if ( BloodHit != none )
|
|
{
|
|
BloodHit.bMustShow = !bRecentHit;
|
|
if ( Momentum != vect(0,0,0) )
|
|
{
|
|
BloodHit.HitDir = Momentum;
|
|
LastBloodHitDirection = BloodHit.HitDir;
|
|
}
|
|
else
|
|
{
|
|
if ( InstigatedBy != none )
|
|
BloodHit.HitDir = Location - InstigatedBy.Location;
|
|
else
|
|
BloodHit.HitDir = Location - HitLocation;
|
|
BloodHit.HitDir.Z = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( (DamageType.name == 'DamTypeShotgun' || DamageType.name == 'DamTypeDBShotgun' || DamageType.name == 'DamTypeFrag') && (Health < 0) && (InstigatedBy != none) && (VSize(InstigatedBy.Location - Location) < 350) )
|
|
DoDamageFX( HitBone, 800*Damage, DamageType, Rotator(HitNormal) );
|
|
else
|
|
DoDamageFX( HitBone, Damage, DamageType, Rotator(HitNormal) );
|
|
|
|
if (DamageType.default.DamageOverlayMaterial != none && Damage > 0 )
|
|
SetOverlayMaterial( DamageType.default.DamageOverlayMaterial, DamageType.default.DamageOverlayTime, false );
|
|
}
|
|
|
|
// added HeadStubOS
|
|
simulated function Destroyed()
|
|
{
|
|
if (HeadStubOS != none)
|
|
HeadStubOS.Destroy();
|
|
|
|
super.Destroyed();
|
|
}
|
|
|
|
//todo: simplify using int i?
|
|
simulated function ZombieCrispUp()
|
|
{
|
|
local int i;
|
|
super.ZombieCrispUp();
|
|
|
|
for (i = 0; i < Skins.Length; i++)
|
|
{
|
|
Skins[i]=Texture'KFOldSchoolZeds_Textures.Shared.BurntZedSkin';
|
|
}
|
|
}
|
|
|
|
simulated function SetBurningBehavior()
|
|
{
|
|
if( Role == Role_Authority )
|
|
{
|
|
Intelligence = BRAINS_Retarded;
|
|
|
|
SetGroundSpeed(OriginalGroundSpeed * 0.8);
|
|
AirSpeed *= 0.8;
|
|
WaterSpeed *= 0.8;
|
|
|
|
if( Controller != none )
|
|
{
|
|
MonsterController(Controller).Accuracy = -5;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// copy-paste, just to replace the blood streak decal
|
|
event KImpact(actor other, vector pos, vector impactVel, vector impactNorm)
|
|
{
|
|
local int numSounds, soundNum;
|
|
local vector WallHit, WallNormal;
|
|
local Actor WallActor;
|
|
local KFBloodStreakDecalOS Streak;
|
|
local float VelocitySquared;
|
|
local float RagHitVolume;
|
|
local float StreakDist;
|
|
|
|
numSounds = RagImpactSounds.Length;
|
|
|
|
if ( LastStreakLocation == vect(0,0,0) )
|
|
{
|
|
StreakDist = 0;
|
|
}
|
|
else
|
|
{
|
|
StreakDist = VSizeSquared(LastStreakLocation - pos);
|
|
}
|
|
|
|
LastStreakLocation = pos;
|
|
|
|
WallActor = Trace(WallHit, WallNormal, pos - impactNorm * 16, pos + impactNorm * 16, false);
|
|
|
|
if ( WallActor != none && Level.TimeSeconds > LastStreakTime + BloodStreakInterval && !class'GameInfo'.static.UseLowGore() )
|
|
{
|
|
if ( StreakDist < 1400 )
|
|
{
|
|
return;
|
|
}
|
|
Streak = spawn(class'KFBloodStreakDecalOS',,, WallHit, rotator(-WallNormal));
|
|
|
|
LastStreakTime = Level.TimeSeconds;
|
|
}
|
|
|
|
if ( numSounds > 0 && Level.TimeSeconds > RagLastSoundTime + RagImpactSoundInterval )
|
|
{
|
|
VelocitySquared = VSizeSquared(impactVel);
|
|
RagHitVolume = FMin(2.0, (VelocitySquared / 40000));
|
|
soundNum = Rand(numSounds);
|
|
|
|
PlaySound(RagImpactSounds[soundNum], SLOT_None, RagHitVolume);
|
|
RagLastSoundTime = Level.TimeSeconds;
|
|
}
|
|
}
|
|
|
|
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
|
{
|
|
AmbientSound = none;
|
|
super.Died( Killer, damageType, HitLocation );
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
HeadStubClassOS=Class'KFOldSchoolZeds.GibHeadStumpOS'
|
|
MonsterHeadGibletOS=Class'KFOldSchoolZeds.ClotGibHeadOS'
|
|
MonsterThighGibletOS=Class'KFOldSchoolZeds.ClotGibThighOS'
|
|
MonsterArmGibletOS=Class'KFOldSchoolZeds.ClotGibArmOS'
|
|
MonsterLegGibletOS=Class'KFOldSchoolZeds.ClotGibLegOS'
|
|
MonsterTorsoGibletOS=Class'KFOldSchoolZeds.ClotGibTorsoOS'
|
|
MonsterLowerTorsoGibletOS=Class'KFOldSchoolZeds.ClotGibLowerTorsoOS'
|
|
MixTexturePool(0)=Texture'KFOldSchoolZeds_Textures.Bloat.BloatSkin'
|
|
MixTexturePool(1)=Texture'KFOldSchoolZeds_Textures.clot.ClotSkin'
|
|
MixTexturePool(2)=Texture'KFOldSchoolZeds_Textures.clot.ClotSkinVariant2'
|
|
MixTexturePool(3)=Texture'KFOldSchoolZeds_Textures.Crawler.CrawlerSkin'
|
|
MixTexturePool(4)=Texture'KFOldSchoolZeds_Textures.Fleshpound.PoundSkin'
|
|
MixTexturePool(5)=Texture'KFOldSchoolZeds_Textures.GoreFast.GorefastSkin'
|
|
MixTexturePool(6)=Texture'KFOldSchoolZeds_Textures.Gunpound.GunPoundSkin'
|
|
MixTexturePool(7)=Texture'KFOldSchoolZeds_Textures.Patriarch.PatriarchSkin'
|
|
MixTexturePool(8)=Texture'KFOldSchoolZeds_Textures.siren.SirenSkin'
|
|
MixTexturePool(9)=Texture'KFOldSchoolZeds_Textures.Stalker.StalkerSkin'
|
|
MixHairPool(0)=FinalBlend'KFOldSchoolZeds_Textures.Patriarch.BossHairFB'
|
|
MixHairPool(1)=FinalBlend'KFOldSchoolZeds_Textures.Crawler.CrawlerHairFB'
|
|
MixHairPool(2)=FinalBlend'KFOldSchoolZeds_Textures.siren.SirenHairFB'
|
|
MixHairPool(3)=FinalBlend'KFOldSchoolZeds_Textures.Stalker.StalkerHairFB'
|
|
MoanVolume=10.000000
|
|
RagMaxSpinAmount=1.000000
|
|
BloodStreakInterval=0.500000
|
|
MonsterHeadGiblet=None
|
|
MonsterThighGiblet=None
|
|
MonsterArmGiblet=None
|
|
MonsterLegGiblet=None
|
|
MonsterTorsoGiblet=None
|
|
MonsterLowerTorsoGiblet=None
|
|
ExtCollAttachBoneName="bip01 Spine"
|
|
LeftShoulderBone="Bip01 L Clavicle"
|
|
RightShoulderBone="Bip01 R Clavicle"
|
|
LeftThighBone="Bip01 L Thigh"
|
|
RightThighBone="Bip01 R Thigh"
|
|
LeftFArmBone="Bip01 L Forearm"
|
|
LeftFootBone="Bip01 L Foot"
|
|
RightFootBone="Bip01 R Foot"
|
|
LeftHandBone="bip01 l hand"
|
|
RightHandBone="Bip01 R Hand"
|
|
NeckBone="Bip01 Neck"
|
|
ProjectileBloodSplatClass=None
|
|
ObliteratedEffectClass=Class'KFOldSchoolZeds.GibExplosionOS'
|
|
GibGroupClass=Class'KFOldSchoolZeds.KFNoGibGroupOS'
|
|
SoundGroupClass=Class'KFOldSchoolZeds.KFMaleZombieSoundsOS'
|
|
FireRootBone="bip01 Spine"
|
|
DeResTime=0.000000
|
|
RagdollLifeSpan=0.000000
|
|
RagDeathVel=150.000000
|
|
RagShootStrength=6000.000000
|
|
RagSpinScale=2.500000
|
|
RagDeathUpKick=170.000000
|
|
RagGravScale=1.500000
|
|
RagImpactSounds(0)=Sound'KFOldSchoolZeds_Sounds.Shared.Breakbone_01'
|
|
RagImpactSounds(1)=Sound'KFOldSchoolZeds_Sounds.Shared.Breakbone_02'
|
|
RagImpactSounds(2)=Sound'KFOldSchoolZeds_Sounds.Shared.Breakbone_03'
|
|
RagImpactSoundInterval=0.500000
|
|
ControllerClass=Class'KFOldSchoolZeds.KFMonsterControllerOS'
|
|
RootBone=
|
|
HeadBone="Bip01 Head"
|
|
SpineBone1="Bip01 Spine1"
|
|
SpineBone2=
|
|
SoundVolume=200
|
|
Mass=300.000000
|
|
Begin Object Class=KarmaParamsSkel Name=PawnKParams
|
|
KConvulseSpacing=(Max=2.200000)
|
|
KLinearDamping=0.150000
|
|
KAngularDamping=0.050000
|
|
KBuoyancy=1.000000
|
|
KStartEnabled=True
|
|
KVelDropBelowThreshold=50.000000
|
|
bHighDetailOnly=False
|
|
KFriction=0.600000
|
|
KRestitution=0.300000
|
|
KImpactThreshold=250.000000
|
|
End Object
|
|
KParams=KarmaParamsSkel'KFOldSchoolZeds.KFMonsterOS.PawnKParams'
|
|
|
|
}
|