rott/kf_sources/KFMod/Classes/KFMonster.uc
2026-07-14 20:27:09 +07:00

4130 lines
122 KiB
Ucode

// Base Zombie Class.
class KFMonster extends Skaarj
hidecategories(AnimTweaks,DeRes,Force,Gib,Karma,Udamage,UnrealPawn)
Abstract;
#exec OBJ LOAD FILE=KF_EnemyGlobalSndTwo.uax
#exec OBJ LOAD FILE=KFZED_Temp_UT.utx
#exec OBJ LOAD FILE=KFZED_FX_T.utx
var array<string> EventClasses;
/* reference to the ZombieVolume which spawned me .. */
var ZombieVolume SpawnVolume;
var name MeleeAnims[3];
var name HitAnims[3];
var(Sounds) sound MoanVoice; // The sound of this Monster randomly "talking"
var(Sounds) float MoanVolume;// The volume to use for the zombie moaning
var float NextBileTime, BileFrequency;
var int BileCount;
var Pawn BileInstigator;
var class<DamTypeVomit> LastBileDamagedByType;
var name KFHitFront;
var name KFHitBack;
var name KFHitLeft;
var name KFHitRight;
var(Karma) float RagMaxSpinAmount; // The max we'll scale up spin amount for locational hit damage
var int LookRotPitch,LookRotYaw;
var Pawn LookTarget;
var int HitMomentum;
var bool bRanged;
var() bool bStunImmune; // is the zombie immune to stun hit effects?
var bool bSTUNNED;
var float StunTime;
var int StunsRemaining; // Number of stuns that can ever play on one instance of this character(-1 is the default and is infinite)
var bool bDecapitated; // has he lost his noggin'!?
var int Gored; // Has he lost his whole torso?! if so, how much of it?
var bool DECAP;
var bool bBurnified;
var bool bBurnApplied;
var byte HeatAmount;
var() float BleedOutDuration; // How long this zombie survives after losing its head
var float BleedOutTime; // When this zombie will die from bleeding out
var bool bNoBrainBitEmitter; // We want to skip the brain bit emitter for this zed
var() bool bCannibal; // If true, this enemy will stop to eat corpses it finds.
var bool bZapped; // This zed has been zapped by the ZEDGun
var bool bOldZapped; // The last state of the bZapped flag
var float RemainingZap; // How much zap time this zed has left
var float TotalZap; // How much zap this zed has taken
var float LastZapTime; // The last time we recieved any "zap"
var() float ZapDuration; // How long a zap lasts
var() float ZappedSpeedMod; // How much to slow down zeds when they are zapped
var() float ZapThreshold; // How much time of being hit with zap before this zed get's "zapped"
var() float ZappedDamageMod; // How much to scale damage by when this zed is zapped
var() float ZapResistanceScale; // Every time the zed gets zapped, scale his resistance up by this modifier. This prevent zeds from getting constantly raped by zap (like the fleshpound)
var Pawn ZappedBy; //who did the zapping
var bool bHarpoonStunned; // This zed has been stunned by a harpoon
var bool bOldHarpoonStunned; // The last state of the bHarpoonStunned flag
var() bool bHarpoonToHeadStuns;// A harpoon to this zeds head will stun it
var() bool bHarpoonToBodyStuns;// A harpoon to this zeds body will stun it
var int NumHarpoonsAttached;// The number of harpoons stuck in us
var float DamageToMonsterScale;// How much to scale up damage for this zed damaging other monsters. Monsters have much higher health than humans, so when monsters duke it out they need to do more damage to each other
var float HumanBileAggroChance;// What random percentage chance (0.15-1.0) to have a zed go attack the nearest bloat when a human sprays them with a bloat bile weapon
// Zombie flags:
// 0 - Normal zombie
// 1 - Ranged zombie
// 2 - Leaping zombie
// 3 - Massive zombie
var() byte ZombieFlag;
var float LastPuntTime, DecapTime;
var rotator NewTorsoRotation;
//var int MaxTorsoYaw,MaxTorsoPitch,MaxTorsoRoll; // limits on SetBoneRot for hit reactions.
var(AI) int MaxSpineVariation; // Maximum base amount the bone can bend. Set in defprops.
var(AI) bool bContorts ; // Does this guy do the LIiiiMBO!!!
var(AI) float MaxContortionPercentage; // def. 0.25
var() int MeleeDamage; // The amount of damage this Zed does when melee attacking
var int damageForce;
var float LastPainAnim;
var(AI) float MinTimeBetweenPainAnims;
var bool playedHit;
var vector KickLocation, ImpactVector;
var actor KickTarget;
var bool bPlayBrainSplash;
var bool bPlayGoreSplash;
var float FeedThreshold; // OBSOLOTE.
var float CorpseStaticTime; // The level.timeseconds record of when the corpse has stopped moving, and should soon go static. 1 second later, it will.
var bool bCorpsePositionSet;
var float CorpseLifeSpan; // The time the zombie corpse will be around for.
var bool bDestroyNextTick; // Destroy this pawn next tick because destroying it now will cause problems
var float TimeSetDestroyNextTickTime; // The time we set the bDestroyNextTick flag
var pawn LastDamagedBy;
var class<damagetype> LastDamagedByType;
var int LastDamageAmount;
var vector LastHitLocation,LastMomentum ;
var float TorsoReturnAlpha;
var bool bBackstabbed;
var bool bFatAss; // HACK for pathfinding
var string KFRagdollName;
var class<DamTypeZombieAttack> ZombieDamType[3];
var class<DamTypeZombieAttack> CurrentDamType;
var sound MiscSound; //
var(Sounds) sound HeadLessDeathSound; //The sound of this zombie dieing without a head
var(Sounds) sound DecapitationSound; //The sound of this zombies head exploding
var(Sounds) sound MeleeAttackHitSound; //The sound of this zombie's melee attack hitting a player
var(Sounds) sound JumpSound; // sound of this zombie jumping
var float SpinDamConst;
var float SpinDamRand;
var() int ScreamDamage;
var() bool bMeleeStunImmune; // if true, this monster cannot be stunned or staggered by melee blows
// Fire Related
var int BurnDown; // Number of times our zombie must suffer Fire Damage.
var bool bAshen; // is our Zed crispy yet?
var class<Emitter> BurnEffect; // The appearance of the flames we are attaching to our Zed.
var class<Emitter> AltBurnEffect; // The low gore appearance of the "flames" we are attaching to our Zed.
var int LastBurnDamage; // Record the last amount of Fire damage the pawn suffered.
var() int CrispUpThreshhold; // How much burn down does this zed have to take before crisping up
var bool bCrispified; // The zed has been "ashen" alredy
var class<DamageType> FireDamageClass; // Holds the Fire Damage Type that was last done(Flamethrower or Mac10)
var Effect_ShadowController RealtimeShadow;
var(Pawn) bool bRealtimeShadows; // Advanced Shadows care of Squirrelzero's code.
var(Pawn) Name PuntAnim; // The animation to play when a zombie punts a karma object
// Add an animation name here, to have it randomize with the monster's default movement animations.
var Array<Name>AdditionalWalkAnims;
var Name SpawningWalkAnim; // the anim we're confirmed to spawn with.
var float BloodStreakInterval;
var transient float LastStreakTime;
var vector LastStreakLocation; // Stores the last place we left a streak, so we don't put them too close together
var class <KFGib> MonsterHeadGiblet;
var class <KFGib> MonsterThighGiblet;
var class <KFGib> MonsterArmGiblet;
var class <KFGib> MonsterLegGiblet;
var class <KFGib> MonsterTorsoGiblet;
var class <KFGib> MonsterLowerTorsoGiblet;
// Better Gore
var(Pawn) Material GoredMat; // Swap this in when he's blown in half.
var bool bDiffAdjusted; // has this monster had it's stats adjusted for the server's difficulty? Do once.
var bool bCloaked;
var bool bSpotted; // if true , use the "revealed" shader, instead of the cloak effect
var Emitter FlamingFXs;
var Pawn BurnInstigator;
var(AI) enum EIntelligence
{
BRAINS_Retarded, // Dumbasses
BRAINS_Stupid, // Just plain stupid.
BRAINS_Mammal,
BRAINS_Human // Smarties
} Intelligence;
var(AI) bool bCanDistanceAttackDoors;
var bool bDistanceAttackingDoor;
var() bool bStartUpDisabled,bNoAutoHuntEnemies;
var() int HealthModifer;
var() name FirstSeePlayerEvent;
var int ExpectingChannel;
var bool bResetAnimAct;
var float ResetAnimActTime;
var(ExtendedCollision) bool bUseExtendedCollision; // Whether or not this monster has an extra collision cylinder
var(ExtendedCollision) vector ColOffset; // The offset of the extended collision cylinder from the root bone
var(ExtendedCollision) float ColRadius,ColHeight; // The size of the extended collision cylinder
var(ExtendedCollision) name ExtCollAttachBoneName; // The bone name to attach the extended collision to
var ExtendedZCollision MyExtCollision; // The extended collision cylinder
var bool SavedExtCollision; // Saved original state of the external collision
// Bone Names - These are used by the gore system. Need to use variables instead
// of hard coding these while we switch over from old player models to new ones - Ramm
var() name LeftShoulderBone; // The bone name of the left shoulder bone
var() name RightShoulderBone; // The bone name of the right shoulder bone
var() name LeftThighBone; // The bone name of the left thigh bone
var() name RightThighBone; // The bone name of the right thigh bone
var() name LeftFArmBone; // The bone name of the left forearm bone
var() name RightFArmBone; // The bone name of the right forearm bone
var() name LeftFootBone; // The bone name of the left foot bone
var() name RightFootBone; // The bone name of the right foot bone
var() name LeftHandBone; // The bone name of the left hand bone
var() name RightHandBone; // The bone name of the right hand bone
var() name NeckBone; // The bone name of the neck bone
var float OriginalGroundSpeed;// The difficulty adjusted ground speed (need to store this off, because we have to restore this value at certain times)
// Gore
var SeveredAppendageAttachment SeveredLeftArm; // The meaty attachments that get attached when body parts are blown off
var SeveredAppendageAttachment SeveredRightArm; // The meaty attachments that get attached when body parts are blown off
var SeveredAppendageAttachment SeveredLeftLeg; // The meaty attachments that get attached when body parts are blown off
var SeveredAppendageAttachment SeveredRightLeg; // The meaty attachments that get attached when body parts are blown off
var SeveredAppendageAttachment SeveredHead; // The meaty attachments that get attached when body parts are blown off
var(Gore) float SeveredArmAttachScale; // The drawscale of the arm gore attachement
var(Gore) float SeveredLegAttachScale; // The drawscale of the leg gore attachement
var(Gore) float SeveredHeadAttachScale; // The drawscale of the head gore attachement
var class<DismembermentJetHead> NeckSpurtEmitterClass; // class of the destroyed head neck emitter
var class<DismembermentJetDecapitate> NeckSpurtNoGibEmitterClass;// class of the chopped off head neck emitter
var class<DismembermentJetLimb> LimbSpurtEmitterClass; // class of the chopped off head neck emitter
var class<SeveredAppendageAttachment> SeveredArmAttachClass; // class of the severed arm for this role
var class<SeveredAppendageAttachment> SeveredLegAttachClass; // class of the severed arm for this role
var class<SeveredAppendageAttachment> SeveredHeadAttachClass; // class of the severed arm for this role
var class <ProjectileBloodSplat> ProjectileBloodSplatClass; // class of the wall bloodsplat from a projectile's impact
var class <SeveredAppendage> DetachedArmClass; // class of detached arm to spawn for this pawn. Modified by the subclass to match the player model
var class <SeveredAppendage> DetachedLegClass; // class of detached leg to spawn for this pawn. Modified by the subclass to match the player model
var class <SeveredAppendage> DetachedHeadClass; // class of detached head to spawn for this pawn. Modified by the subclass to match the player model
var class <SeveredAppendage> DetachedSpecialArmClass;// class of detached special arm to spawn for this pawn. Modified by the subclass to match the player model
var bool bLeftArmGibbed; // LeftArm is already blown off
var bool bRightArmGibbed; // RightArm is already blown off
var bool bLeftLegGibbed; // LeftLeg is already blown off
var bool bRightLegGibbed; // RightLeg is already blown off
var bool bHeadGibbed; // Head is already blown off
var class <Emitter> ObliteratedEffectClass; // class of detached arm to spawn for this pawn. Modified by the RoleInfo to match the player model
var() float PlayerCountHealthScale; // How much % of total health to add to this Zed for each additional player in the game
var float LastViewCheckTime; // internal use, used to see how long its been since we checked if there was line of site between this Zed and the local player controller
var float LastSeenOrRelevantTime; // The last time this Zed was seen, or was relevant
var name HeadlessWalkAnims[4]; // Headless movement anims - Forward, Back, Left, Right
var name BurningWalkFAnims[3]; // Burning move forward anims
var name BurningWalkAnims[3]; // Burning movement anims, Back, Left, Right
var() float PoundRageBumpDamScale; // How much to scale the damage from the FleshPound running into this AI
var() float HiddenGroundSpeed; // How fast this Zed should move when it's out of view;
var rotator NeckRot; // Used for the low gore "decapitaton"
var bool bZedUnderControl; // The zed is under the control of the zed control device
var int NumZCDHits; // How many times has this zed been hit with the ZCD Device
var() vector OnlineHeadshotOffset; // Headshot offset for when a zed isn't animating online. "Best guess" since movement anims aren't played on the server side
var() float OnlineHeadshotScale; // Headshot scale for when a zed isn't animating online. Scaled a little bit to cover the area the head might be in when moving, since movement anims aren't played server side
var() float HeadHealth; // How much health the zed's head has, get below this and they lose thier head
var() float PlayerNumHeadHealthScale;// How much % of total head health to add to this Zed for each additional player in the game
var() float MotionDetectorThreat; // How much of a threat is this zed considered to be to motion sensing devices
// Headshot debugging
//var vector ServerHeadLocation; // The location of the Zed's head on the server, used for debugging
//var vector LastServerHeadLocation;
// Achievements Helpers
var bool bHealed;
var bool bOnlyDamagedByCrossbow;
var bool bDamagedAPlayer;
var bool bLaserSightedEBRM14Headshotted;
replication
{
reliable if(bNetDirty && Role == ROLE_Authority)
bDecapitated,Gored,LookTarget,bBurnified,bAshen,FeedThreshold,bCannibal,bDiffAdjusted,bCloaked,
bCrispified,bZedUnderControl;
reliable if(bNetDirty && Role == ROLE_Authority)
bZapped, bHarpoonStunned;
// Headshot debugging
// reliable if(Role == ROLE_Authority)
// ServerHeadLocation;
}
event PreBeginPlay()
{
Super.PreBeginPlay();
CalcAmbientRelevancyScale();
}
simulated function CalcAmbientRelevancyScale()
{
// Make the zed only relevant by thier ambient sound out to a range of 10 meters
CustomAmbientRelevancyScale = 500/(100 * SoundRadius);
}
// empty functions to avoid casting
function StartCharging(){}
function DebugLog(){}
function BreakGrapple(){}
//-----------------------------------------------------------------------------
// PostBeginPlay
//-----------------------------------------------------------------------------
simulated function PostBeginPlay()
{
local float RandomGroundSpeedScale;
local float MovementSpeedDifficultyScale;
local vector AttachPos;
if(ROLE==ROLE_Authority)
{
if ( (ControllerClass != None) && (Controller == None) )
Controller = spawn(ControllerClass);
if ( Controller != None )
Controller.Possess(self);
SplashTime = 0;
SpawnTime = Level.TimeSeconds;
EyeHeight = BaseEyeHeight;
OldRotYaw = Rotation.Yaw;
if( HealthModifer!=0 )
Health = HealthModifer;
if ( bUseExtendedCollision && MyExtCollision == none )
{
MyExtCollision = Spawn(class 'ExtendedZCollision',self);
MyExtCollision.SetCollisionSize(ColRadius,ColHeight);
MyExtCollision.bHardAttach = true;
AttachPos = Location + (ColOffset >> Rotation);
MyExtCollision.SetLocation( AttachPos );
MyExtCollision.SetPhysics( PHYS_None );
MyExtCollision.SetBase( self );
SavedExtCollision = MyExtCollision.bCollideActors;
}
}
AssignInitialPose();
// Let's randomly alter the position of our zombies' spines, to give their animations
// the appearance of being somewhat unique.
SetTimer(1.0, false);
//Set Karma Ragdoll skeleton for this character.
if (KFRagdollName != "")
RagdollOverride = KFRagdollName; //ClotKarma
//Log("Ragdoll Skeleton name is :"$RagdollOverride);
if (bActorShadows && bPlayerShadows && (Level.NetMode != NM_DedicatedServer))
{
// decide which type of shadow to spawn
if (!bRealtimeShadows)
{
PlayerShadow = Spawn(class'ShadowProjector',Self,'',Location);
PlayerShadow.ShadowActor = self;
PlayerShadow.bBlobShadow = bBlobShadow;
PlayerShadow.LightDirection = Normal(vect(1,1,3));
PlayerShadow.LightDistance = 320;
PlayerShadow.MaxTraceDistance = 350;
PlayerShadow.InitShadow();
}
else
{
RealtimeShadow = Spawn(class'Effect_ShadowController',self,'',Location);
RealtimeShadow.Instigator = self;
RealtimeShadow.Initialize();
}
}
bSTUNNED = false;
DECAP = false;
// Difficulty Scaling
if (Level.Game != none && !bDiffAdjusted)
{
//log(self$" Beginning ground speed "$default.GroundSpeed);
if( Level.Game.NumPlayers <= 3 )
{
HiddenGroundSpeed = default.HiddenGroundSpeed;
}
else if( Level.Game.NumPlayers <= 5 )
{
HiddenGroundSpeed = default.HiddenGroundSpeed * 1.3;
}
else if( Level.Game.NumPlayers >= 6 )
{
HiddenGroundSpeed = default.HiddenGroundSpeed * 1.65;
}
// Some randomization to their walk speeds.
RandomGroundSpeedScale = 1.0 + ((1.0 - (FRand() * 2.0)) * 0.1); // +/- 10%
SetGroundSpeed(default.GroundSpeed * RandomGroundSpeedScale);
//log(self$" Randomized ground speed "$GroundSpeed$" RandomGroundSpeedScale "$RandomGroundSpeedScale);
if( Level.Game.GameDifficulty < 2.0 )
{
MovementSpeedDifficultyScale = 0.95;
}
else if( Level.Game.GameDifficulty < 4.0 )
{
MovementSpeedDifficultyScale = 1.0;
}
else if( Level.Game.GameDifficulty < 5.0 )
{
MovementSpeedDifficultyScale = 1.15;
}
else if( Level.Game.GameDifficulty < 7.0 )
{
MovementSpeedDifficultyScale = 1.22;
}
else // Hardest difficulty
{
MovementSpeedDifficultyScale = 1.3;
}
GroundSpeed *= MovementSpeedDifficultyScale;
AirSpeed *= MovementSpeedDifficultyScale;
WaterSpeed *= MovementSpeedDifficultyScale;
// Store the difficulty adjusted ground speed to restore if we change it elsewhere
OriginalGroundSpeed = GroundSpeed;
//log(self$" Scaled ground speed "$GroundSpeed$" Difficulty "$Level.Game.GameDifficulty$" MovementSpeedDifficultyScale "$MovementSpeedDifficultyScale);
// Scale health by difficulty
Health *= DifficultyHealthModifer();
HealthMax *= DifficultyHealthModifer();
HeadHealth *= DifficultyHeadHealthModifer();
// Scale health by number of players
Health *= NumPlayersHealthModifer();
HealthMax *= NumPlayersHealthModifer();
HeadHealth *= NumPlayersHeadHealthModifer();
MeleeDamage = Max((DifficultyDamageModifer() * MeleeDamage),1);
SpinDamConst = Max((DifficultyDamageModifer() * SpinDamConst),1);
SpinDamRand = Max((DifficultyDamageModifer() * SpinDamRand),1);
ScreamDamage = Max((DifficultyDamageModifer() * ScreamDamage),1);
bDiffAdjusted = true;
}
if( Level.NetMode!=NM_DedicatedServer )
{
AdditionalWalkAnims[AdditionalWalkAnims.length] = default.MovementAnims[0];
MovementAnims[0] = AdditionalWalkAnims[Rand(AdditionalWalkAnims.length)];
}
}
// Accessor for GroundSpeed so we can track what is setting it
simulated function SetGroundSpeed(float NewGroundSpeed)
{
GroundSpeed = NewGroundSpeed;
}
// Scales the damage this Zed deals by the difficulty level
function float DifficultyDamageModifer()
{
local float AdjustedDamageModifier;
if ( Level.Game.GameDifficulty >= 7.0 ) // Hell on Earth
{
AdjustedDamageModifier = 1.75;
}
else if ( Level.Game.GameDifficulty >= 5.0 ) // Suicidal
{
AdjustedDamageModifier = 1.50;
}
else if ( Level.Game.GameDifficulty >= 4.0 ) // Hard
{
AdjustedDamageModifier = 1.25;
}
else if ( Level.Game.GameDifficulty >= 2.0 ) // Normal
{
AdjustedDamageModifier = 1.0;
}
else //if ( GameDifficulty == 1.0 ) // Beginner
{
AdjustedDamageModifier = 0.3;
}
// Do less damage if we're alone
if( Level.Game.NumPlayers == 1 )
{
AdjustedDamageModifier *= 0.75;
}
return AdjustedDamageModifier;
}
// Scales the health this Zed has by the difficulty level
function float DifficultyHealthModifer()
{
local float AdjustedModifier;
if ( Level.Game.GameDifficulty >= 7.0 ) // Hell on Earth
{
AdjustedModifier = 1.75;
}
else if ( Level.Game.GameDifficulty >= 5.0 ) // Suicidal
{
AdjustedModifier = 1.55;
}
else if ( Level.Game.GameDifficulty >= 4.0 ) // Hard
{
AdjustedModifier = 1.35;
}
else if ( Level.Game.GameDifficulty >= 2.0 ) // Normal
{
AdjustedModifier = 1.0;
}
else //if ( GameDifficulty == 1.0 ) // Beginner
{
AdjustedModifier = 0.5;
}
return AdjustedModifier;
}
// Scales the health this Zed has by number of players
function float NumPlayersHealthModifer()
{
local float AdjustedModifier;
local int NumEnemies;
local Controller C;
AdjustedModifier = 1.0;
For( C=Level.ControllerList; C!=None; C=C.NextController )
{
if( C.bIsPlayer && C.Pawn!=None && C.Pawn.Health > 0 )
{
NumEnemies++;
}
}
if( NumEnemies > 1 )
{
AdjustedModifier += (NumEnemies - 1) * PlayerCountHealthScale;
}
return AdjustedModifier;
}
// Scales the head health this Zed has by the difficulty level
function float DifficultyHeadHealthModifer()
{
local float AdjustedModifier;
if ( Level.Game.GameDifficulty >= 7.0 ) // Hell on Earth
{
AdjustedModifier = 1.75;
}
else if ( Level.Game.GameDifficulty >= 5.0 ) // Suicidal
{
AdjustedModifier = 1.55;
}
else if ( Level.Game.GameDifficulty >= 4.0 ) // Hard
{
AdjustedModifier = 1.35;
}
else if ( Level.Game.GameDifficulty >= 2.0 ) // Normal
{
AdjustedModifier = 1.0;
}
else //if ( GameDifficulty == 1.0 ) // Beginner
{
AdjustedModifier = 0.5;
}
return AdjustedModifier;
}
// Scales the head health this Zed has by number of players
function float NumPlayersHeadHealthModifer()
{
local float AdjustedModifier;
local int NumEnemies;
local Controller C;
AdjustedModifier = 1.0;
For( C=Level.ControllerList; C!=None; C=C.NextController )
{
if( C.bIsPlayer && C.Pawn!=None && C.Pawn.Health > 0 )
{
NumEnemies++;
}
}
if( NumEnemies > 1 )
{
AdjustedModifier += (NumEnemies - 1) * PlayerNumHeadHealthScale;
}
return AdjustedModifier;
}
simulated function PostNetBeginPlay()
{
EnableChannelNotify ( 1,1);
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
AnimBlendParams(1, 1.0, 0.0,, HeadBone);
super(pawn).PostNetBeginPlay();
}
// This zed can dodge an incoming shot right now. Overriden in various states
// to prevent certain actions from being interrupted.
function bool CanGetOutOfWay()
{
return true;
}
/* DisplayDebug()
list important actor variable on canvas. Also show the pawn's controller and weapon info
*/
simulated function DisplayDebug(Canvas Canvas, out float YL, out float YPos)
{
local int i;
local name Sequence;
local float Frame, Rate;
Super.DisplayDebug(Canvas, YL, YPos);
for( i = 0; i < 16; i++ )
{
if( IsAnimating( i ) )
{
GetAnimParams( i, Sequence, Frame, Rate );
Canvas.DrawText("Anim:: Channel("@i@") Frame("@Frame@") Rate("@Rate@") Name("@Sequence@")");
YPos += YL;
Canvas.SetPos(4,YPos);
}
}
Canvas.DrawText("bShotAnim: "@bShotAnim);
YPos += YL;
Canvas.SetPos(4,YPos);
}
// This zed has been taken control of. Boost its health and speed
function SetMindControlled(bool bNewMindControlled)
{
if( bNewMindControlled )
{
NumZCDHits++;
if( bNewMindControlled != bZedUnderControl )
{
SetGroundSpeed(OriginalGroundSpeed * 1.25);
Health *= 1.25;
HealthMax *= 1.25;
}
}
else
{
NumZCDHits=0;
}
bZedUnderControl = bNewMindControlled;
}
// Handle the zed being commanded to move to a new location
function GivenNewMarker()
{
GotoState('');
}
// Getter for the Original groundspeed of the zed (adjusted for difficulty, etc)
simulated function float GetOriginalGroundSpeed()
{
if( bZedUnderControl )
{
return OriginalGroundSpeed * 1.25;
}
else
{
return OriginalGroundSpeed;
}
}
//returns how exposed this player is to another actor
function float GetExposureTo(vector TestLocation)
{
local float PercentExposed;
if( FastTrace(GetBoneCoords(HeadBone).Origin,TestLocation))
{
PercentExposed += 0.4;
}
if( FastTrace(GetBoneCoords(RootBone).Origin,TestLocation))
{
PercentExposed += 0.3;
}
if( FastTrace(GetBoneCoords(LeftFootBone).Origin,TestLocation))
{
PercentExposed += 0.15;
}
if( FastTrace(GetBoneCoords(RightFootBone).Origin,TestLocation))
{
PercentExposed += 0.15;
}
return PercentExposed;
}
// Update the shadow if the detail settings have changed in the detail menu
simulated function UpdateShadow()
{
if (bActorShadows && bPlayerShadows && (Level.NetMode != NM_DedicatedServer))
{
if (PlayerShadow != none)
PlayerShadow.Destroy();
if (RealtimeShadow != none)
RealtimeShadow.Destroy();
// decide which type of shadow to spawn
if (!bRealtimeShadows)
{
PlayerShadow = Spawn(class'ShadowProjector',Self,'',Location);
PlayerShadow.ShadowActor = self;
PlayerShadow.bBlobShadow = bBlobShadow;
PlayerShadow.LightDirection = Normal(vect(1,1,3));
PlayerShadow.LightDistance = 320;
PlayerShadow.MaxTraceDistance = 350;
PlayerShadow.InitShadow();
}
else
{
RealtimeShadow = Spawn(class'Effect_ShadowController',self,'',Location);
RealtimeShadow.Instigator = self;
RealtimeShadow.Initialize();
}
}
else if (PlayerShadow != none && Level.NetMode != NM_DedicatedServer)
{
PlayerShadow.Destroy();
PlayerShadow = none;
}
else if (RealtimeShadow != none && Level.NetMode != NM_DedicatedServer)
{
RealtimeShadow.Destroy();
RealtimeShadow = none;
}
}
// Setters for extra collision cylinders
simulated function ToggleAuxCollision(bool newbCollision)
{
if ( !newbCollision )
{
SavedExtCollision = MyExtCollision.bCollideActors;
MyExtCollision.SetCollision(false);
}
else
{
MyExtCollision.SetCollision(SavedExtCollision);
}
}
function bool MakeGrandEntry()
{
Return False;
}
function bool Cloaked()
{
return bCloaked;
}
// move karma objects by a kick
// The Kick animation will ONLY be called if the Zombie is On level ground with the KActor,
// and is facing it.
event Bump(actor Other)
{
local Vector X,Y,Z;
GetAxes(Rotation, X,Y,Z);
super.Bump(Other);
if( Other==none )
return;
if( Other.IsA('NetKActor') && Physics != PHYS_Falling && Location.Z < Other.Location.Z + CollisionHeight
&& Location.Z > Other.Location.Z - (CollisionHeight * 0.5) && Base!=Other && Base.bStatic
&& normal(X) dot normal(Other.Location - Location) >= 0.7)
{
if( KActor(Other).KGetMass()>=0.5 && !MonsterController(Controller).CanAttack(Controller.Enemy) )
{
// Store kick impact data
ImpactVector = Vector(controller.Rotation)*15000 + (velocity * (Mass / 2) ) ; // 30
KickLocation = Other.Location;
KickTarget = Other;
KFMonsterController(Controller).KickTarget = KActor(Other);
SetAnimAction(PuntAnim);
}
}
}
// No more File cabinet surfing zombies please..
singular event BaseChange()
{
if ( KActor(Base) != None || Pawn(Base) != None )
{
JumpOffPawn();
}
}
function JumpOffPawn()
{
Velocity += (50 + CollisionRadius) * VRand();
Velocity.Z = 80 + CollisionHeight;
SetPhysics(PHYS_Falling);
bNoJumpAdjust = true;
if ( Controller != None )
{
Controller.SetFall();
}
}
event PickWallAdjustInLowGravity( vector WallHitNormal, actor HitActor )
{
//Controller.SetFall();
}
// Actually execute the kick (this is notified in the ZombieKick animation)
function KickActor()
{
KickTarget.Velocity.Z += (Mass * 5 + (KGetMass() * 10));
KickTarget.KAddImpulse(ImpactVector, KickLocation);
Acceleration = vect(0,0,0);
Velocity = vect(0,0,0);
KFMonsterController(controller).GotoState('Kicking');
bShotAnim = true;
}
simulated function bool IsMoreThanHalf ( int AngleRot )
{
if ( AngleRot > 32768 )
{
return True;
}
else
{
return False;
}
}
// Return true if we can do the Zombie speed adjust that gets the Zeds
// to the player faster if they can't be seen
function bool CanSpeedAdjust()
{
if ( !bDecapitated && !bZapped )
{
return true;
}
return false;
}
simulated function Tick(float DeltaTime)
{
local PlayerController P;
local float DistSquared;
// if(Level.NetMode == NM_DedicatedServer)
// {
// IsHeadShot(vect(0,0,0), vect(0,0,0), 1.0);
// }
// If we've flagged this character to be destroyed next tick, handle that
if( bDestroyNextTick && TimeSetDestroyNextTickTime < Level.TimeSeconds )
{
Destroy();
}
// Make Zeds move faster if they aren't net relevant, or noone has seen them
// in a while. This well get the Zeds to the player in larger groups, and
// quicker - Ramm
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 the Zed has been bleeding long enough, make it die
if ( Role == ROLE_Authority && bDecapitated )
{
if ( BleedOutTime > 0 && Level.TimeSeconds - BleedOutTime >= 0 )
{
Died(LastDamagedBy.Controller,class'DamTypeBleedOut',Location);
BleedOutTime=0;
}
}
//SPLATTER!!!!!!!!!
//TODO - can we work this into Epic's gib code?
//Will we see enough improvement in efficiency to be worth the effort?
if ( Level.NetMode!=NM_DedicatedServer )
{
TickFX(DeltaTime);
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 ( 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;
// The Zed can take more zap each time they get zapped
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;
}
}
// Apply "Zap" to the Zed
function SetZapped(float ZapAmount, Pawn Instigator)
{
LastZapTime = Level.TimeSeconds;
if( bZapped )
{
TotalZap = ZapThreshold;
RemainingZap = ZapDuration;
SetOverlayMaterial(Material'KFZED_FX_T.Energy.ZED_overlay_Hit_Shdr', RemainingZap, true);
}
else
{
TotalZap += ZapAmount;
if( TotalZap >= ZapThreshold )
{
RemainingZap = ZapDuration;
SetOverlayMaterial(Material'KFZED_FX_T.Energy.ZED_overlay_Hit_Shdr', RemainingZap, true);
bZapped = true;
}
}
ZappedBy = Instigator;
}
// Set the zed to the zapped behavior
simulated function SetZappedBehavior()
{
if( Role == Role_Authority )
{
Intelligence = BRAINS_Retarded; // burning dumbasses!
SetGroundSpeed(OriginalGroundSpeed * ZappedSpeedMod);
AirSpeed *= ZappedSpeedMod;
WaterSpeed *= ZappedSpeedMod;
// Make them less accurate while they are burning
if( Controller != none )
{
MonsterController(Controller).Accuracy = -5; // More chance of missing. (he's burning now, after all) :-D
}
}
// Set the forward movement anim to a random burning anim
MovementAnims[0] = BurningWalkFAnims[Rand(3)];
WalkAnims[0] = BurningWalkFAnims[Rand(3)];
// Set the rest of the movement anims to the headless anim (not sure if these ever even get played) - Ramm
MovementAnims[1] = BurningWalkAnims[0];
WalkAnims[1] = BurningWalkAnims[0];
MovementAnims[2] = BurningWalkAnims[1];
WalkAnims[2] = BurningWalkAnims[1];
MovementAnims[3] = BurningWalkAnims[2];
WalkAnims[3] = BurningWalkAnims[2];
}
// Turn off the on-fire behavior
simulated function UnSetZappedBehavior()
{
local int i;
if ( Role == Role_Authority )
{
Intelligence = default.Intelligence;
if( bBurnified )
{
SetGroundSpeed(GetOriginalGroundSpeed() * 0.80);
}
else
{
SetGroundSpeed(GetOriginalGroundSpeed());
}
AirSpeed = default.AirSpeed;
WaterSpeed = default.WaterSpeed;
// Set normal accuracy
if ( Controller != none )
{
MonsterController(Controller).Accuracy = MonsterController(Controller).default.Accuracy;
}
}
// restore regular anims
for ( i = 0; i < 4; i++ )
{
MovementAnims[i] = default.MovementAnims[i];
WalkAnims[i] = default.WalkAnims[i];
}
}
function TakeBileDamage()
{
Super.TakeDamage(2 + Rand(3), BileInstigator, Location, vect(0,0,0), LastBileDamagedByType);
}
simulated function StartBurnFX()
{
local class<emitter> Effect;
if( bDeleteMe )
{
return;
}
// No real flames when low gore, make them smoke, smoking kills
if ( class'GameInfo'.static.UseLowGore() )
{
Effect = AltBurnEffect;
}
else
{
Effect = BurnEffect;
}
if ( FlamingFXs == None )
{
FlamingFXs = Spawn(Effect);
}
FlamingFXs.SetBase(Self);
FlamingFXs.Emitters[0].SkeletalMeshActor = self;
FlamingFXs.Emitters[0].UseSkeletalLocationAs = PTSU_SpawnOffset;
AttachEmitterEffect(Effect, HeadBone, Location, Rotation);
bBurnApplied = True;
}
simulated function StopBurnFX()
{
UnSetBurningBehavior();
RemoveFlamingEffects();
if ( FlamingFXs != None )
{
FlamingFXs.Kill();
}
bBurnApplied = False;
}
// High damage was taken, make em fall over.
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).bUseFreezeHack = True;
Return True;
}
function AddVelocity( vector NewVelocity)
{
if( VSize(NewVelocity) > 50 )
{
Super.AddVelocity(NewVelocity);
}
}
// Important Block of code controlling how the Zombies (excluding the Bloat and Fleshpound who cannot be stunned, respond to damage from the
// various weapons in the game. The basic rule is that any damage amount equal to or greater than 40 points will cause a stun.
// There are exceptions with the fists however, which are substantially under the damage quota but can still cause stuns 50% of the time.
// Why? Cus if they didn't at least have that functionality, they would be fundamentally useless. And anyone willing to take on a hoarde of zombies
// with only the gloves on his hands, deserves more respect than that!
simulated function PlayTakeHit(vector HitLocation, int Damage, class<DamageType> DamageType)
{
local int FistStrikeStunChance;
if( Level.TimeSeconds - LastPainAnim < MinTimeBetweenPainAnims )
return;
// No anim if we're burning, we're already playing an anim
if ( !(bCrispified && bBurnified) /*DamageType.name != 'DamTypeBurned' && DamageType.name != 'DamTypeFlamethrower'*/ )
{
if( Damage>=5 )
PlayDirectionalHit(HitLocation);
else if (DamageType.name == 'DamTypeShotgun' || DamageType.name == 'DamTypeDBShotgun'
|| DamageType.name == 'DamTypeFrag' || DamageType.name == 'DamTypeAA12Shotgun'
|| DamageType.name == 'DamTypePipeBomb' || DamageType.name == 'DamTypeM79Grenade'
|| DamageType.name == 'DamTypeM32Grenade' || DamageType.name == 'DamTypeM203Grenade'
|| DamageType.name == 'DamTypeBenelli' || DamageType.name == 'DamTypeKSGShotgun'
|| DamageType.name == 'DamTypeTrenchgun' || DamageType.name == 'DamTypeNailgun'
|| DamageType.name == 'DamTypeSPShotgun' || DamageType.name == 'DamTypeSPGrenade'
|| DamageType.name == 'DamTypeSealSquealExplosion' || DamageType.name == 'DamTypeSeekerSixRocket')
PlayDirectionalHit(HitLocation);
else if (DamageType.name == 'DamTypeClaws')
{
FistStrikeStunChance = rand(10);
if ( FistStrikeStunChance > 5 )
PlayDirectionalHit(HitLocation);
}
else if (DamageType.name == 'DamTypeKnife')
{
FistStrikeStunChance = rand(10);
if ( FistStrikeStunChance > 7 )
PlayDirectionalHit(HitLocation);
}
else if (DamageType.name == 'DamTypeChainsaw')
PlayDirectionalHit(HitLocation);
else if (DamageType.name == 'DamTypeStunNade')
PlayDirectionalHit(HitLocation);
else if (DamageType.name == 'DamTypeCrossbowHeadshot' || DamageType.name == 'DamTypeCrossbuzzsaw'
|| DamageType.name == 'DamTypeCrossbowHeadShot' )
PlayDirectionalHit(HitLocation);
LastPainAnim = Level.TimeSeconds;
}
if( Level.TimeSeconds - LastPainSound < MinTimeBetweenPainSounds )
return;
LastPainSound = Level.TimeSeconds;
if ( class<DamTypeBurned>(DamageType) == none && class<DamTypeFlamethrower>(DamageType) == none )
{
PlaySound(HitSound[0], SLOT_Pain,1.25,,400);
}
}
simulated function DoDerezEffect(); // fuck no!
simulated function DoDamageFX( Name boneName, int Damage, class<DamageType> DamageType, Rotator r )
{
local float DismemberProbability;
local int RandBone;
local bool bDidSever;
//log("DamageFX bonename = "$boneName$" "$Level.TimeSeconds$" Damage "$Damage);
if( bDecapitated && !bPlayBrainSplash )
{
if( class<DamTypeMelee>(DamageType) != none )
{
HitFX[HitFxTicker].damtype = class'DamTypeMeleeDecapitation';
}
else if( class<DamTypeNailGun>(DamageType) != none )
{
HitFX[HitFxTicker].damtype = class'DamTypeProjectileDecap';
}
else
{
HitFX[HitFxTicker].damtype = class'DamTypeDecapitation';
}
if ( DamageType.default.bNeverSevers || class'GameInfo'.static.UseLowGore()
|| (Level.Game != none && Level.Game.PreventSever(self, boneName, Damage, DamageType)) )
{
HitFX[HitFxTicker].bSever = false;
}
else
{
HitFX[HitFxTicker].bSever = true;
}
HitFX[HitFxTicker].bone = HeadBone;
HitFX[HitFxTicker].rotDir = r;
HitFxTicker = HitFxTicker + 1;
if( HitFxTicker > ArrayCount(HitFX)-1 )
HitFxTicker = 0;
bPlayBrainSplash = true;
if( Damage > DamageType.default.HumanObliterationThreshhold && Damage != 1000 )
{
// Do nothing
}
else
{
return;
}
}
if ( FRand() > 0.3f || Damage > 30 || Health <= 0 /*|| DamageType == class 'DamTypeCrossbowHeadshot'*/)
{
HitFX[HitFxTicker].damtype = DamageType;
if( Health <= 0 /*|| DamageType == class 'DamTypeCrossbowHeadshot'*/)
{
switch( boneName )
{
case 'neck':
boneName = HeadBone;
break;
case LeftFootBone:
case 'lleg':
boneName = LeftThighBone;
break;
case RightFootBone:
case 'rleg':
boneName = RightThighBone;
break;
case RightHandBone:
case RightShoulderBone:
case 'rarm':
boneName = RightFArmBone;
break;
case LeftHandBone:
case LeftShoulderBone:
case 'larm':
boneName = LeftFArmBone;
break;
case 'None':
case 'spine':
boneName = FireRootBone;
break;
}
if( DamageType.default.bAlwaysSevers || (Damage == 1000) )
{
HitFX[HitFxTicker].bSever = true;
bDidSever = true;
if ( boneName == 'None' )
{
boneName = FireRootBone;
}
}
else if( DamageType.Default.GibModifier > 0.0 )
{
DismemberProbability = Abs( (Health - Damage*DamageType.Default.GibModifier) / 130.0f );
if( FRand() < DismemberProbability )
{
HitFX[HitFxTicker].bSever = true;
bDidSever = true;
}
}
}
if ( DamageType.default.bNeverSevers || class'GameInfo'.static.UseLowGore()
|| (Level.Game != none && Level.Game.PreventSever(self, boneName, Damage, DamageType)) )
{
HitFX[HitFxTicker].bSever = false;
bDidSever = false;
}
if ( HitFX[HitFxTicker].bSever )
{
if( !DamageType.default.bLocationalHit && (boneName == 'None' || boneName == FireRootBone ||
boneName == 'Spine' ))
{
RandBone = Rand(4);
switch( RandBone )
{
case 0:
boneName = LeftThighBone;
break;
case 1:
boneName = RightThighBone;
break;
case 2:
boneName = LeftFArmBone;
break;
case 3:
boneName = RightFArmBone;
break;
case 4:
boneName = HeadBone;
break;
default:
boneName = LeftThighBone;
}
}
}
if ( Health < 0 && Damage > DamageType.default.HumanObliterationThreshhold && Damage != 1000 && !class'GameInfo'.static.UseLowGore() )
{
boneName = 'obliterate';
}
HitFX[HitFxTicker].bone = boneName;
HitFX[HitFxTicker].rotDir = r;
HitFxTicker = HitFxTicker + 1;
if( HitFxTicker > ArrayCount(HitFX)-1 )
HitFxTicker = 0;
// If this was a really hardcore damage from an explosion, randomly spawn some arms and legs
if ( bDidSever && !DamageType.default.bLocationalHit && Damage > 200 && Damage != 1000 && !class'GameInfo'.static.UseLowGore() )
{
if ((Damage > 400 && FRand() < 0.3) || FRand() < 0.1 )
{
DoDamageFX(HeadBone,1000,DamageType,r);
DoDamageFX(LeftThighBone,1000,DamageType,r);
DoDamageFX(RightThighBone,1000,DamageType,r);
DoDamageFX(LeftFArmBone,1000,DamageType,r);
DoDamageFX(RightFArmBone,1000,DamageType,r);
}
if ( FRand() < 0.25 )
{
DoDamageFX(LeftThighBone,1000,DamageType,r);
DoDamageFX(RightThighBone,1000,DamageType,r);
if ( FRand() < 0.5 )
{
DoDamageFX(LeftFArmBone,1000,DamageType,r);
}
else
{
DoDamageFX(RightFArmBone,1000,DamageType,r);
}
}
else if ( FRand() < 0.35 )
DoDamageFX(LeftThighBone,1000,DamageType,r);
else if ( FRand() < 0.5 )
DoDamageFX(RightThighBone,1000,DamageType,r);
else if ( FRand() < 0.75 )
{
if ( FRand() < 0.5 )
{
DoDamageFX(LeftFArmBone,1000,DamageType,r);
}
else
{
DoDamageFX(RightFArmBone,1000,DamageType,r);
}
}
}
}
}
// Overriden to allow for specifying velocities for the gibs
simulated function KFSpawnGiblet( class<Gib> GibClass, Vector Location, Rotator Rotation, float GibPerterbation, optional float GibVelocity )
{
local Gib Giblet;
local Vector Direction, Dummy;
if( (GibClass == None) || class'GameInfo'.static.UseLowGore() )
return;
Instigator = self;
Giblet = Spawn( GibClass,,, Location, Rotation );
if( Giblet == None )
return;
Giblet.SetDrawScale(Giblet.DrawScale * (CollisionRadius*CollisionHeight)/1100); // 1100 = 25 * 44
GibPerterbation *= 32768.0;
Rotation.Pitch += ( FRand() * 2.0 * GibPerterbation ) - GibPerterbation;
Rotation.Yaw += ( FRand() * 2.0 * GibPerterbation ) - GibPerterbation;
Rotation.Roll += ( FRand() * 2.0 * GibPerterbation ) - GibPerterbation;
GetAxes( Rotation, Dummy, Dummy, Direction );
if( GibVelocity > 0 )
{
Giblet.Velocity = Velocity + Normal(Direction) * (GibVelocity + (GibVelocity * (FRand() * 0.25)));
}
else
{
Giblet.Velocity = Velocity + Normal(Direction) * 512;
}
}
//Stops the green shit when a player dies.
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
{
local float frame, rate;
local name seq;
local LavaDeath LD;
local MiscEmmiter BE;
AmbientSound = None;
bCanTeleport = false; // sjs - fix karma going crazy when corpses land on teleporters
bReplicateMovement = false;
bTearOff = true;
bPlayedDeath = true;
StopBurnFX();
if (CurrentCombo != None)
CurrentCombo.Destroy();
HitDamageType = DamageType; // these are replicated to other clients
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'WeaponSounds.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);
}
// stop shooting
AnimBlendParams(1, 0.0);
FireState = FS_None;
// Try to adjust around performance
//log(Level.DetailMode);
LifeSpan = RagdollLifeSpan;
GotoState('ZombieDying');
if ( BE != None )
return;
PlayDyingAnimation(DamageType, HitLoc);
}
State ZombieDying extends Dying
{
ignores AnimEnd, Trigger, Bump, HitWall, HeadVolumeChange, PhysicsVolumeChange, Falling, BreathTimer, Died, RangedAttack; //Tick
function bool CanGetOutOfWay()
{
return false;
}
simulated function Landed(vector HitNormal)
{
//SetPhysics(PHYS_None);
SetCollision(false, false, false);
if( !bDestroyNextTick )
{
Disable('Tick');
}
}
simulated function Timer()
{
local KarmaParamsSkel skelParams;
if( bDestroyNextTick )
{
// If we've flagged this character to be destroyed next tick, handle that
if( TimeSetDestroyNextTickTime < Level.TimeSeconds )
{
Destroy();
}
else
{
SetTimer(0.01, false);
}
return;
}
if ( !PlayerCanSeeMe() )
{
StartDeRes();
Destroy();
}
// If we are running out of life, but we still haven't come to rest, force the de-res.
// unless pawn is the viewtarget of a player who used to own it
else if ( LifeSpan <= DeResTime && bDeRes == false )
{
skelParams = KarmaParamsSkel(KParams);
skelParams.bKImportantRagdoll = false;
// spawn derez
bDeRes=true;
}
else
{
SetTimer(1.0, false);
}
}
simulated function BeginState()
{
if( bDestroyNextTick )
{
// If we've flagged this character to be destroyed next tick, handle that
if( TimeSetDestroyNextTickTime < Level.TimeSeconds )
{
Destroy();
}
else
{
SetTimer(0.01, false);
}
}
else
{
if ( bTearOff && (Level.NetMode == NM_DedicatedServer) || class'GameInfo'.static.UseLowGore() )
LifeSpan = 1.0;
else
SetTimer(2.0, false);
}
SetPhysics(PHYS_Falling);
if ( Controller != None )
{
Controller.Destroy();
}
}
simulated function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex )
{
local Vector HitNormal, shotDir;
local Vector PushLinVel, PushAngVel;
local Name HitBone;
local float HitBoneDist;
local bool bIsHeadshot;
local vector HitRay;
if ( bFrozenBody || bRubbery )
return;
if( Physics == PHYS_KarmaRagdoll )
{
// Can't shoot corpses during de-res
if ( bDeRes )
return;
// Throw the body if its a rocket explosion or shock combo
if( damageType.Default.bThrowRagdoll )
{
shotDir = Normal(Momentum);
PushLinVel = (RagDeathVel * shotDir) + vect(0, 0, 250);
PushAngVel = Normal(shotDir Cross vect(0, 0, 1)) * -18000;
KSetSkelVel( PushLinVel, PushAngVel );
}
else if( damageType.Default.bRagdollBullet )
{
if ( Momentum == vect(0,0,0) )
Momentum = HitLocation - InstigatedBy.Location;
if ( FRand() < 0.65 )
{
if ( Velocity.Z <= 0 )
PushLinVel = vect(0,0,40);
PushAngVel = Normal(Normal(Momentum) Cross vect(0, 0, 1)) * -8000 ;
PushAngVel.X *= 0.5;
PushAngVel.Y *= 0.5;
PushAngVel.Z *= 4;
KSetSkelVel( PushLinVel, PushAngVel );
}
PushLinVel = RagShootStrength*Normal(Momentum);
KAddImpulse(PushLinVel, HitLocation);
if ( (LifeSpan > 0) && (LifeSpan < DeResTime + 2) )
LifeSpan += 0.2;
}
else
{
PushLinVel = RagShootStrength*Normal(Momentum);
KAddImpulse(PushLinVel, HitLocation);
}
}
if (Damage > 0)
{
Health -= Damage;
if ( !bDecapitated && class<KFWeaponDamageType>(damageType)!=none &&
class<KFWeaponDamageType>(damageType).default.bCheckForHeadShots )
{
bIsHeadShot = IsHeadShot(HitLocation, normal(Momentum), 1.0);
}
if( bIsHeadShot )
RemoveHead();
HitRay = vect(0,0,0);
if( InstigatedBy != none )
HitRay = Normal(HitLocation-(InstigatedBy.Location+(vect(0,0,1)*InstigatedBy.EyeHeight)));
CalcHitLoc( HitLocation, HitRay, HitBone, HitBoneDist );
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) );
// Actually do blood on a client
PlayHit(Damage, InstigatedBy, hitLocation, damageType, Momentum);
DoDamageFX( HitBone, Damage, DamageType, Rotator(HitNormal) );
}
if ( (DamageType.Default.DamageOverlayMaterial != None) && (Level.DetailMode != DM_Low) && !Level.bDropDetail )
SetOverlayMaterial(DamageType.Default.DamageOverlayMaterial, DamageType.default.DamageOverlayTime, true);
}
}
simulated function Timer()
{
// bSTUNNED variable actually indicates flinching, not stunning! So don't get confused.
bSTUNNED = false;
// If burn tick count > 0, call TakeFireDamage() function.
// Otherwise stop burning and turn off the timer
if (BurnDown > 0)
{
// Every tick LastBurnDamage is increased by 3 + random value from 0 to 2 (excluding)
TakeFireDamage(LastBurnDamage + rand(2) + 3 , LastDamagedBy);
SetTimer(1.0,false); // Sets timer function to be executed each second
}
else
{
UnSetBurningBehavior();
RemoveFlamingEffects();
StopBurnFX();
SetTimer(0, false); // Disable timer
}
}
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;
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
{
SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
bGibbed = true;
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
if( Level.NetMode == NM_ListenServer )
{
bDestroyNextTick = true;
TimeSetDestroyNextTickTime = Level.TimeSeconds;
}
else
{
Destroy();
}
return;
}
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore())
{
//AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
{
for( i = 0; i < ArrayCount(HitEffects); i++ )
{
if( HitEffects[i] == None )
continue;
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
}
}
}
if ( class'GameInfo'.static.UseLowGore() )
{
HitFX[SimHitFxTicker].bSever = false;
switch( HitFX[SimHitFxTicker].bone )
{
case 'head':
if( !bHeadGibbed )
{
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
}
bHeadGibbed=true;
}
break;
}
}
if( HitFX[SimHitFxTicker].bSever )
{
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
switch( HitFX[SimHitFxTicker].bone )
{
case 'obliterate':
break;
case LeftThighBone:
if( !bLeftLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bLeftLegGibbed=true;
}
break;
case RightThighBone:
if( !bRightLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightLegGibbed=true;
}
break;
case LeftFArmBone:
if( !bLeftArmGibbed )
{
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;;
bLeftArmGibbed=true;
}
break;
case RightFArmBone:
if( !bRightArmGibbed )
{
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightArmGibbed=true;
}
break;
case 'head':
if( !bHeadGibbed )
{
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
}
bHeadGibbed=true;
}
break;
}
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
HideBone(HitFX[SimHitFxTicker].bone);
}
}
}
// Handle doing the decapitation hit effects
simulated function DecapFX( Vector DecapLocation, Rotator DecapRotation, bool bSpawnDetachedHead, optional bool bNoBrainBits )
{
local float GibPerterbation;
local BrainSplash SplatExplosion;
local int i;
// Do the cute version of the Decapitation
if ( class'GameInfo'.static.UseLowGore() )
{
CuteDecapFX();
return;
}
bNoBrainBitEmitter = bNoBrainBits;
GibPerterbation = 0.060000; // damageType.default.GibPerterbation;
if(bSpawnDetachedHead)
{
SpecialHideHead();
}
else
{
HideBone(HeadBone);
}
if( bSpawnDetachedHead )
{
SpawnSeveredGiblet( DetachedHeadClass, DecapLocation, DecapRotation, GibPerterbation, GetBoneRotation(HeadBone) );
}
// Plug in headless anims if we have them
for( i = 0; i < 4; i++ )
{
if( HeadlessWalkAnims[i] != '' && HasAnim(HeadlessWalkAnims[i]) )
{
MovementAnims[i] = HeadlessWalkAnims[i];
WalkAnims[i] = HeadlessWalkAnims[i];
}
}
if ( !bSpawnDetachedHead && !bNoBrainBits && EffectIsRelevant(DecapLocation,false) )
{
KFSpawnGiblet( class 'KFMod.KFGibBrain',DecapLocation, self.Rotation, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',DecapLocation, self.Rotation, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',DecapLocation, self.Rotation, GibPerterbation, 250 ) ;
}
SplatExplosion = Spawn(class 'BrainSplash',self,, DecapLocation );
}
// Handle hiding the head when its been melee chopped off
simulated function SpecialHideHead()
{
local int BoneScaleSlot;
local coords boneCoords;
// Only scale the bone down once
if( SeveredHead == none )
{
boneScaleSlot = 4;
SeveredHead = Spawn(SeveredHeadAttachClass,self);
SeveredHead.SetDrawScale(SeveredHeadAttachScale);
boneCoords = GetBoneCoords( 'neck' );
AttachEmitterEffect( NeckSpurtNoGibEmitterClass, 'neck', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredHead, 'neck');
}
else
{
return;
}
SetBoneScale(BoneScaleSlot, 0.0, '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);
SetBoneRotation('neck', NeckRot);
RemoveHead();
}
simulated function SpawnSeveredGiblet( class<SeveredAppendage> GibClass, Vector Location, Rotator Rotation, float GibPerterbation, rotator SpawnRotation )
{
local SeveredAppendage Giblet;
local Vector Direction, Dummy;
if( (GibClass == None) || class'GameInfo'.static.UseLowGore() )
return;
Instigator = self;
Giblet = Spawn( GibClass,,, Location, SpawnRotation );
if( Giblet == None )
return;
Giblet.SpawnTrail();
GibPerterbation *= 32768.0;
Rotation.Pitch += ( FRand() * 2.0 * GibPerterbation ) - GibPerterbation;
Rotation.Yaw += ( FRand() * 2.0 * GibPerterbation ) - GibPerterbation;
Rotation.Roll += ( FRand() * 2.0 * GibPerterbation ) - GibPerterbation;
GetAxes( Rotation, Dummy, Dummy, Direction );
Giblet.Velocity = Velocity + Normal(Direction) * (Giblet.MaxSpeed + (Giblet.MaxSpeed/2) * FRand());
// Give a little upward motion to the decapitated head
if( class<SeveredHead>(GibClass) != none )
{
Giblet.Velocity.Z += 50;
}
//Giblet.LifeSpan = self.RagdollLifeSpan;
}
simulated function HideBone(name boneName)
{
local int BoneScaleSlot;
local coords boneCoords;
local bool bValidBoneToHide;
if( boneName == LeftThighBone )
{
boneScaleSlot = 0;
bValidBoneToHide = true;
if( SeveredLeftLeg == none )
{
SeveredLeftLeg = Spawn(SeveredLegAttachClass,self);
SeveredLeftLeg.SetDrawScale(SeveredLegAttachScale);
boneCoords = GetBoneCoords( 'lleg' );
AttachEmitterEffect( LimbSpurtEmitterClass, 'lleg', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredLeftLeg, 'lleg');
}
}
else if ( boneName == RightThighBone )
{
boneScaleSlot = 1;
bValidBoneToHide = true;
if( SeveredRightLeg == none )
{
SeveredRightLeg = Spawn(SeveredLegAttachClass,self);
SeveredRightLeg.SetDrawScale(SeveredLegAttachScale);
boneCoords = GetBoneCoords( 'rleg' );
AttachEmitterEffect( LimbSpurtEmitterClass, 'rleg', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredRightLeg, 'rleg');
}
}
else if( boneName == RightFArmBone )
{
boneScaleSlot = 2;
bValidBoneToHide = true;
if( SeveredRightArm == none )
{
SeveredRightArm = Spawn(SeveredArmAttachClass,self);
SeveredRightArm.SetDrawScale(SeveredArmAttachScale);
boneCoords = GetBoneCoords( 'rarm' );
AttachEmitterEffect( LimbSpurtEmitterClass, 'rarm', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredRightArm, 'rarm');
}
}
else if ( boneName == LeftFArmBone )
{
boneScaleSlot = 3;
bValidBoneToHide = true;
if( SeveredLeftArm == none )
{
SeveredLeftArm = Spawn(SeveredArmAttachClass,self);
SeveredLeftArm.SetDrawScale(SeveredArmAttachScale);
boneCoords = GetBoneCoords( 'larm' );
AttachEmitterEffect( LimbSpurtEmitterClass, 'larm', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredLeftArm, 'larm');
}
}
else if ( boneName == HeadBone )
{
// Only scale the bone down once
if( SeveredHead == none )
{
bValidBoneToHide = true;
boneScaleSlot = 4;
SeveredHead = Spawn(SeveredHeadAttachClass,self);
SeveredHead.SetDrawScale(SeveredHeadAttachScale);
boneCoords = GetBoneCoords( 'neck' );
if( bNoBrainBitEmitter )
{
AttachEmitterEffect( NeckSpurtNoGibEmitterClass, 'neck', boneCoords.Origin, rot(0,0,0) );
}
else
{
AttachEmitterEffect( NeckSpurtEmitterClass, 'neck', boneCoords.Origin, rot(0,0,0) );
}
AttachToBone(SeveredHead, 'neck');
}
else
{
return;
}
}
else if ( boneName == 'spine' )
{
bValidBoneToHide = true;
boneScaleSlot = 5;
}
// Only hide the bone if it is one of the arms, legs, or head, don't hide other misc bones
if( bValidBoneToHide )
{
SetBoneScale(BoneScaleSlot, 0.0, BoneName);
}
}
// Used to attach an emitter instead of an xemitter
simulated function AttachEmitterEffect( class<Emitter> EmitterClass, Name BoneName, Vector Location, Rotator Rotation )
{
local Actor a;
local int i;
if( BoneName == 'None' )
return;
for( i = 0; i < Attached.Length; i++ )
{
if( Attached[i] == None )
continue;
if( Attached[i].AttachmentBone != BoneName )
continue;
if( ClassIsChildOf( EmitterClass, Attached[i].Class ) )
return;
}
a = Spawn( EmitterClass,,, Location, Rotation );
if( !AttachToBone( a, BoneName ) )
{
log( "Couldn't attach "$EmitterClass$" to "$BoneName, 'Error' );
a.Destroy();
return;
}
for( i = 0; i < Attached.length; i++ )
{
if( Attached[i] == a )
break;
}
a.SetRelativeRotation( Rotation );
}
// Maybe spawn some chunks when the player gets obliterated
simulated function SpawnGibs(Rotator HitRotation, float ChunkPerterbation)
{
bGibbed = true;
PlayDyingSound();
if ( class'GameInfo'.static.UseLowGore() )
return;
if ( FlamingFXs != none )
{
FlamingFXs.Emitters[0].SkeletalMeshActor = none;
FlamingFXs.Destroy();
}
if( ObliteratedEffectClass != none )
Spawn( ObliteratedEffectClass,,, Location, HitRotation );
super.SpawnGibs(HitRotation,ChunkPerterbation);
if ( FRand() < 0.1 )
{
KFSpawnGiblet( class 'KFMod.KFGibBrain',Location, HitRotation, ChunkPerterbation, 500 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',Location, HitRotation, ChunkPerterbation, 500 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',Location, HitRotation, ChunkPerterbation, 500 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',Location, HitRotation, ChunkPerterbation, 500 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',Location, HitRotation, ChunkPerterbation, 500 ) ;
SpawnSeveredGiblet( DetachedLegClass, Location, HitRotation, ChunkPerterbation, HitRotation );
SpawnSeveredGiblet( DetachedLegClass, Location, HitRotation, ChunkPerterbation, HitRotation );
SpawnSeveredGiblet( DetachedArmClass, Location, HitRotation, ChunkPerterbation, HitRotation );
if ( DetachedSpecialArmClass != None )
{
SpawnSeveredGiblet( DetachedSpecialArmClass, Location, HitRotation, ChunkPerterbation, HitRotation );
}
else
{
SpawnSeveredGiblet( DetachedArmClass, Location, HitRotation, ChunkPerterbation, HitRotation );
}
}
else if ( FRand() < 0.25 )
{
KFSpawnGiblet( class 'KFMod.KFGibBrainb',Location, HitRotation, ChunkPerterbation, 500 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',Location, HitRotation, ChunkPerterbation, 500 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',Location, HitRotation, ChunkPerterbation, 500 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',Location, HitRotation, ChunkPerterbation, 500 ) ;
SpawnSeveredGiblet( DetachedLegClass, Location, HitRotation, ChunkPerterbation, HitRotation );
SpawnSeveredGiblet( DetachedLegClass, Location, HitRotation, ChunkPerterbation, HitRotation );
if ( FRand() < 0.5 )
{
KFSpawnGiblet( class 'KFMod.KFGibBrain',Location, HitRotation, ChunkPerterbation, 500 ) ;
SpawnSeveredGiblet( DetachedArmClass, Location, HitRotation, ChunkPerterbation, HitRotation );
}
}
else if ( FRand() < 0.35 )
{
KFSpawnGiblet( class 'KFMod.KFGibBrainb',Location, HitRotation, ChunkPerterbation, 500 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',Location, HitRotation, ChunkPerterbation, 500 ) ;
SpawnSeveredGiblet( DetachedLegClass, Location, HitRotation, ChunkPerterbation, HitRotation );
}
else if ( FRand() < 0.5 )
{
KFSpawnGiblet( class 'KFMod.KFGibBrainb',Location, HitRotation, ChunkPerterbation, 500 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',Location, HitRotation, ChunkPerterbation, 500 ) ;
SpawnSeveredGiblet( DetachedArmClass, Location, HitRotation, ChunkPerterbation, HitRotation );
}
}
simulated function StartDeRes()
{
if( Level.NetMode == NM_DedicatedServer )
return;
AmbientGlow=0;
MaxLights=5;
if( Physics == PHYS_KarmaRagdoll )
{
// Remove flames
RemoveFlamingEffects();
KSetBlockKarma(true);
// Turn off any overlays
SetOverlayMaterial(None, 0.0f, true);
SetCollision(true, true, true);
}
}
function bool CanAttack(Actor A)
{
if (A == none)
return false;
if(bSTUNNED)
return false;
if(KFDoorMover(A)!=none)
return true;
else if(KFHumanPawn(A)!=none && KFHumanPawn(A).Health <= 0)
return ( VSize(A.Location - Location) < MeleeRange + CollisionRadius);
else return ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius );
}
function DoorAttack(Actor A)
{
if ( bShotAnim || Physics == PHYS_Swimming)
return;
else if ( A!=None )
{
bShotAnim = true;
SetAnimAction('DoorBash');
GotoState('DoorBashing');
//PlaySound(sound'Claw2s', SLOT_None); KFTODO: Replace this
return;
}
}
function CorpseAttack(Actor A)
{
if ( bShotAnim || Physics == PHYS_Swimming)
return;
Velocity.X = 0;
Velocity.Y = 0;
Acceleration = vect(0,0,0);
bShotAnim = true;
SetAnimAction('ZombieFeed');
Health+=(1+Rand(3));
Health = Min(Health,Default.Health*1.5);
}
function RangedAttack(Actor A)
{
if ( bShotAnim || Physics == PHYS_Swimming)
return;
else if ( CanAttack(A) )
{
bShotAnim = true;
SetAnimAction('Claw');
//PlaySound(sound'Claw2s', SLOT_None); KFTODO: Replace this
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
return;
}
}
simulated event SetAnimAction(name NewAction)
{
local int meleeAnimIndex;
if( NewAction=='' )
Return;
if(NewAction == 'Claw')
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
CurrentDamtype = ZombieDamType[meleeAnimIndex];
}
else if( NewAction == 'DoorBash' )
{
CurrentDamtype = ZombieDamType[Rand(3)];
}
ExpectingChannel = DoAnimAction(NewAction);
if( AnimNeedsWait(NewAction) )
{
bWaitForAnim = true;
}
else
{
bWaitForAnim = false;
}
if( Level.NetMode!=NM_Client )
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
}
}
simulated function bool AnimNeedsWait(name TestAnim)
{
if( ExpectingChannel == 0 )
{
return true;
}
return false;
}
simulated function int DoAnimAction( name AnimName )
{
if( AnimName=='HitF' || AnimName=='HitF2' || AnimName=='HitF3' || AnimName==KFHitFront || AnimName==KFHitBack || AnimName==KFHitRight
|| AnimName==KFHitLeft )
{
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
PlayAnim(AnimName,, 0.1, 1);
return 1;
}
PlayAnim(AnimName,,0.1);
return 0;
}
simulated function AnimEnd(int Channel)
{
/* local name Sequence;
local float Frame, Rate;
GetAnimParams( ExpectingChannel, Sequence, Frame, Rate );
log(Level.TimeSeconds$" "$self$" "$GetStateName()$" AnimEnd for Exp Chan "$ExpectingChannel$" = "$Sequence$" Channel: "$Channel);
GetAnimParams( 0, Sequence, Frame, Rate );
log(self$" "$GetStateName()$" AnimEnd for Chan 0 = "$Sequence);
GetAnimParams( 1, Sequence, Frame, Rate );
log(self$" "$GetStateName()$" AnimEnd for Chan 1 = "$Sequence);
log(self$" "$GetStateName()$" AnimEnd bShotAnim = "$bShotAnim);*/
AnimAction = '';
if ( bShotAnim && Channel==ExpectingChannel )
{
bShotAnim = false;
if( Controller!=None )
Controller.bPreparingMove = false;
}
if( !bPhysicsAnimUpdate && Channel==0 )
bPhysicsAnimUpdate = Default.bPhysicsAnimUpdate;
Super(xPawn).AnimEnd(Channel);
}
simulated function HandleBumpGlass()
{
Acceleration = vect(0,0,0);
Velocity = vect(0,0,0);
SetAnimAction(MeleeAnims[0]);
bShotAnim = true;
controller.GotoState('WaitForAnim');
}
simulated function StoodUp();
simulated function FellDown();
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);
// Melee damage is +/- 10% of default
if ( MeleeDamageTarget(UsedMeleeDamage, PushDir) )
{
PlaySound(MeleeAttackHitSound, SLOT_Interact, 2.0);
}
}
function ZombieMoan() // Moved from Controller to here (so we don't need an own controller for each moan type).
{
PlaySound(MoanVoice, SLOT_Misc, MoanVolume,,250.0);
}
function RemoveHead()
{
local int i;
Intelligence = BRAINS_Retarded; // Headless dumbasses!
bDecapitated = true;
DECAP = true;
DecapTime = Level.TimeSeconds;
Velocity = vect(0,0,0);
SetAnimAction('HitF');
SetGroundSpeed(GroundSpeed *= 0.80);
AirSpeed *= 0.8;
WaterSpeed *= 0.8;
// No more raspy breathin'...cuz he has no throat or mouth :S
AmbientSound = MiscSound;
//TODO - do we need to inform the controller that we can't move owing to lack of head,
// or is that handled elsewhere
if ( Controller != none )
{
MonsterController(Controller).Accuracy = -5; // More chance of missing. (he's headless now, after all) :-D
}
// Head explodes, causing additional hurty.
if( KFPawn(LastDamagedBy)!=None )
{
TakeDamage( LastDamageAmount + 0.25 * HealthMax , LastDamagedBy, LastHitLocation, LastMomentum, LastDamagedByType);
if ( BurnDown > 0 )
{
KFSteamStatsAndAchievements(KFPawn(LastDamagedBy).PlayerReplicationInfo.SteamStatsAndAchievements).AddBurningDecapKill(class'KFGameType'.static.GetCurrentMapName(Level));
}
}
if( Health > 0 )
{
BleedOutTime = Level.TimeSeconds + BleedOutDuration;
}
//TODO - Find right place for this
// He's got no head so biting is out.
if (MeleeAnims[2] == 'Claw3')
MeleeAnims[2] = 'Claw2';
if (MeleeAnims[1] == 'Claw3')
MeleeAnims[1] = 'Claw1';
// Plug in headless anims if we have them
for( i = 0; i < 4; i++ )
{
if( HeadlessWalkAnims[i] != '' && HasAnim(HeadlessWalkAnims[i]) )
{
MovementAnims[i] = HeadlessWalkAnims[i];
WalkAnims[i] = HeadlessWalkAnims[i];
}
}
PlaySound(DecapitationSound, SLOT_Misc,1.30,true,525);
}
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.takeDamage(Damage + 600, instigatedBy, hitLocation, momentum, damageType);
HurtRadius(500, 1000, class'DamTypeFrag', 100000, Location);
}
else
{
Super.takeDamage(Damage, instigatedBy, hitLocation, momentum, damageType);
}
if( bIsHeadShot && Health <= 0 )
{
KFGameType(Level.Game).DramaticEvent(0.03);
}
bBackstabbed = false;
}
function PlayDyingSound()
{
if( Level.NetMode!=NM_Client )
{
if ( bGibbed )
{
// Do nothing for now
PlaySound(GibGroupClass.static.GibSound(), SLOT_Pain,2.0,true,525);
return;
}
if( bDecapitated )
{
PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525);
}
else
{
PlaySound(DeathSound[0], SLOT_Pain,1.30,true,525);
}
}
}
// New Hit FX for Zombies!
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 ProjectileBloodSplat BloodHit;
local rotator SplatRot;
bRecentHit = Level.TimeSeconds - LastPainTime < 0.2;
LastDamageAmount = Damage;
// Call the modified version of the original Pawn playhit
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 );
// Do a zapped effect is someone shoots us and we're zapped to help show that the zed is taking more damage
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) );
//log("HitLocation "$Hitlocation) ;
if ( DamageType.Default.bCausesBlood && (!bRecentHit || (bRecentHit && (FRand() > 0.8))))
{
if ( !class'GameInfo'.static.NoBlood() && !class'GameInfo'.static.UseLowGore() )
{
if ( Momentum != vect(0,0,0) )
SplatRot = rotator(Normal(Momentum));
else
{
if ( InstigatedBy != None )
SplatRot = rotator(Normal(Location - InstigatedBy.Location));
else
SplatRot = rotator(Normal(Location - HitLocation));
}
BloodHit = Spawn(ProjectileBloodSplatClass,InstigatedBy,, HitLocation, SplatRot);
}
}
if( InstigatedBy != none && InstigatedBy.PlayerReplicationInfo != none &&
KFSteamStatsAndAchievements(InstigatedBy.PlayerReplicationInfo.SteamStatsAndAchievements) != none &&
Health <= 0 && Damage > DamageType.default.HumanObliterationThreshhold && Damage != 1000 && (!bDecapitated || bPlayBrainSplash) )
{
KFSteamStatsAndAchievements(InstigatedBy.PlayerReplicationInfo.SteamStatsAndAchievements).AddGibKill(class<DamTypeM79Grenade>(damageType) != none);
if ( self.IsA('ZombieFleshPound') )
{
KFSteamStatsAndAchievements(InstigatedBy.PlayerReplicationInfo.SteamStatsAndAchievements).AddFleshpoundGibKill();
}
}
DoDamageFX( HitBone, Damage, DamageType, Rotator(HitNormal) );
if (DamageType.default.DamageOverlayMaterial != None && Damage > 0 ) // additional check in case shield absorbed
SetOverlayMaterial( DamageType.default.DamageOverlayMaterial, DamageType.default.DamageOverlayTime, false );
}
// Modified version of the original Pawn playhit. Set up because we want our blood puffs to be directional based
// On the momentum of the bullet, not out from the center of the player
function OldPlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HitIndex)
{
local Vector HitNormal;
local vector BloodOffset, Mo;
local class<Effects> DesiredEffect;
local class<Emitter> DesiredEmitter;
local PlayerController Hearer;
if ( DamageType == None )
return;
if ( (Damage <= 0) && ((Controller == None) || !Controller.bGodMode) )
return;
if (Damage > DamageType.Default.DamageThreshold) //spawn some blood
{
HitNormal = Normal(HitLocation - Location);
// Play any set effect
if ( EffectIsRelevant(Location,true) )
{
DesiredEffect = DamageType.static.GetPawnDamageEffect(HitLocation, Damage, Momentum, self, (Level.bDropDetail || Level.DetailMode == DM_Low));
if ( DesiredEffect != None )
{
BloodOffset = 0.2 * CollisionRadius * HitNormal;
BloodOffset.Z = BloodOffset.Z * 0.5;
Mo = Momentum;
if ( Mo.Z > 0 )
Mo.Z *= 0.5;
spawn(DesiredEffect,self,,HitLocation + BloodOffset, rotator(Mo));
}
// Spawn any preset emitter
// Don't spawn the blood when we're zapped as we're spawning the zapped damage emitter elsewhere
if( !bZapped || (bZapped && !DamageType.default.bLocationalHit) )
{
DesiredEmitter = DamageType.Static.GetPawnDamageEmitter(HitLocation, Damage, Momentum, self, (Level.bDropDetail || Level.DetailMode == DM_Low));
if (DesiredEmitter != None)
{
if( InstigatedBy != none )
HitNormal = Normal((InstigatedBy.Location+(vect(0,0,1)*InstigatedBy.EyeHeight))-HitLocation);
spawn(DesiredEmitter,,,HitLocation+HitNormal + (-HitNormal * CollisionRadius), Rotator(HitNormal));
}
}
}
}
if ( Health <= 0 )
{
if ( PhysicsVolume.bDestructive && (PhysicsVolume.ExitActor != None) )
Spawn(PhysicsVolume.ExitActor);
return;
}
if ( Level.TimeSeconds - LastPainTime > 0.1 )
{
if ( InstigatedBy != None && (DamageType != None) && DamageType.default.bDirectDamage )
Hearer = PlayerController(InstigatedBy.Controller);
if ( Hearer != None )
Hearer.bAcuteHearing = true;
PlayTakeHit(HitLocation,Damage,damageType);
if ( Hearer != None )
Hearer.bAcuteHearing = false;
LastPainTime = Level.TimeSeconds;
}
}
// Implemented in subclasses - return false if there is some action that we don't want the direction hit to interrupt
simulated function bool HitCanInterruptAction()
{
return true;
}
function PlayDirectionalHit(Vector HitLoc)
{
local Vector X,Y,Z, Dir;
GetAxes(Rotation, X,Y,Z);
HitLoc.Z = Location.Z;
Dir = -Normal(Location - HitLoc);
if( !HitCanInterruptAction() )
{
return;
}
// random
if ( VSize(Location - HitLoc) < 1.0 )
Dir = VRand();
else Dir = -Normal(Location - HitLoc);
if ( Dir dot X > 0.7 || Dir == vect(0,0,0))
{
if( LastDamagedBy!=none && LastDamageAmount>0 )
{
if ( StunsRemaining != 0 && (LastDamageAmount >= (0.5 * default.Health) ||
(VSize(LastDamagedBy.Location - Location) <= (MeleeRange * 2) && ClassIsChildOf(LastDamagedbyType,class 'DamTypeMelee') &&
KFPawn(LastDamagedBy) != none && LastDamageAmount > (0.10* default.Health))) )
{
SetAnimAction(HitAnims[Rand(3)]);
bSTUNNED = true;
SetTimer(StunTime,false);
StunsRemaining--;
}
else
SetAnimAction(KFHitFront);
}
}
else if ( Dir Dot X < -0.7 )
SetAnimAction(KFHitBack);
else if ( Dir Dot Y > 0 )
SetAnimAction(KFHitRight);
else SetAnimAction(KFHitLeft);
}
simulated function PlayDirectionalDeath(Vector HitLoc); // Death animation replaced with ragdoll.
// Overridden so that anims don't get interrupted on the server if one is already playing
function bool IsHeadShot(vector loc, vector ray, float AdditionalScale)
{
local coords C;
local vector HeadLoc, B, M, diff;
local float t, DotMM, Distance;
local int look;
local bool bUseAltHeadShotLocation;
local bool bWasAnimating;
if (HeadBone == '')
return False;
// If we are a dedicated server estimate what animation is most likely playing on the client
if (Level.NetMode == NM_DedicatedServer)
{
if (Physics == PHYS_Falling)
PlayAnim(AirAnims[0], 1.0, 0.0);
else if (Physics == PHYS_Walking)
{
// Only play the idle anim if we're not already doing a different anim.
// This prevents anims getting interrupted on the server and borking things up - Ramm
if( !IsAnimating(0) && !IsAnimating(1) )
{
if (bIsCrouched)
{
PlayAnim(IdleCrouchAnim, 1.0, 0.0);
}
else
{
bUseAltHeadShotLocation=true;
}
}
else
{
bWasAnimating = true;
}
if ( bDoTorsoTwist )
{
SmoothViewYaw = Rotation.Yaw;
SmoothViewPitch = ViewPitch;
look = (256 * ViewPitch) & 65535;
if (look > 32768)
look -= 65536;
SetTwistLook(0, look);
}
}
else if (Physics == PHYS_Swimming)
PlayAnim(SwimAnims[0], 1.0, 0.0);
if( !bWasAnimating )
{
SetAnimFrame(0.5);
}
}
if( bUseAltHeadShotLocation )
{
HeadLoc = Location + (OnlineHeadshotOffset >> Rotation);
AdditionalScale *= OnlineHeadshotScale;
}
else
{
C = GetBoneCoords(HeadBone);
HeadLoc = C.Origin + (HeadHeight * HeadScale * AdditionalScale * C.XAxis);
}
//ServerHeadLocation = HeadLoc;
// Express snipe trace line in terms of B + tM
B = loc;
M = ray * (2.0 * CollisionHeight + 2.0 * CollisionRadius);
// Find Point-Line Squared Distance
diff = HeadLoc - B;
t = M Dot diff;
if (t > 0)
{
DotMM = M dot M;
if (t < DotMM)
{
t = t / DotMM;
diff = diff - (t * M);
}
else
{
t = 1;
diff -= M;
}
}
else
t = 0;
Distance = Sqrt(diff Dot diff);
return (Distance < (HeadRadius * HeadScale * AdditionalScale));
}
//TODO - log this to hell to find the last ANs,
// and look to consolidate any duplicate code,
// including multiple !=nones for the same target
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
{
local vector HitLocation, HitNormal;
local actor HitActor;
local Name TearBone;
local float dummy;
local Emitter BloodHit;
//local vector TraceDir;
if( Level.NetMode==NM_Client || Controller==None )
Return False; // Never should be done on client.
if ( Controller.Target!=none && Controller.Target.IsA('KFDoorMover'))
{
Controller.Target.TakeDamage(hitdamage, self ,HitLocation,pushdir, CurrentDamType);
Return True;
}
/*ClearStayingDebugLines();
TraceDir = Normal(Controller.Target.Location - Location);
DrawStayingDebugLine(Location, Location + (TraceDir * (MeleeRange * 1.4 + Controller.Target.CollisionRadius + CollisionRadius)) , 255,255,0);*/
// check if still in melee range
if ( (Controller.target != None) && (bSTUNNED == false) && (DECAP == false) && (VSize(Controller.Target.Location - Location) <= MeleeRange * 1.4 + Controller.Target.CollisionRadius + CollisionRadius)
&& ((Physics == PHYS_Flying) || (Physics == PHYS_Swimming) || (Abs(Location.Z - Controller.Target.Location.Z)
<= FMax(CollisionHeight, Controller.Target.CollisionHeight) + 0.5 * FMin(CollisionHeight, Controller.Target.CollisionHeight))) )
{
// See if a trace would hit a pawn (Have to turn of hit point collision so trace doesn't hit the Human Pawn's bullet whiz cylinder)
bBlockHitPointTraces = false;
HitActor = Trace(HitLocation, HitNormal, Controller.Target.Location , Location + EyePosition(), true);
bBlockHitPointTraces = true;
// If the trace wouldn't hit a pawn, do the old thing of just checking if there is something blocking the trace
if( Pawn(HitActor) == none )
{
// Have to turn of hit point collision so trace doesn't hit the Human Pawn's bullet whiz cylinder
bBlockHitPointTraces = false;
HitActor = Trace(HitLocation, HitNormal, Controller.Target.Location, Location, false);
bBlockHitPointTraces = true;
if ( HitActor != None )
return false;
}
if ( KFHumanPawn(Controller.Target) != none )
{
//TODO - line below was KFPawn. Does this whole block need to be KFPawn, or is it OK as KFHumanPawn?
KFHumanPawn(Controller.Target).TakeDamage(hitdamage, Instigator ,HitLocation,pushdir, CurrentDamType); //class 'KFmod.ZombieMeleeDamage');
if (KFHumanPawn(Controller.Target).Health <=0)
{
if ( !class'GameInfo'.static.UseLowGore() )
{
BloodHit = Spawn(class'KFMod.FeedingSpray',self,,Controller.Target.Location,rotator(pushdir)); //
KFHumanPawn(Controller.Target).SpawnGibs(rotator(pushdir), 1);
TearBone=KFPawn(Controller.Target).GetClosestBone(HitLocation,Velocity,dummy);
KFHumanPawn(Controller.Target).HideBone(TearBone);
}
// Give us some Health back
if (Health <= (1.0-FeedThreshold)*HealthMax)
{
Health += FeedThreshold*HealthMax * Health/HealthMax;
}
}
}
else if (Controller.target != None)
{
// Do more damage if you are attacking another zed so that zeds don't just stand there whacking each other forever! - Ramm
if( KFMonster(Controller.Target) != none )
{
hitdamage *= DamageToMonsterScale;
}
Controller.Target.TakeDamage(hitdamage, self ,HitLocation,pushdir, CurrentDamType); //class 'KFmod.ZombieMeleeDamage');
}
return true;
}
return false;
}
simulated function PlayDyingAnimation(class<DamageType> DamageType, vector HitLoc)
{
local vector shotDir, hitLocRel, deathAngVel, shotStrength;
local float maxDim;
local string RagSkelName;
local KarmaParamsSkel skelParams;
local bool PlayersRagdoll;
local PlayerController pc;
if( MyExtCollision!=None )
MyExtCollision.Destroy();
if ( Level.NetMode != NM_DedicatedServer )
{
// Is this the local player's ragdoll?
if(OldController != None)
pc = PlayerController(OldController);
if( pc != None && pc.ViewTarget == self )
PlayersRagdoll = true;
// In low physics detail, if we were not just controlling this pawn,
// and it has not been rendered in 3 seconds, just destroy it.
if( Level.NetMode == NM_ListenServer )
{
// For a listen server, use LastSeenOrRelevantTime instead of render time so
// monsters don't disappear for other players that the host can't see - Ramm
if( Level.PhysicsDetailLevel != PDL_High && !PlayersRagdoll && (Level.TimeSeconds-LastSeenOrRelevantTime)>3 ||
bGibbed )
{
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
if( Level.NetMode == NM_ListenServer )
{
bDestroyNextTick = true;
TimeSetDestroyNextTickTime = Level.TimeSeconds;
}
else
{
Destroy();
}
return;
}
}
else if( Level.PhysicsDetailLevel!=PDL_High && !PlayersRagdoll && (Level.TimeSeconds-LastRenderTime)>3 ||
bGibbed)
{
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
if( Level.NetMode == NM_ListenServer )
{
bDestroyNextTick = true;
TimeSetDestroyNextTickTime = Level.TimeSeconds;
}
else
{
Destroy();
}
return;
}
// Try and obtain a rag-doll setup. Use optional 'override' one out of player record first, then use the species one.
if( RagdollOverride != "")
RagSkelName = RagdollOverride;
else if(Species != None)
RagSkelName = Species.static.GetRagSkelName( GetMeshName() );
else RagSkelName = "Male1"; // Otherwise assume it is Male1 ragdoll were after here.
KMakeRagdollAvailable();
if( KIsRagdollAvailable() && RagSkelName != "" )
{
skelParams = KarmaParamsSkel(KParams);
skelParams.KSkeleton = RagSkelName;
// Stop animation playing.
StopAnimating(true);
// StopAnimating() resets the neck bone rotation, we have to set it again
// if the zed was decapitated the cute way
if ( class'GameInfo'.static.UseLowGore() && NeckRot != rot(0,0,0) )
{
SetBoneRotation('neck', NeckRot);
}
if( DamageType != none )
{
if ( DamageType.default.bLeaveBodyEffect )
TearOffMomentum = vect(0,0,0);
if ( DamageType.default.bKUseOwnDeathVel )
{
RagDeathVel = DamageType.default.KDeathVel;
RagDeathUpKick = DamageType.default.KDeathUpKick;
RagShootStrength = DamageType.default.KDamageImpulse;
}
}
// Set the dude moving in direction he was shot in general
shotDir = Normal(GetTearOffMomemtum());
shotStrength = RagDeathVel * shotDir;
// Calculate angular velocity to impart, based on shot location.
hitLocRel = TakeHitLocation - Location;
if( DamageType.default.bLocationalHit )
{
hitLocRel.X *= RagSpinScale;
hitLocRel.Y *= RagSpinScale;
if( Abs(hitLocRel.X) > RagMaxSpinAmount )
{
if( hitLocRel.X < 0 )
{
hitLocRel.X = FMax((hitLocRel.X * RagSpinScale), (RagMaxSpinAmount * -1));
}
else
{
hitLocRel.X = FMin((hitLocRel.X * RagSpinScale), RagMaxSpinAmount);
}
}
if( Abs(hitLocRel.Y) > RagMaxSpinAmount )
{
if( hitLocRel.Y < 0 )
{
hitLocRel.Y = FMax((hitLocRel.Y * RagSpinScale), (RagMaxSpinAmount * -1));
}
else
{
hitLocRel.Y = FMin((hitLocRel.Y * RagSpinScale), RagMaxSpinAmount);
}
}
}
else
{
// We scale the hit location out sideways a bit, to get more spin around Z.
hitLocRel.X *= RagSpinScale;
hitLocRel.Y *= RagSpinScale;
}
//log("hitLocRel.X = "$hitLocRel.X$" hitLocRel.Y = "$hitLocRel.Y);
//log("TearOffMomentum = "$VSize(GetTearOffMomemtum()));
// If the tear off momentum was very small for some reason, make up some angular velocity for the pawn
if( VSize(GetTearOffMomemtum()) < 0.01 )
{
//Log("TearOffMomentum magnitude of Zero");
deathAngVel = VRand() * 18000.0;
}
else
{
deathAngVel = RagInvInertia * (hitLocRel cross shotStrength);
}
// Set initial angular and linear velocity for ragdoll.
// Scale horizontal velocity for characters - they run really fast!
if ( DamageType.Default.bRubbery )
skelParams.KStartLinVel = vect(0,0,0);
if ( Damagetype.default.bKUseTearOffMomentum )
skelParams.KStartLinVel = GetTearOffMomemtum() + Velocity;
else
{
skelParams.KStartLinVel.X = 0.6 * Velocity.X;
skelParams.KStartLinVel.Y = 0.6 * Velocity.Y;
skelParams.KStartLinVel.Z = 1.0 * Velocity.Z;
skelParams.KStartLinVel += shotStrength;
}
// If not moving downwards - give extra upward kick
if( !DamageType.default.bLeaveBodyEffect && !DamageType.Default.bRubbery && (Velocity.Z > -10) )
skelParams.KStartLinVel.Z += RagDeathUpKick;
if ( DamageType.Default.bRubbery )
{
Velocity = vect(0,0,0);
skelParams.KStartAngVel = vect(0,0,0);
}
else
{
skelParams.KStartAngVel = deathAngVel;
// Set up deferred shot-bone impulse
maxDim = Max(CollisionRadius, CollisionHeight);
skelParams.KShotStart = TakeHitLocation - (1 * shotDir);
skelParams.KShotEnd = TakeHitLocation + (2*maxDim*shotDir);
skelParams.KShotStrength = RagShootStrength;
}
//log("RagDeathVel = "$RagDeathVel$" KShotStrength = "$skelParams.KShotStrength$" RagDeathUpKick = "$RagDeathUpKick);
// If this damage type causes convulsions, turn them on here.
if(DamageType != none && DamageType.default.bCauseConvulsions)
{
RagConvulseMaterial=DamageType.default.DamageOverlayMaterial;
skelParams.bKDoConvulsions = true;
}
// Turn on Karma collision for ragdoll.
KSetBlockKarma(true);
// Set physics mode to ragdoll.
// This doesn't actaully start it straight away, it's deferred to the first tick.
SetPhysics(PHYS_KarmaRagdoll);
// If viewing this ragdoll, set the flag to indicate that it is 'important'
if( PlayersRagdoll )
skelParams.bKImportantRagdoll = true;
skelParams.bRubbery = DamageType.Default.bRubbery;
bRubbery = DamageType.Default.bRubbery;
skelParams.KActorGravScale = RagGravScale;
return;
}
// jag
}
// non-ragdoll death fallback
Velocity += GetTearOffMomemtum();
BaseEyeHeight = Default.BaseEyeHeight;
SetTwistLook(0, 0);
SetInvisibility(0.0);
// We don't do this - Ramm
//PlayDirectionalDeath(HitLoc);
SetPhysics(PHYS_Falling);
}
// Give zombies forward momentum with jumps.
function bool DoJump( bool bUpdating )
{
if ( !bIsCrouched && !bWantsToCrouch && ((Physics == PHYS_Walking) || (Physics == PHYS_Ladder) || (Physics == PHYS_Spider)) )
{
PlayOwnedSound(JumpSound, SLOT_Pain, GruntVolume,,80);
if ( Role == ROLE_Authority )
{
if ( (Level.Game != None) && (Level.Game.GameDifficulty > 2) )
MakeNoise(0.1 * Level.Game.GameDifficulty);
if ( bCountJumps && (Inventory != None) )
Inventory.OwnerEvent('Jumped');
}
if ( Physics == PHYS_Spider )
Velocity = JumpZ * Floor;
else if ( Physics == PHYS_Ladder )
Velocity.Z = 0;
else if ( bIsWalking )
{
Velocity.Z = Default.JumpZ;
Velocity.X = (Default.JumpZ * 0.5);
}
else
{
Velocity.Z = JumpZ;
Velocity.X = (JumpZ * 0.5);
}
if ( (Base != None) && !Base.bWorldGeometry )
{
Velocity.Z += Base.Velocity.Z;
Velocity.X += Base.Velocity.X;
}
SetPhysics(PHYS_Falling);
return true;
}
return false;
}
// Overridden to handle making attached explosives explode when this pawn dies
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
local int i;
for( i=0; i<Attached.length; i++ )
{
if( SealSquealProjectile(Attached[i])!=None )
{
SealSquealProjectile(Attached[i]).HandleBasePawnDestroyed();
}
}
super.Died(Killer,damageType,HitLocation);
}
// Overridden to handle making attached explosives explode when this pawn dies
simulated function Destroyed()
{
local int i;
for( i=0; i<Attached.length; i++ )
{
if( Emitter(Attached[i])!=None && Attached[i].IsA('DismembermentJet') )
{
Emitter(Attached[i]).Kill();
Attached[i].LifeSpan = 2;
}
// Make attached explosives blow up when this pawn dies
if( SealSquealProjectile(Attached[i])!=None )
{
SealSquealProjectile(Attached[i]).HandleBasePawnDestroyed();
}
}
if( MyExtCollision!=None )
MyExtCollision.Destroy();
if( PlayerShadow != None )
PlayerShadow.Destroy();
if ( FlamingFXs != none )
{
FlamingFXs.Emitters[0].SkeletalMeshActor = none;
FlamingFXs.Destroy();
}
if(RealtimeShadow !=none)
RealtimeShadow.Destroy();
if( SeveredLeftArm != none )
{
SeveredLeftArm.Destroy();
}
if( SeveredRightArm != none )
{
SeveredRightArm.Destroy();
}
if( SeveredRightLeg != none )
{
SeveredRightLeg.Destroy();
}
if( SeveredLeftLeg != none )
{
SeveredLeftLeg.Destroy();
}
if( SeveredHead != none )
{
SeveredHead.Destroy();
}
if(SpawnVolume != none)
{
SpawnVolume.RemoveZEDFromSpawnList(self);
}
RemoveFlamingEffects();
Super.Destroyed();
}
simulated function ZombieCrispUp()
{
bAshen = true;
bCrispified = true;
SetBurningBehavior();
if ( Level.NetMode == NM_DedicatedServer || class'GameInfo'.static.UseLowGore() )
{
Return;
}
Skins[0]=Combiner'PatchTex.Common.BurnSkinEmbers_cmb';
Skins[1]=Combiner'PatchTex.Common.BurnSkinEmbers_cmb';
Skins[2]=Combiner'PatchTex.Common.BurnSkinEmbers_cmb';
Skins[3]=Combiner'PatchTex.Common.BurnSkinEmbers_cmb';
}
// Set the zed to the on fire behavior
simulated function SetBurningBehavior()
{
if( Role == Role_Authority )
{
Intelligence = BRAINS_Retarded; // burning dumbasses!
SetGroundSpeed(OriginalGroundSpeed * 0.8);
AirSpeed *= 0.8;
WaterSpeed *= 0.8;
// Make them less accurate while they are burning
if( Controller != none )
{
MonsterController(Controller).Accuracy = -5; // More chance of missing. (he's burning now, after all) :-D
}
}
// Set the forward movement anim to a random burning anim
MovementAnims[0] = BurningWalkFAnims[Rand(3)];
WalkAnims[0] = BurningWalkFAnims[Rand(3)];
// Set the rest of the movement anims to the headless anim (not sure if these ever even get played) - Ramm
MovementAnims[1] = BurningWalkAnims[0];
WalkAnims[1] = BurningWalkAnims[0];
MovementAnims[2] = BurningWalkAnims[1];
WalkAnims[2] = BurningWalkAnims[1];
MovementAnims[3] = BurningWalkAnims[2];
WalkAnims[3] = BurningWalkAnims[2];
}
// Turn off the on-fire behavior
simulated function UnSetBurningBehavior()
{
local int i;
// Don't turn off this behavior until the harpoon stun is over
if( bHarpoonStunned )
{
return;
}
if ( Role == Role_Authority )
{
Intelligence = default.Intelligence;
if( !bZapped )
{
SetGroundSpeed(GetOriginalGroundSpeed());
AirSpeed = default.AirSpeed;
WaterSpeed = default.WaterSpeed;
}
// Set normal accuracy
if ( Controller != none )
{
MonsterController(Controller).Accuracy = MonsterController(Controller).default.Accuracy;
}
}
bAshen = False;
// restore regular anims
for ( i = 0; i < 4; i++ )
{
MovementAnims[i] = default.MovementAnims[i];
WalkAnims[i] = default.WalkAnims[i];
}
}
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(default.GroundSpeed);
}
}
}
// We can add blood streak decals here, as the Actor's body moves in Ragdoll
event KImpact(actor other, vector pos, vector impactVel, vector impactNorm)
{
local int numSounds, soundNum;
local vector WallHit, WallNormal;
local Actor WallActor;
local KFBloodStreakDecal 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);
//Added no blood for low gore
if ( WallActor != None && Level.TimeSeconds > LastStreakTime + BloodStreakInterval && !class'GameInfo'.static.UseLowGore() )
{
if ( StreakDist < 1400 ) //0.75m
{
//log("Streak dist too small "$ Sqrt(StreakDist)/50$"m");
return;
}
Streak = spawn(class 'KFMod.KFBloodStreakDecal',,, 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;
}
}
simulated function RemoveFlamingEffects()
{
local int i;
if ( Level.NetMode == NM_DedicatedServer )
{
return;
}
for ( i = 0; i < Attached.length; i++ )
{
if ( xEmitter(Attached[i]) != none )
{
xEmitter(Attached[i]).mRegen = false;
Attached[i].LifeSpan = 2;
}
else if ( Emitter(Attached[i]) != None && !Attached[i].IsA('DismembermentJet') )
{
Emitter(Attached[i]).Kill();
Attached[i].LifeSpan = 2;
}
}
}
simulated function TurnOff()
{
RemoteRole = ROLE_SimulatedProxy;
}
// this could have been responsible for making the zombies "lethargic" in wander state.
// they should retain the same walk speed at all times.
event SetWalking(bool bNewIsWalking);
function Trigger( actor Other, pawn EventInstigator )
{
if( Controller!=None )
Controller.Trigger(Other,EventInstigator);
}
// Laught at victory of boss battle.
function bool SetBossLaught()
{
Return False; // Normal zeds have no feelings..
}
state DoorBashing
{
simulated function bool HitCanInterruptAction()
{
return false;
}
function Tick( float Delta )
{
Acceleration = vect(0,0,0);
global.Tick(Delta);
}
Begin:
Sleep(GetAnimDuration('DoorBash') + 0.1);
GotoState('');
}
static simulated function PreCacheAssets(LevelInfo myLevel)
{
//log("*********");
//log(default.Class);
//DynamicLoadSounds();
PreCacheStaticMeshes(myLevel);
if(myLevel != none)
PreCacheMaterials(myLevel);
//DynamicLoadMeshAndSkins();
}
static simulated function DynamicLoadMeshAndSkins()
{
/*
local int i;
if(default.MeshRef != "")
UpdateDefaultMesh( SkeletalMesh(DynamicLoadObject(default.MeshRef, Class'SkeletalMesh')) );
for ( i = 0; i < default.SkinsRef.Length; i++ )
{
if(default.SkinsRef[i] != "")
default.Skins[i] = Material(DynamicLoadObject(default.SkinsRef[i], class'Material'));
}
*/
}
static simulated function DynamicLoadSounds()
{
/* local int i;
if( default.AmbientSoundRef == "" )
{
log("don't have sound refs, bailing out");
return;
}
for(i = 0;i < default.HitSoundRef.length;i++)
{
default.HitSound[i] = Sound(DynamicLoadObject(default.HitSoundRef[i], Class'Sound'));
}
for(i = 0;i < default.ChallengeSoundRef.length;i++)
{
default.ChallengeSound[i] = Sound(DynamicLoadObject(default.ChallengeSoundRef[i], Class'Sound'));
}
for(i = 0;i < default.DeathSoundRef.length;i++)
{
default.DeathSound[i] = Sound(DynamicLoadObject(default.DeathSoundRef[i], Class'Sound'));
}
default.AmbientSound = Sound(DynamicLoadObject(default.AmbientSoundRef, Class'Sound'));
default.MoanVoice = Sound(DynamicLoadObject(default.MoanVoiceRef, Class'Sound'));
default.JumpSound = Sound(DynamicLoadObject(default.JumpSoundRef, Class'Sound'));
if( default.MeleeAttackHitSoundRef != "" )
default.MeleeAttackHitSound = Sound(DynamicLoadObject(default.MeleeAttackHitSoundRef, Class'Sound'));
*/
}
static simulated function PreCacheStaticMeshes(LevelInfo myLevel)
{//should be derived and used.
/*
if( default.DetachedArmClassRef != "" )
default.DetachedArmClass = class<SeveredAppendage>(DynamicLoadObject(default.DetachedArmClassRef, Class'Class'));
if( default.DetachedLegClassRef != "" )
default.DetachedLegClass = class<SeveredAppendage>(DynamicLoadObject(default.DetachedLegClassRef, Class'Class'));
if( default.DetachedHeadClassRef != "" )
default.DetachedHeadClass = class<SeveredAppendage>(DynamicLoadObject(default.DetachedHeadClassRef, Class'Class'));
if( default.DetachedSpecialArmClassRef != "" )
default.DetachedSpecialArmClass = class<SeveredAppendage>(DynamicLoadObject(default.DetachedSpecialArmClassRef, Class'Class'));
*/
if(myLevel == none)
return;
if( default.DetachedArmClass != none )
myLevel.AddPrecacheStaticMesh(default.DetachedArmClass.default.StaticMesh);
if( default.DetachedLegClass != none )
myLevel.AddPrecacheStaticMesh(default.DetachedLegClass.default.StaticMesh);
if( default.DetachedHeadClass != none )
myLevel.AddPrecacheStaticMesh(default.DetachedHeadClass.default.StaticMesh);
if( default.DetachedSpecialArmClass != none )
myLevel.AddPrecacheStaticMesh(default.DetachedSpecialArmClass.default.StaticMesh);
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{//should be derived and used.
}
defaultproperties
{
RootBone="CHR_Pelvis"
HeadBone=head//"CHR_Head"//head
SpineBone1="CHR_Spine2"//spine
SpineBone2="CHR_Spine3"
FireRootBone="CHR_Spine1"
LeftShoulderBone=lshoulder//"CHR_LArmCollar"//lshoulder
RightShoulderBone=rshoulder//"CHR_RArmCollar"//rshoulder
LeftThighBone=lthigh//"CHR_LThigh"//lthigh
RightThighBone=rthigh//"CHR_RThigh"//rthigh
LeftFArmBone=lfarm//"CHR_LArmForeArm"//lfarm
RightFArmBone=rfarm//"CHR_RArmForeArm"//rfarm
LeftFootBone=lfoot//"CHR_LAnkle"//lfoot
RightFootBone=rfoot//"CHR_RAnkle"//rfoot
LeftHandBone=lefthand//"CHR_LArmPalm"//lefthand
RightHandBone=righthand//"CHR_RArmPalm"//righthand
NeckBone="CHR_Neck"
ExtCollAttachBoneName="Bip01"
HeadlessWalkAnims(0)="WalkF_Headless"
HeadlessWalkAnims(1)="WalkB_Headless"
HeadlessWalkAnims(2)="WalkL_Headless"
HeadlessWalkAnims(3)="WalkR_Headless"
BurningWalkFAnims(0)="WalkF_Fire"
BurningWalkFAnims(1)="WalkF_Fire"
BurningWalkFAnims(2)="WalkF_Fire"
BurningWalkAnims(0)="WalkB_Fire"
BurningWalkAnims(1)="WalkL_Fire"
BurningWalkAnims(2)="WalkR_Fire"
MeleeAnims(0)="Claw"
MeleeAnims(1)="Claw2"
MeleeAnims(2)="Claw3"
HitAnims(0)="HitF"
HitAnims(1)="HitF2"
HitAnims(2)="HitF3"
KFHitFront="HitReactionF"
KFHitBack="HitReactionB"
KFHitLeft="HitReactionL"
KFHitRight="HitReactionR"
MaxSpineVariation=1000
MaxContortionPercentage=0.250000
MinTimeBetweenPainAnims=0.500000
playedHit=True
ZombieDamType(0)=Class'KFMod.ZombieMeleeDamage'
ZombieDamType(1)=Class'KFMod.ZombieMeleeDamage'
ZombieDamType(2)=Class'KFMod.ZombieMeleeDamage'
PuntAnim="PoundPunt"
BloodStreakInterval=0.25000
DeathAnim(0)=
DeathAnim(1)=
DeathAnim(2)=
DeathAnim(3)=
bCanDodge=False
ChallengeSound(0)=None
ChallengeSound(1)=None
ChallengeSound(2)=None
ChallengeSound(3)=None
GruntVolume=0.500000
FootstepVolume=1.000000
GibGroupClass=Class'KFMod.KFNoGibGroup'
SoundGroupClass=Class'KFMod.KFMaleZombieSounds'
IdleHeavyAnim="Idle_LargeZombie"
IdleRifleAnim="Idle_LargeZombie"
FireHeavyRapidAnim="MeleeAttack"
FireHeavyBurstAnim="MeleeAttack"
FireRifleRapidAnim="MeleeAttack"
FireRifleBurstAnim="MeleeAttack"
DeResTime=6.000000
DeResMat0=Texture'KFCharacters.KFDeRez'
DeResMat1=Texture'KFCharacters.KFDeRez'
DeResLiftVel=(Points=(,(InVal=0.000000,OutVal=0.000000),(InVal=0.000000,OutVal=0.000000)))
DeResLiftSoftness=(Points=((OutVal=0.000000),(InVal=0.000000,OutVal=0.000000),(InVal=0.000000,OutVal=0.000000)))
DeResLateralFriction=0.000000
CorpseLifeSpan = 120.0
RequiredEquipment(0)="none"
RequiredEquipment(1)="none"
bCanSwim=False
bCanStrafe=False
bSameZoneHearing=True
bAdjacentZoneHearing=True
bMuffledHearing=False
bAroundCornerHearing=True
bCanUse=False
PeripheralVision=360.000000
SkillModifier=5.000000
MeleeRange=50.000000
JumpZ=320.000000
MaxFallSpeed=2500.0//600.000000
ControllerClass=Class'KFMod.KFMonsterController'
TurnLeftAnim="TurnLeft"
TurnRightAnim="TurnRight"
IdleCrouchAnim="Idle_LargeZombie"
IdleWeaponAnim="Idle_LargeZombie"
IdleRestAnim="Idle_LargeZombie"
bDramaticLighting=False
AmbientGlow=0
bFullVolume=False
SoundRadius=80.0
SoundVolume=50
AmbientSoundScaling=6.83
TransientSoundVolume=1.000000
bBlockKarma=True
Mass=100.0//300.000000
Begin Object Class=KarmaParamsSkel Name=PawnKParams
KFriction=1.3
KRestitution=0.2
KAngularDamping=0.05
KLinearDamping=0.15
KBuoyancy=1
KStartEnabled=True
KImpactThreshold=85
KVelDropBelowThreshold=50
KConvulseSpacing=(Min=0.5,Max=2.2)
bHighDetailOnly=False
End Object
KParams=KarmaParamsSkel'KFMod.KFMonster.PawnKParams'
BurnEffect=class'KFMonsterFlame'
AltBurnEffect=class'KFAltMonsterFlame'
MonsterHeadGiblet = class 'ClotGibHead'
MonsterThighGiblet = class 'ClotGibThigh'
MonsterArmGiblet = class 'ClotGibArm'
MonsterLegGiblet = class 'ClotGibLeg'
MonsterTorsoGiblet = class 'ClotGibTorso'
MonsterLowerTorsoGiblet = class 'ClotGibLowerTorso'
WalkingPct=+1.0
CrouchedPct=+1.0
bCanCrouch=false
FeedThreshold = 0.10
CollisionRadius=26.000000
bRealtimeShadows= false
SightRadius=+20000.0
Alertness = 1.0
HearingThreshold=+20000.0
HeadRadius=7.0
Intelligence=BRAINS_Human
BileFrequency=0.5
RagdollLifeSpan=30.000000
RagInvInertia=4.0
RagDeathVel=100.000000
RagDeathUpKick=0.000000
RagShootStrength=200
RagSpinScale=7.5
RagMaxSpinAmount=100
RagGravScale=1.0
RagImpactSoundInterval=0.25
RagImpactSounds(0)=sound'KF_EnemyGlobalSnd.Zomb_BodyImpact'
SeveredHeadAttachScale=1.0
SeveredLegAttachScale=1.0
SeveredArmAttachScale=1.0
SeveredHeadAttachClass = class'SeveredHeadAttachment'
SeveredArmAttachClass = class'SeveredArmAttachment'
SeveredLegAttachClass = class'SeveredLegAttachment'
NeckSpurtEmitterClass=class'DismembermentJetHead'
NeckSpurtNoGibEmitterClass=class'DismembermentJetDecapitate'
LimbSpurtEmitterClass=class'DismembermentJetLimb'
ObliteratedEffectClass=class'PlayerObliteratedEmitter'
// These two are fallbacks. Should assign these in subclasses
//DetachedArmClassRef="ROEffects.SeveredArm"
//DetachedLegClassRef="ROEffects.SeveredLeg"
//DetachedHeadClassRef="KFMod.SeveredHead"
ProjectileBloodSplatClass=class'ROEffects.ProjectileBloodSplat'
HeadLessDeathSound=sound'KF_EnemyGlobalSnd.Zomb_HeadlessDie'
DecapitationSound=sound'KF_EnemyGlobalSnd.Generic_Decap'
//MeleeAttackHitSoundRef="KF_EnemyGlobalSnd.Zomb_HitPlayer_Claw"
MoanVolume=1.5
PlayerCountHealthScale=0.0
BleedOutDuration=5.0
PoundRageBumpDamScale=1.0
HiddenGroundSpeed=300.0
HeadHeight=2.0
HeadScale=1.1
CrispUpThreshhold=5
OnlineHeadshotScale=1.0
HeadHealth=25
PlayerNumHeadHealthScale=0.0
MotionDetectorThreat=1.0
StunTime=1.0
StunsRemaining=-1
ZapDuration=4.0
ZappedSpeedMod=0.5
ZapThreshold=0.25
ZappedDamageMod=2.0
ZapResistanceScale=2.0
DamageToMonsterScale=3.0
HumanBileAggroChance=0.75
bHarpoonToHeadStuns=true
bHarpoonToBodyStuns=true
}