//=============================================================================
// KFPawn
//=============================================================================
class KFPawn extends xPawn
      dependsOn(xPawnGibGroup);

#exec OBJ LOAD FILE=Inf_Weapons.uax
#exec OBJ LOAD FILE=Inf_Player.uax

// From XPawn
var(Gib) class<xPawnGibGroup> GibGroupClass;
var(Gib) int GibCountCalf;
var(Gib) int GibCountForearm;
var(Gib) int GibCountHead;
var(Gib) int GibCountTorso;
var(Gib) int GibCountUpperArm;

var string KFBSkin;
var string KFFSkin;
var mesh  KFSMesh;

var bool bCanBeHealed;
var float healthToGive, lastHealTime;

var bool bResetingAnimAct;
var float NextBileTime, BileFrequency, AnimActResetTime;
var int BileCount;
var Pawn BileInstigator;
var class<DamTypeVomit> LastBileDamagedByType;

var name ClientIdleWeaponAnim;

var bool bThrowingNade; // ARE WE ALREADY THROWING ONE?

var Inventory SecondaryItem;

// Player Health Bar textures
var Texture TeamBeaconTexture, NoEntryTexture;
var Material TeamBeaconBorderMaterial;

var class<DamageType> LastHitDamType; // records the last kind of damge you took (for siren hack)

var (Global) Mesh SafeMesh;

// 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 int LastBurnDamage; // Record the last amount of Fire damage the pawn suffered.
var Emitter ItBUURRNNNS;

var bool bBurnified;
var bool bBurnApplied;

var pawn LastDamagedBy,BurnInstigator;

var() globalconfig bool bDetailedShadows,bRealDeathType,bRealtimeShadows;

var KFLevelRules LevRls;
var PlayerReplicationInfo OwnerPRI;

var byte bIsQuickHealing;

/* Feet adjusters ================*/
var bool bHasFootAdjust;
var globalconfig bool bDoAdjustFeet;
var float MaxZHeight,MinZHeight,OldAdjust[2];
var KFPawnFootAdjuster Adjuster;
var Class<SPECIES_KFMaleHuman> FeetAdjSpec;

// Animations
const NUM_FIRE_ANIMS = 4;
var name FireAnims[NUM_FIRE_ANIMS];
var name FireAltAnims[NUM_FIRE_ANIMS];
var name FireCrouchAnims[NUM_FIRE_ANIMS];
var name FireCrouchAltAnims[NUM_FIRE_ANIMS];
var name HitAnims[4];
var name PostFireBlendStandAnim;
var name PostFireBlendCrouchAnim;


// Footstepping
var(Sounds)     float           FootStepSoundRadius;    // The radius that footstep sounds can be heard
var(Sounds)     float           QuietFootStepVolume;    // The amount to scale footstepsounds when the player is walking slowly
var(Sounds)     sound           SoundFootsteps[20];     // Indexed by ESurfaceTypes (sorry about the literal).
var(Bob)        float           BobSpeedModifier;       // A general scalar for walkbob/footstep sound speed
var(Bob)        float           BobScaleModifier;       // A general scalar for walkbob intensity

var             bool            bMovementDisabled;      // The player can't move right now
var             float           StopDisabledTime;       // When the player can move again;

// 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 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 <ROBloodSpurt>         BleedingEmitterClass;      // class of the bleeding emitter
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 RoleInfo to match the player model
var class <SeveredAppendage>    DetachedLegClass;       // class of detached arm to spawn for this pawn. Modified by the RoleInfo 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 class <Emitter>             ObliteratedEffectClass; // class of detached arm to spawn for this pawn. Modified by the RoleInfo to match the player model
var(Sounds) sound               DecapitationSound;      //The sound of this players head exploding

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 float   LastDropCashMessageTime;
var float   DropCashMessageDelay;

var bool    bDestroyAfterRagDollTick;   // Wait until the ragdoll tick has happened and then destroy. Prevents crashes where we try and destroy the actor right after initializing ragdoll
var bool    bProcessedRagTickDestroy;   // Already called destroy for a bDestroyAfterRagDollTick setting

// Collision
var     KFBulletWhipAttachment  AuxCollisionCylinder;   // Additional collision cylinder for detecting bullets passing by
var                 bool        SavedAuxCollision;      // Saved aux collision cylinder status

var         float               NadeThrowTimeout;       // Little hack to timeout the grenade throwing flag on the client in case it gets stuck. At some point we should try and track down what is actually causing it to get stuck, this is just a quick fix - Ramm

var     Emitter    AttachedEmitter;

/* === Replicated Custom Animation Struct.

Used with ACTION_PlayKFReplicatedAnim Objects to
allow level designers to play replicated scripted
animations on pawns */

struct SScriptedAnimRepInfo
{
    var         name                BaseAnim;
    var         float               BlendInTime;
    var         float               BlendOutTime;
    var         float               AnimRate;
    var         byte                AnimIterations;
    var         bool                bLoopAnim;
    var         float               StartFrame;
};

var SScriptedAnimRepInfo            ScriptedAnimData;



// Hit detection debugging - Only use when debugging
/*var vector DrawLocation;
var rotator DrawRotation;
var int DrawIndex;
var vector HitStart;
var vector HitEnd;
var byte HitPointDebugByte;
var byte OldHitPointDebugByte;*/

replication
{
    reliable if(Role == ROLE_Authority)
        bBurnified,QuickHeal,ClientCurrentWeaponSold,ClientForceChangeWeapon;

    reliable if(Role < ROLE_Authority)
        ServerBuyPotato,ServerBuyWeapon,ServerSellWeapon,ServerBuyKevlar,ServerBuyFirstAid,ServerBuyAmmo,ServerSellAmmo;

    reliable if(Role < ROLE_Authority)
        SecondaryItem,TossCash;

    reliable if ( bNetDirty && (Role == Role_Authority) && bNetOwner )
        bMovementDisabled;

    reliable if(Role == ROLE_Authority && bNetDirty && !bNetOwner)
       ScriptedAnimData;

    // Hit detection debugging - Only use when debugging
    /*reliable if (Role == ROLE_Authority)
        DrawLocation,DrawRotation,DrawIndex,HitPointDebugByte,HitStart,HitEnd;*/
}

simulated function PostBeginPlay()
{
    Super(UnrealPawn).PostBeginPlay();
    AssignInitialPose();

    if( bActorShadows && bPlayerShadows && (Level.NetMode!=NM_DedicatedServer) )
    {
        if( bDetailedShadows )
            PlayerShadow = Spawn(class'KFShadowProject',Self,'',Location);
        else PlayerShadow = Spawn(class'ShadowProjector',Self,'',Location);
        PlayerShadow.ShadowActor = self;
        PlayerShadow.bBlobShadow = bBlobShadow;
        PlayerShadow.LightDirection = Normal(vect(1,1,3));
        PlayerShadow.InitShadow();
    }

    // Only need this on the server
    if( Role == Role_Authority/*IsLocallyControlled() || Level.NetMode == NM_DedicatedServer || Level.NetMode == NM_ListenServer*/ )
    {
        if (  AuxCollisionCylinder == none )
        {
            AuxCollisionCylinder = Spawn(class 'KFBulletWhipAttachment',self);
            //AttachToBone(AuxCollisionCylinder, 'CHR_Pelvis');

            ///AuxCollisionCylinder = Spawn(class 'ExtendedZCollision',self);
            //MyExtCollision.SetCollisionSize(ColRadius,ColHeight);

            AuxCollisionCylinder.bHardAttach = true;
            //AttachPos = Location + (ColOffset >> Rotation);
            AuxCollisionCylinder.SetLocation( Location );
            AuxCollisionCylinder.SetPhysics( PHYS_None );
            AuxCollisionCylinder.SetBase( self );

        }
        SavedAuxCollision = AuxCollisionCylinder.bCollideActors;
    }
}

// Setters for extra collision cylinders
simulated function ToggleAuxCollision(bool newbCollision)
{
    if ( !newbCollision )
    {
        SavedAuxCollision = AuxCollisionCylinder.bCollideActors;

        AuxCollisionCylinder.SetCollision(false);
    }
    else
    {
        AuxCollisionCylinder.SetCollision(SavedAuxCollision);
    }
}

function PossessedBy(Controller C)
{
    Super.PossessedBy(C);
    if ( C.PlayerReplicationInfo != None )
        OwnerPRI = C.PlayerReplicationInfo;
}

simulated Function PostNetBeginPlay()
{
    EnableChannelNotify(1,1);
    EnableChannelNotify(2,1);
    super.PostNetBeginPlay();
}

simulated function string GetDefaultCharacter()
{
    return "Corporal_Lewis";
}

simulated function Setup(xUtil.PlayerRecord rec, optional bool bLoadNow)
{
    local class<Emitter> AttachedEmitterClass;
    local vector AttachOffset;

    if( rec.Species==None || Class<SPECIES_KFMaleHuman>(rec.Species)==None )
        rec = class'xUtil'.static.FindPlayerRecord(GetDefaultCharacter());
    Species = rec.Species;
    RagdollOverride = rec.Ragdoll;
    if ( Species!=None && !Species.static.Setup(self,rec) )
    {
        rec = class'xUtil'.static.FindPlayerRecord(GetDefaultCharacter());
        Species = rec.Species;
        RagdollOverride = rec.Ragdoll;
        if ( !Species.static.Setup(self,rec) )
            return;
    }

    if( rec.AttachedEmitter != "" && AttachedEmitter == none && Level.NetMode != NM_DedicatedServer)
    {
        AttachedEmitterClass = class<Emitter>(DynamicLoadObject(rec.AttachedEmitter,class'Class'));
        AttachedEmitter = Spawn(AttachedEmitterClass, self);
        if( AttachToBone(AttachedEmitter, rec.BoneName ) )
        {
            AttachOffset.X = rec.XOffset;
            AttachOffset.Y = rec.YOffset;
            AttachOffset.Z = rec.ZOffset;
            AttachedEmitter.SetRelativeLocation( AttachOffset );//vect(rec.XOffset, rec.YOffset, rec.ZOffset) );
        }
    }

    if( Class<SPECIES_KFMaleHuman>(Species) != none )
    {
        DetachedArmClass = Class<SPECIES_KFMaleHuman>(Species).default.DetachedArmClass;
        DetachedLegClass = Class<SPECIES_KFMaleHuman>(Species).default.DetachedLegClass;
    }

    ResetPhysicsBasedAnim();
//  bHasFootAdjust = False;
//  FeetAdjSpec = Class<SPECIES_KFMaleHuman>(rec.Species);
//  if( !bDoAdjustFeet || FeetAdjSpec==None || Level.NetMode==NM_DedicatedServer )
//      return;
//  if( Adjuster!=None )
//      Adjuster.Destroy();
//  Adjuster = Spawn(Class'KFPawnFootAdjuster',,,vect(0,0,0),rot(0,0,0));
//  Adjuster.AdjustingPawn = Self;
//  Adjuster.SpecType = FeetAdjSpec;
//  Adjuster.LinkMesh(Mesh);
}

// Clean up weapon inventory before level change since certain weapons
// (sniper scopes with 3d scopes) won't get properly garbage collected
// otherwise. This lead to the webadmin and memory leak issues - Ramm
simulated function PreTravelCleanUp()
{
    local Inventory Inv;
    local KFWeapon invWeapon;
    local int count;

    count=0;

    // consider doing a check on count to make sure it doesn't get too high
    // and force Unreal to crash with a run away loop
    for ( Inv=Inventory; Inv!=None; Inv=Inv.Inventory )
    {
        invWeapon = KFWeapon(Inv);
        if ( invWeapon != None )
        {
            invWeapon.PreTravelCleanUp();
        }

        count++;

        if( count > 500 )
            break;
    }
}

//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.5;
    }

    if( FastTrace(GetBoneCoords(RootBone).Origin,TestLocation))
    {
        PercentExposed += 0.5;
    }

    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( bDetailedShadows )
            PlayerShadow = Spawn(class'KFShadowProject',Self,'',Location);
        else PlayerShadow = Spawn(class'ShadowProjector',Self,'',Location);
        PlayerShadow.ShadowActor = self;
        PlayerShadow.bBlobShadow = bBlobShadow;
        PlayerShadow.LightDirection = Normal(vect(1,1,3));
        PlayerShadow.InitShadow();
    }
    else if (PlayerShadow != none && Level.NetMode != NM_DedicatedServer)
    {
        PlayerShadow.Destroy();
        PlayerShadow = none;
    }
}

simulated function DoDerezEffect();

function SetScriptedAnimData(
name BaseAnim,
float BlendInTime,
float BlendOutTime,
float AnimRate,
byte AnimIterations,
bool bLoopAnim,
float StartFrame)
{
    ScriptedAnimData.BaseAnim       = BaseAnim;
    ScriptedAnimData.BlendInTime    = BlendInTime;
    ScriptedAnimData.BlendOutTime   = BlendOutTime;
    ScriptedAnimdata.AnimRate       = AnimRate;
    ScriptedAnimData.AnimIterations = AnimIterations;
    ScriptedAnimData.bLoopAnim      = bLoopAnim;
    ScriptedAnimData.StartFrame     = StartFrame;

    /* Play the anim on the server too*/
    if(Role == Role_Authority)
    {
        PlayScriptedAnim(ScriptedAnimData);
    }

}

simulated function PlayScriptedAnim( SScriptedAnimRepInfo  AnimData)
{
    if(AnimData.bLoopAnim)
    {
        LoopAnim(AnimData.BaseAnim, AnimData.AnimRate, AnimData.BlendInTime);
    }
    else
    {
        PlayAnim(AnimData.baseAnim,AnimData.AnimRate,AnimData.BlendInTime);
    }

    if( AnimData.StartFrame > 0.0 )
       SetAnimFrame(AnimData.StartFrame, 0, 1);
}

simulated event PostNetReceive()
{
    if( ScriptedAnimData.BaseAnim != '')
    {
        PlayScriptedAnim(ScriptedAnimData);
        ScriptedAnimData.BaseAnim = '';
    }

    if ( PlayerReplicationInfo != None )
    {
        Setup(class'xUtil'.static.FindPlayerRecord(PlayerReplicationInfo.CharacterName));
        bNetNotify = false;
    }
    else if ( (DrivenVehicle != None) && (DrivenVehicle.PlayerReplicationInfo != None) )
    {
        Setup(class'xUtil'.static.FindPlayerRecord(DrivenVehicle.PlayerReplicationInfo.CharacterName));
        bNetNotify = false;
    }
}

// Don't let this pawn move for a certain amount of time
function DisableMovement(float DisableDuration)
{
    StopDisabledTime = Level.TimeSeconds + DisableDuration;
    bMovementDisabled = true;
}

// Modify the velocity of the pawn
simulated event ModifyVelocity(float DeltaTime, vector OldVelocity)
{
    if( Role == ROLE_Authority )
    {
        if( bMovementDisabled )
        {
            if( Level.TimeSeconds > StopDisabledTime )
            {
                bMovementDisabled = false;
            }
        }
    }

    if( bMovementDisabled && Physics == PHYS_Walking )
    {
        Velocity.X = 0;
        Velocity.Y = 0;
        Velocity.Z = 0;
    }

    if ( KFGameReplicationInfo(Level.GRI).BaseDifficulty >= 5 && bMovementDisabled && Velocity.Z > 0 )
    {
        Velocity.Z = 0;
    }
}

function TakeFallingDamage()
{
    local float Shake, EffectiveSpeed;
    local float UsedMaxFallSpeed;

    UsedMaxFallSpeed = MaxFallSpeed;

    // Higher max fall speed in low grav so that weapons that push you around
    // don't cause you to die
    if( Instigator != none && Instigator.PhysicsVolume.Gravity.Z > class'PhysicsVolume'.default.Gravity.Z )
    {
        UsedMaxFallSpeed *= 2.0;
    }

    if (Velocity.Z < -0.5 * UsedMaxFallSpeed)
    {
        if ( Role == ROLE_Authority )
        {
            MakeNoise(1.0);
            if (Velocity.Z < -1 * UsedMaxFallSpeed)
            {
                EffectiveSpeed = Velocity.Z;
                if ( TouchingWaterVolume() )
                    EffectiveSpeed = FMin(0, EffectiveSpeed + 100);
                if ( EffectiveSpeed < -1 * UsedMaxFallSpeed )
                    TakeDamage(-100 * (EffectiveSpeed + UsedMaxFallSpeed)/UsedMaxFallSpeed, None, Location, vect(0,0,0), class'Fell');
            }
        }
        if ( Controller != None )
        {
            Shake = FMin(1, -1 * Velocity.Z/MaxFallSpeed);
            Controller.DamageShake(Shake);
        }
    }
    else if (Velocity.Z < -1.4 * JumpZ)
        MakeNoise(0.5);
}

exec function TestEye()
{
    local Vector X,Y,Z;

    if( !class'ROEngine.ROLevelInfo'.static.RODebugMode() )
        return;


    ClearStayingDebugLines();
    DrawDebugSphere( EyePosition() + Location, 12, 12, 0, 255, 0);

    GetAxes( Controller.rotation, X, Y, Z );

    DrawStayingDebugLine((EyePosition() + Location), (EyePosition() + Location)+500* X, 0,0,255);
    DrawStayingDebugLine((EyePosition() + Location), (EyePosition() + Location)+200* Y, 0,255,0);
    Spawn(class 'ROEngine.RODebugTracer',self,,(EyePosition() + Location),Rotation);
}

/* 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);
        }
    }
}



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 ( FRand() > 0.3f || Damage > 30 || Health <= 0 )
    {
        HitFX[HitFxTicker].damtype = DamageType;

        if( Health <= 0 )
        {
            switch( boneName )
            {
                case 'neck':
                    boneName = HeadBone;
                    break;

                case 'lfoot':
                case 'lleg':
                    boneName = 'lthigh';
                    break;

                case 'rfoot':
                case 'rleg':
                    boneName = 'rthigh';
                    break;

                case 'righthand':
                case 'rshoulder':
                case 'rarm':
                    boneName = 'rfarm';
                    break;

                case 'lefthand':
                case 'lshoulder':
                case 'larm':
                    boneName = 'lfarm';
                    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 = 'lthigh';
                        break;
                    case 1:
                        boneName = 'rthigh';
                        break;
                    case 2:
                        boneName = 'lfarm';
                        break;
                    case 3:
                        boneName = 'rfarm';
                        break;
                    case 4:
                        boneName = HeadBone;
                        break;
                    default:
                        boneName = 'lthigh';
                }
            }
        }

        if( Health < 0 && Damage > DamageType.default.HumanObliterationThreshhold && Damage != 1000 )
        {
            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 )
        {
            if ((Damage > 400 && FRand() < 0.3) || FRand() < 0.1 )
            {
                DoDamageFX(HeadBone,1000,DamageType,r);
                DoDamageFX('lthigh',1000,DamageType,r);
                DoDamageFX('rthigh',1000,DamageType,r);
                DoDamageFX('lfarm',1000,DamageType,r);
                DoDamageFX('rfarm',1000,DamageType,r);
            }
            if ( FRand() < 0.25 )
            {
                DoDamageFX('lthigh',1000,DamageType,r);
                DoDamageFX('rthigh',1000,DamageType,r);
                if ( FRand() < 0.5 )
                {
                    DoDamageFX('lfarm',1000,DamageType,r);
                }
                else
                {
                    DoDamageFX('rfarm',1000,DamageType,r);
                }
            }
            else if ( FRand() < 0.35 )
                DoDamageFX('lthigh',1000,DamageType,r);
            else if ( FRand() < 0.5 )
                DoDamageFX('rthigh',1000,DamageType,r);
            else if ( FRand() < 0.75 )
            {
                if ( FRand() < 0.5 )
                {
                    DoDamageFX('lfarm',1000,DamageType,r);
                }
                else
                {
                    DoDamageFX('rfarm',1000,DamageType,r);
                }
            }
        }
    }
}


//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;

    if( Adjuster!=None )
        Adjuster.Destroy();
    bHasFootAdjust = False;
    AmbientSound = None;
    bCanTeleport = false; // sjs - fix karma going crazy when corpses land on teleporters
    bReplicateMovement = false;
    bTearOff = true;
    bPlayedDeath = true;
    //bFrozenBody = true;

    SafeMesh = Mesh;

    if (CurrentCombo != None)
        CurrentCombo.Destroy();

    HitDamageType = DamageType; // these are replicated to other clients
    TakeHitLocation = HitLoc;

    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'Inf_Weapons.F1.f1_explode01', SLOT_None, 1.5*TransientSoundVolume ); // KFTODO: Replace this sound
                }
            }
        }
        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;
    //LifeSpan = RagdollLifeSpan;

    GotoState('Dying');
    if ( BE != None )
        return;

    PlayDyingAnimation(DamageType, HitLoc);
}

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 ( 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.PhysicsDetailLevel!=PDL_High && !PlayersRagdoll && (Level.TimeSeconds-LastRenderTime)>3 )
        {
            GoTo'NonRagdoll';
            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);

            if( DamageType != None )
            {
                if ( DamageType.default.bLeaveBodyEffect )
                    TearOffMomentum = vect(0,0,0);

                if( DamageType.default.bKUseOwnDeathVel )
                {
                    RagDeathVel = DamageType.default.KDeathVel;
                    RagDeathUpKick = DamageType.default.KDeathUpKick;
                }
            }

            // 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;

            // We scale the hit location out sideways a bit, to get more spin around Z.
            hitLocRel.X *= RagSpinScale;
            hitLocRel.Y *= RagSpinScale;

            // 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;
            }

            // 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
    }

NonRagdoll:
    // non-ragdoll death fallback
    Velocity += GetTearOffMomemtum();
    BaseEyeHeight = Default.BaseEyeHeight;
    SetTwistLook(0, 0);
    SetInvisibility(0.0);
    PlayDirectionalDeath(HitLoc);
    SetPhysics(PHYS_Falling);
}

// jag
// Called when in Ragdoll when we hit something over a certain threshold velocity
// Used to play impact sounds.
event KImpact(actor other, vector pos, vector impactVel, vector impactNorm)
{
    local float VelocitySquared;
    local float RagHitVolume;

    if(Level.TimeSeconds > RagLastSoundTime + RagImpactSoundInterval)
    {
        VelocitySquared = VSizeSquared(impactVel);

        //log("Ragimpact velocity: "$VSize(impactVel)$" VelocitySquared: "$VelocitySquared);

        RagHitVolume = FMin(2.0,(VelocitySquared/40000));

        //log("RagHitVolume = "$RagHitVolume);

        PlaySound(RagImpactSounds[0], SLOT_None, RagHitVolume);
        RagLastSoundTime = Level.TimeSeconds;
    }
}
//jag

simulated function bool ForceDefaultCharacter()
{
    return false;
}

simulated function ProcessHitFX()
{
    local Coords boneCoords;
    local class<xEmitter> HitEffects[4];
    local int i,j;
    local float GibPerterbation;

    if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh) )
    {
        SimHitFxTicker = HitFxTicker;
        return;
    }

    for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
    {
        j++;
        if ( j > 30 )
        {
            SimHitFxTicker = HitFxTicker;
            return;
        }

        if( (HitFX[SimHitFxTicker].damtype == None) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
            continue;

        //log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);

        if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
        {
            SpawnGibs( HitFX[SimHitFxTicker].rotDir, 0);
            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 )
            {
            //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;

        if( HitFX[SimHitFxTicker].bSever )
        {
            GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
            bFlaming = HitFX[SimHitFxTicker].DamType.Default.bFlaming;

            switch( HitFX[SimHitFxTicker].bone )
            {
                case 'obliterate':
                    break;

                case 'lthigh':
                    if( !bLeftLegGibbed )
                    {
                        SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
                        bLeftLegGibbed=true;
                    }
                    break;

                case 'rthigh':
                    if( !bRightLegGibbed )
                    {
                        SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
                        bRightLegGibbed=true;
                    }
                    break;

                case 'lfarm':
                    if( !bLeftArmGibbed )
                    {
                        SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
                        bLeftArmGibbed=true;
                    }
                    break;

                case 'rfarm':
                    if( !bRightArmGibbed )
                    {
                        SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
                        bRightArmGibbed=true;
                    }
                    break;

                case 'head':
                    SpawnGiblet( GetGibClass(EGT_Head), boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation );
                    break;
            }

            if (LastHitDamType == class 'SirenScreamDamage')
                HideBone(HeadBone);
            else if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone )
                HideBone(HitFX[SimHitFxTicker].bone);
        }
    }
}

simulated function SpawnSeveredGiblet( class<SeveredAppendage> GibClass, Vector Location, Rotator Rotation, float GibPerterbation )
{
    local SeveredAppendage 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.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());
    //Giblet.LifeSpan = self.RagdollLifeSpan;
}

function class<Gib> GetGibClass(xPawnGibGroup.EGibType gibType)
{
    return GibGroupClass.static.GetGibClass(gibType);
}

simulated function HideBone(name boneName)
{
    local int BoneScaleSlot;
    local coords boneCoords;

    if( boneName == 'lthigh' )
    {
        boneScaleSlot = 0;
        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 == 'rthigh' )
    {
        boneScaleSlot = 1;
        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 == 'rfarm' )
    {
        boneScaleSlot = 2;
        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 == 'lfarm' )
    {
        boneScaleSlot = 3;
        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 )
    {
        boneScaleSlot = 4;
        if( SeveredHead == none )
        {
            SeveredHead = Spawn(SeveredHeadAttachClass,self);
            SeveredHead.SetDrawScale(SeveredHeadAttachScale);
            boneCoords = GetBoneCoords( 'neck' );
            AttachEmitterEffect( NeckSpurtEmitterClass, 'neck', boneCoords.Origin, rot(0,0,0) );
            AttachToBone(SeveredHead, 'neck');
        }
    }
    else if ( boneName == 'spine' )
        boneScaleSlot = 5;

    SetBoneScale(BoneScaleSlot, 0.0, BoneName);
}

function PlayDyingSound()
{
    if( Level.NetMode!=NM_Client )
    {
        if ( bGibbed )
        {
            PlaySound(GibGroupClass.static.GibSound(), SLOT_Pain,2.0,true,525);
            return;
        }

        if ( HeadVolume.bWaterVolume )
        {
            PlaySound(GetSound(EST_Drown), SLOT_Pain,2.5*TransientSoundVolume,true,500);
            return;
        }

        PlaySound(SoundGroupClass.static.GetDeathSound(), SLOT_Pain,2.5*TransientSoundVolume, true,500);
    }
}

// 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 ( ItBUURRNNNS != none )
    {
        ItBUURRNNNS.Emitters[0].SkeletalMeshActor = none;
        ItBUURRNNNS.Destroy();
    }

    if( ObliteratedEffectClass != none )
        Spawn( ObliteratedEffectClass,,, Location, HitRotation );

    super.SpawnGibs(HitRotation,ChunkPerterbation);

    if ( FRand() < 0.1 )
    {
        SpawnSeveredGiblet( DetachedLegClass, Location, HitRotation, ChunkPerterbation );
        SpawnSeveredGiblet( DetachedLegClass, Location, HitRotation, ChunkPerterbation );
        SpawnSeveredGiblet( DetachedArmClass, Location, HitRotation, ChunkPerterbation );
        SpawnSeveredGiblet( DetachedArmClass, Location, HitRotation, ChunkPerterbation );
    }
    else if ( FRand() < 0.25 )
    {
        SpawnSeveredGiblet( DetachedLegClass, Location, HitRotation, ChunkPerterbation );
        SpawnSeveredGiblet( DetachedLegClass, Location, HitRotation, ChunkPerterbation );
        if ( FRand() < 0.5 )
        {
            SpawnSeveredGiblet( DetachedArmClass, Location, HitRotation, ChunkPerterbation );
        }
    }
    else if ( FRand() < 0.35 )
        SpawnSeveredGiblet( DetachedLegClass, Location, HitRotation, ChunkPerterbation );
    else if ( FRand() < 0.5 )
    {
        SpawnSeveredGiblet( DetachedArmClass, Location, HitRotation, ChunkPerterbation );
    }
}

simulated function StartDeRes()
{
    if( Level.NetMode == NM_DedicatedServer )
        return;

    AmbientGlow=0;
    MaxLights=5;

    // DeResFX = Spawn(class'DeResPart', self, , Location);
    //Skins[0] = DeResMat0;
    //Skins[1] = DeResMat1;

    if( Physics == PHYS_KarmaRagdoll )
    {
        // Remove flames
        RemoveFlamingEffects();
        // Turn off any overlays
        SetOverlayMaterial(None, 0.0f, true);
    }
}

// Remove Shield Sounds , Randomize for Human Pain sound.

function int ShieldAbsorb(int dam)
{
    local float Interval, damage, Remaining;
    //local int PainSound;

    damage = dam;

    if ( ShieldStrength == 0 )
        return damage;

    if ( KFPlayerReplicationInfo(PlayerReplicationInfo) != none && KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none )
    {
        damage *= KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.GetBodyArmorDamageModifier(KFPlayerReplicationInfo(PlayerReplicationInfo));
    }

      // Super.ShieldAbsorb(dam);
    //SetOverlayMaterial( ShieldHitMat, ShieldHitMatTime, false );

    // Randomize Painsounds on Armor hit
//  PainSound = rand(6);
//  if (PainSound == 0)
//      PlaySound(sound'KFPlayerSound.hpain3', SLOT_Pain,2*TransientSoundVolume,,400);
//  else if (PainSound == 1)
//      PlaySound(sound'KFPlayerSound.hpain2', SLOT_Pain,2*TransientSoundVolume,,400);
//  else if (PainSound == 2)
//      PlaySound(sound'KFPlayerSound.hpain1', SLOT_Pain,2*TransientSoundVolume,,400);
//  else if (PainSound == 3)
//      PlaySound(sound'KFPlayerSound.hpain3', SLOT_Pain,2*TransientSoundVolume,,400);
//  else if (PainSound == 4)
//      PlaySound(sound'KFPlayerSound.hpain2', SLOT_Pain,2*TransientSoundVolume,,400);
//  else if (PainSound == 5)
//      PlaySound(sound'KFPlayerSound.hpain1', SLOT_Pain,2*TransientSoundVolume,,400);

    if ( ShieldStrength > SmallShieldStrength )
    {
        Interval = ShieldStrength - SmallShieldStrength;
        if ( Interval >= 0.75 * damage )
        {
            ShieldStrength -= 0.75 * damage;
            if ( ShieldStrength < SmallShieldStrength )
                SmallShieldStrength = ShieldStrength;
            return (0.25 * Damage);
        }
        else
        {
            ShieldStrength = SmallShieldStrength;
            damage -= Interval;
            Remaining = 0.33 * Interval;
            if ( Remaining <= damage )
                return damage;
            damage -= Remaining;
        }
    }

    if ( ShieldStrength >= 0.5 * damage )
    {
        ShieldStrength -= damage ;
        SmallShieldStrength = ShieldStrength;
        return Remaining + (0.25 * damage);   // 0.5
    }
    else
    {
        damage -= ShieldStrength;
        ShieldStrength = 0;
        SmallShieldStrength = 0;
    }

    return damage + Remaining;
}

// Play the pawn firing animations
simulated function StartFiringX(bool bAltFire, bool bRapid)
{
    local name FireAnim;
    local int AnimIndex;

    if ( HasUDamage() && (Level.TimeSeconds - LastUDamageSoundTime > 0.25) )
    {
        LastUDamageSoundTime = Level.TimeSeconds;
        PlaySound(UDamageSound, SLOT_None, 1.5*TransientSoundVolume,,700);
    }

    if (Physics == PHYS_Swimming)
        return;

    AnimIndex = Rand(4);

    if (bAltFire)
    {
        if( bIsCrouched )
        {
            FireAnim = FireCrouchAltAnims[AnimIndex];
        }
        else
        {
            FireAnim = FireAltAnims[AnimIndex];
        }
    }
    else
    {
        if( bIsCrouched )
        {
            FireAnim = FireCrouchAnims[AnimIndex];
        }
        else
        {
            FireAnim = FireAnims[AnimIndex];
        }
    }

    AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);

    if (bRapid)
    {
        if (FireState != FS_Looping)
        {
            LoopAnim(FireAnim,, 0.0, 1);
            FireState = FS_Looping;
        }
    }
    else
    {
        PlayAnim(FireAnim,, 0.0, 1);
        FireState = FS_PlayOnce;
    }

    IdleTime = Level.TimeSeconds;
}

simulated function StopFiring()
{
    if (FireState == FS_Looping)
        FireState = FS_PlayOnce;
    IdleTime = Level.TimeSeconds;
}

simulated function AnimEnd(int Channel)
{
    if (Channel == 1)
    {
        if (FireState == FS_Ready)
        {
            AnimBlendToAlpha(1, 0.0, 0.12);
            FireState = FS_None;
        }
        else if (FireState == FS_PlayOnce)
        {
            if( bIsCrouched )
            {
                PlayAnim(PostFireBlendCrouchAnim,, 0.1, 1);
            }
            else
            {
                PlayAnim(PostFireBlendStandAnim,, 0.1, 1);
            }

            FireState = FS_Ready;
            IdleTime = Level.TimeSeconds;
        }
        else
            AnimBlendToAlpha(1, 0.0, 0.12);
    }
    else if ( bKeepTaunting && (Channel == 0) )
        PlayVictoryAnimation();
}


function RemoveInventorySP( class<inventory> ItemClass )
{
    // If this item is in our inventory chain, unlink it.
    local actor Link;
    local int Count;
    local inventory Item ;

    Item = spawn(ItemClass) ;

    if ( ItemClass == Weapon.class )
        Weapon = None;
    if ( ItemClass == SelectedItem.class )
        SelectedItem = None;
    for( Link = Self; Link!=None; Link=Link.Inventory )
    {
        if( Link.Inventory.class == ItemClass )
        {
            Link.Inventory = Item.Inventory;
            Item.Inventory = None;
            Link.NetUpdateTime = Level.TimeSeconds - 1;
            Item.NetUpdateTime = Level.TimeSeconds - 1;
            break;
        }
        if ( Level.NetMode == NM_Client )
        {
        Count++;
        if ( Count > 1000 )
            break;
    }
    }
    Item.SetOwner(None);
}

// another one cut-n-pasted to remove must-have-ammo fetish
exec function SwitchToLastWeapon()
{
    if ( (Weapon != None) && (Weapon.OldWeapon != None) ) // && Weapon.OldWeapon.HasAmmo() )
    {
        PendingWeapon = Weapon.OldWeapon;
        Weapon.PutDown();
    }
}

// TODO: GetWeapon

simulated function AddHealth()
{
    local int tempHeal ;
    if((level.TimeSeconds - lastHealTime) >= 0.1)
    {
        if(Health < HealthMax)
        {
            tempHeal = int(10 * (level.TimeSeconds - lastHealTime)) ;
            if(tempHeal>0)
              lastHealTime = level.TimeSeconds ;

            Health = Min(Health+tempHeal, HealthMax);
            HealthToGive -= tempHeal ;
        }
        else
        {
            lastHealTime = level.timeSeconds ;
            // if we are all healed, there's gonna be no more healing
            HealthToGive = 0 ;
        }
    }
}

function bool GiveHealth(int HealAmount, int HealMax)
{
    // Taking this out for now because it nerfs the high level medic perks - Ramm
//  if(healAmount >= 50)
//      healAmount = 50;

    // If someone gets healed while burning, reduce the burn length/damage
    if( BurnDown > 0 )
    {
        if( BurnDown > 1 )
        {
            BurnDown *= 0.5;
        }

        LastBurnDamage *= 0.5;
    }

    // Don't let them heal more than the max health
    if( (healAmount + HealthToGive + Health) > HealthMax)
    {
        healAmount = HealthMax - (Health + HealthToGive);

        if( healAmount == 0 )
        {
            return false;
        }
    }

    if( Health<HealMax )
    {
        HealthToGive+=HealAmount;
        lastHealTime = level.timeSeconds;
        return true;
    }
    Return False;
}

function ThrowGrenade()
{
    local inventory inv;
    local Frag aFrag;

    for ( inv = inventory; inv != none; inv = inv.Inventory )
    {
        aFrag = Frag(inv);

        if ( aFrag != none && aFrag.HasAmmo() && !bThrowingNade )
        {
            if ( KFWeapon(Weapon) == none || Weapon.GetFireMode(0).NextFireTime - Level.TimeSeconds > 0.1 ||
                 (KFWeapon(Weapon).bIsReloading && !KFWeapon(Weapon).InterruptReload()) )
            {
                return;
            }

            //TODO: cache this without setting SecItem yet
            //SecondaryItem = aFrag;
            KFWeapon(Weapon).ClientGrenadeState = GN_TempDown;
            Weapon.PutDown();
            break;
            //aFrag.StartThrow();
        }
    }
}

function WeaponDown()
{
    local inventory inv;
    local Frag aFrag;

    for(inv=inventory; inv!=none; inv=inv.Inventory)
    {
        aFrag=Frag(inv);

        if(aFrag!=none && aFrag.HasAmmo() )
        {
            SecondaryItem = aFrag;
            aFrag.StartThrow();
        }
    }
}

simulated function HandleNadeThrowAnim()
{
    if( Weapon != none )
    {
        if( AK47AssaultRifle(Weapon) != none )
        {
            SetAnimAction('Frag_AK47');
        }
        else if( Bullpup(Weapon) != none || SeekerSixRocketLauncher(Weapon) != none)
        {
            SetAnimAction('Frag_Bullpup');
        }
        else if( Crossbow(Weapon) != none )
        {
            SetAnimAction('Frag_Crossbow');
        }
        else if( Deagle(Weapon) != none || Magnum44Pistol(Weapon) != none || MK23Pistol(Weapon) != none)
        {
            SetAnimAction('Frag_Single9mm');
        }
        else if( KFWeapon(Weapon) != none && KFWeapon(Weapon).bDualWeapon )
        {
            SetAnimAction('Frag_Dual9mm');
        }
        else if( FlameThrower(Weapon) != none )
        {
            SetAnimAction('Frag_Flamethrower');
        }
        else if( Axe(Weapon) != none || DwarfAxe(Weapon) != none )
        {
            SetAnimAction('Frag_Axe');
        }
        else if( Chainsaw(Weapon) != none )
        {
            SetAnimAction('Frag_Chainsaw');
        }
        else if( Katana(Weapon) != none || ClaymoreSword(Weapon) != none )
        {
            SetAnimAction('Frag_Katana');
        }
        else if( Knife(Weapon) != none )
        {
            SetAnimAction('Frag_Knife');
        }
        else if( Machete(Weapon) != none )
        {
            SetAnimAction('Frag_Knife');
        }
        else if( Syringe(Weapon) != none )
        {
            SetAnimAction('Frag_Syringe');
        }
        else if( Welder(Weapon) != none )
        {
            SetAnimAction('Frag_Syringe');
        }
        else if( BoomStick(Weapon) != none )
        {
            SetAnimAction('Frag_HuntingShotgun');
        }
        else if( LAW(Weapon) != none )
        {
            SetAnimAction('Frag_LAW');
        }
        else if( Shotgun(Weapon) != none || BenelliShotgun(Weapon) != none || Trenchgun(Weapon) != none)
        {
            SetAnimAction('Frag_Shotgun');
        }
        else if( Winchester(Weapon) != none )
        {
            SetAnimAction('Frag_Winchester');
        }
        else if( Single(Weapon) != none )
        {
            SetAnimAction('Frag_Single9mm');
        }
        else if( M14EBRBattleRifle(Weapon) != none || SPSniperRifle(Weapon) != none )
        {
            SetAnimAction('Frag_M14');
        }
        else if( SCARMK17AssaultRifle(Weapon) != none )
        {
            SetAnimAction('Frag_SCAR');
        }
        else if( AA12AutoShotgun(Weapon) != none || FNFAL_ACOG_AssaultRifle(Weapon) != none
            || SPAutoShotgun(Weapon) != none )
        {
            SetAnimAction('Frag_AA12');
        }
        else if( MP5MMedicGun(Weapon) != none || KSGShotgun(Weapon) != none )
        {
            SetAnimAction('Frag_MP5');
        }
        else if( MP7MMedicGun(Weapon) != none )
        {
            SetAnimAction('Frag_MP7');
        }
        else if( M7A3MMedicGun(Weapon) != none || KrissMMedicGun(Weapon) != none )
        {
            SetAnimAction('Frag_Kriss');
        }
        else if( PipeBombExplosive(Weapon) != none )
        {
            SetAnimAction('Frag_PipeBomb');
        }
        else if( M79GrenadeLauncher(Weapon) != none || ZEDMKIIWeapon(Weapon) != none )
        {
            SetAnimAction('Frag_M79');
        }
        else if( M32GrenadeLauncher(Weapon) != none )
        {
            SetAnimAction('Frag_M32_MGL');
        }
        else if( SealSquealHarpoonBomber(Weapon) != none )
        {
            SetAnimAction('Frag_IJC_SealSqueal');
        }
        else if( M4203AssaultRifle(Weapon) != none || M99SniperRifle(Weapon) != none )
        {
            SetAnimAction('Frag_M4203');
        }
        else if( M4AssaultRifle(Weapon) != none || MKb42AssaultRifle(Weapon) != none)
        {
            SetAnimAction('Frag_M4');
        }
        else if( HuskGun(Weapon) != none )
        {
            SetAnimAction('Frag_HuskGun');
        }
        else if( ZEDGun(Weapon) != none )
        {
            SetAnimAction('Frag_Zed');
        }
        else if( NailGun(Weapon) != none )
        {
            SetAnimAction('Frag_Bullpup');
        }
        else if( SPThompsonSMG(Weapon) != none || ThompsonDrumSMG(Weapon) != none)
        {
            SetAnimAction('Frag_IJC_spThompson_Drum');
        }
        else if( ThompsonSMG(Weapon) != none )
        {
            SetAnimAction('Frag_Thompson');
        }
        else if( Scythe(Weapon) != none )
        {
            SetAnimAction('Frag_Scythe');
        }
        else if( Crossbuzzsaw(Weapon) != none )
        {
            SetAnimAction('Frag_Cheetah');
        }
        else if( FlareRevolver(Weapon) != none )
        {
            SetAnimAction('Frag_Single9mm');
        }
    }
    else
    {
        SetAnimAction('Frag_Knife');
    }
}


simulated function ThrowGrenadeFinished()
{
  SecondaryItem = none;
  KFWeapon(Weapon).ClientGrenadeState = GN_BringUp;
  Weapon.BringUp();
  bThrowingNade = false;
}

simulated function SetNadeTimeOut(float NewTimeOut)
{
    NadeThrowTimeout = NewTimeOut;
}

/* Feet height adjusting functions ======================================================*/
simulated function SetFootHeight( float HeightMulti, byte iNum )
{
//  local rotator R;
//
//  OldAdjust[iNum] = (HeightMulti-OldAdjust[iNum])*0.5+OldAdjust[iNum];
//  if( Abs(OldAdjust[iNum]-HeightMulti)<0.1 )
//      OldAdjust[iNum] = HeightMulti;
//  HeightMulti = OldAdjust[iNum];
//  HeightMulti = 1.f-HeightMulti; // Invert this value.
//  switch( FeetAdjSpec.Default.TorseRotType )
//  {
//  case BRot_Yaw:
//      R.Yaw = FeetAdjSpec.Default.MaxTorsoTurn*HeightMulti;
//      break;
//  case BRot_Roll:
//      R.Roll = FeetAdjSpec.Default.MaxTorsoTurn*HeightMulti;
//      break;
//  default:
//      R.Pitch = FeetAdjSpec.Default.MaxTorsoTurn*HeightMulti;
//  }
//  SetBoneRotation(FeetAdjSpec.Default.TorsoBones[iNum],R);
//  R = rot(0,0,0);
//  switch( FeetAdjSpec.Default.KneeRotType )
//  {
//  case BRot_Yaw:
//      R.Yaw = FeetAdjSpec.Default.MaxKneeTurn*HeightMulti;
//      break;
//  case BRot_Roll:
//      R.Roll = FeetAdjSpec.Default.MaxKneeTurn*HeightMulti;
//      break;
//  default:
//      R.Pitch = FeetAdjSpec.Default.MaxKneeTurn*HeightMulti;
//  }
//  SetBoneRotation(FeetAdjSpec.Default.KneeBones[iNum],R);
}
simulated function UpdateFeetCoords()
{
//  local Coords C;
//  local byte i;
//  local vector X,Y,Z,HL,Pos;
//
//  GetAxes(Rotation,X,Y,Z);
//  Pos = Location+PrePivot;
//  for( i=0; i<2; i++ )
//  {
//      C = GetBoneCoords(FeetAdjSpec.Default.FeetBones[i]);
//      C.Origin = (C.Origin-Pos)<<Rotation;
//      C.Origin = X*C.Origin.X+Y*C.Origin.Y+Pos-Z*MinZHeight*DrawScale;
//      C.XAxis = C.Origin-Z*MaxZHeight*DrawScale;
//      if( Trace(HL,C.YAxis,C.XAxis,C.Origin,true)==None )
//          SetFootHeight(1,i);
//      else SetFootHeight(FClamp(VSize(HL-C.Origin)/DrawScale/MaxZHeight,0.f,1.f),i);
//  }
}
/* End of feet height adjusting functions ======================================================*/

simulated function Tick(float DeltaTime)
{
//  local float DMult;

    if( Role < ROLE_Authority && bThrowingNade )
    {
        if( NadeThrowTimeout > 0 )
    {
        NadeThrowTimeout -= DeltaTime;
        }
// This is a hack to clear this flag on the client after a bit of time. This fixes a bug where you could get stuck unable to use weapons
        if( NadeThrowTimeout <= 0 )
        {
            NadeThrowTimeout = 0;
            ThrowGrenadeFinished();
        }
    }

    // IN other words - we're moving, we've got a piece, but we're not firing or reloading, or jumping / falling Faust:Perhaps we need that later: VSize(Acceleration) != 0 &&
    if (level.NetMode != NM_DEDICATEDSERVER)
    {
        if(bBurnified && !bBurnApplied)
        {
            if ( !bGibbed )
            {
                StartBurnFX();
            }
        }
        else if(!bBurnified && bBurnApplied)
            StopBurnFX();
    }

       // CheckFlashLightAnims();

    if( bResetingAnimAct && (AnimActResetTime<Level.TimeSeconds) ) // Reset replication.
    {
        bResetingAnimAct = False;
        AnimAction = '';
    }
    if(healthToGive > 0 && health > 0)  //
        AddHealth() ;
    else if(healthToGive < 0)
        healthToGive = 0 ;

    if(BileCount>0 && NextBileTime<level.TimeSeconds)
    {
        --BileCount;
        NextBileTime+=BileFrequency;
        TakeBileDamage();
    }
//  if( bHasFootAdjust && (Level.TimeSeconds-LastRenderTime)<1 )
//  {
//      UpdateFeetCoords();
//      if( Level.NetMode!=NM_ListenServer )
//      {
//          if( Physics!=PHYS_Walking )
//              DMult = 1;
//          else DMult = OldAdjust[0]+OldAdjust[1];
//          if( DMult<1.65 && PrePivot.Z<0 )
//          {
//              PrePivot.Z+=DeltaTime*60f;
//              SetDrawScale(DrawScale);
//          }
//          else if( DMult>1.85 && PrePivot.Z>-35 )
//          {
//              PrePivot.Z-=DeltaTime*60f;
//              SetDrawScale(DrawScale);
//          }
//      }
//  }

//  KFTODO: Maybe take this out because we're using notifies for footsteps in third person now
//  if( Level.Netmode != NM_DedicatedServer )
//  {
//      // do footsteps for nonlocal pawns and bots
//      if( !IsLocallyControlled() || (Level.Netmode == NM_Standalone && !IsHumanControlled()))
//      {
//          CheckFootSteps(DeltaTime);
//      }
//  }

    if( Physics == PHYS_KarmaRagdoll && bDestroyAfterRagDollTick &&
        !bProcessedRagTickDestroy && GetRagDollFrames() > 0 )
    {
        bProcessedRagTickDestroy = true;
        Destroy();
    }
    // If we've flagged this character to be destroyed next tick, handle that
    else if( bDestroyNextTick && TimeSetDestroyNextTickTime < Level.TimeSeconds )
    {
        Destroy();
    }


    Super.Tick(deltaTime);
}

// Blend the upper body back to full body animation.
// Called by the native code when AnimBlendTime counts down to zero
simulated event AnimBlendTimer()
{
    AnimBlendToAlpha(1, 0.0, 0.12);
    // Force a new idle anim to play, since we likely just switched weapons
    bIsIdle=false;
    IdleTime = Level.TimeSeconds;
}

simulated event SetAnimAction(name NewAction)
{
    local float UsedBlendOutTime;

    if( NewAction=='' )
        Return;
    if (!bWaitForAnim)
    {
        AnimAction = NewAction;

        if ( AnimAction == 'Weapon_Switch' )
        {
            AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
            PlayAnim(NewAction,, 0.0, 1);
            // Set the timer to blend out the upper body animation
            if( HasAnim( AnimAction ) )
            {
                UsedBlendOutTime = GetAnimDuration(NewAction, 1.0);
            }
            else
            {
                UsedBlendOutTime = 0.2;
            }
            AnimBlendTime = UsedBlendOutTime + 0.1;
        }
        else if ( AnimAction == IdleWeaponAnim )
        {
            PlayAnim(AnimAction,,0.1);
            AnimBlendToAlpha(1,0.0,0.05);
        }
        else if ( AnimAction == 'Reload' )
        {
            AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
            PlayAnim(NewAction,, 0.0, 1);
        }
        else if ( AnimAction == 'AxeAttack' )
        {
            AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
            PlayAnim(NewAction,, 0.0, 1);
        }
        else if ( AnimAction == 'ChainSawAttack' )
        {
            AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
            PlayAnim(NewAction,, 0.0, 1);
        }
        else if ( AnimAction == 'DeagleHold' )
        {
            AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
            LoopAnim(NewAction,, 0.0, 1);
        }
        else if ( AnimAction == 'DualiesHold' )
        {
            AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
            LoopAnim(NewAction,, 0.0, 1);
        }
        else if ( AnimAction == 'Frag_AK47' || AnimAction == 'Frag_Bullpup'
            || AnimAction == 'Frag_Crossbow' || AnimAction == 'Frag_Single9mm'
            || AnimAction == 'Frag_Dual9mm' || AnimAction == 'Frag_Flamethrower'
            || AnimAction == 'Frag_Axe' || AnimAction == 'Frag_Chainsaw'
            || AnimAction == 'Frag_Katana' || AnimAction == 'Frag_Knife'
            || AnimAction == 'Frag_Syringe' || AnimAction == 'Frag_HuntingShotgun'
            || AnimAction == 'Frag_LAW' || AnimAction == 'Frag_Shotgun'
            || AnimAction == 'Frag_M14' || AnimAction == 'Frag_SCAR'
            || AnimAction == 'Frag_AA12' || AnimAction == 'Frag_MP7'
            || AnimAction == 'Frag_PipeBomb' || AnimAction == 'Frag_M79'
            || AnimAction == 'Frag_M32_MGL' || AnimAction == 'Frag_M4'
            || AnimAction == 'Frag_M4203' || AnimAction == 'Frag_MP5'
            || AnimAction == 'Frag_HuskGun' || AnimAction == 'Frag_Kriss'
            || AnimAction == 'Frag_Thompson' || AnimAction == 'Frag_scythe'
            || AnimAction == 'Frag_Cheetah' || AnimAction == 'Frag_Zed'
            || AnimAction == 'Frag_IJC_spThompson_Drum' || AnimAction == 'Frag_IJC_SealSqueal')
        {
            AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
            PlayAnim(NewAction,, 0.0, 1);
            bThrowingNade = true;
        }
        else if ( AnimAction == 'ShotgunFire' )
        {
            AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
            PlayAnim(NewAction,, 0.0, 1);
        }
        else if ( AnimAction == 'DeagleBlast' )
        {
            AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
            PlayAnim(NewAction,, 0.0, 1);
        } // Reloads
        else if ( AnimAction == 'Reload1' || AnimAction == 'Reload_BullPup'
            || AnimAction == 'Reload_Single9mm' || AnimAction == 'Reload_Winchester'
            || AnimAction == 'Reload_Crossbow' || AnimAction == 'Reload_Dual9mm'
            || AnimAction == 'Reload_Flamethrower' || AnimAction == 'Reload_HuntingShotgun'
            || AnimAction == 'Reload_LAW' || AnimAction == 'Reload_Shotgun'
            || AnimAction == 'Reload_Winchester' || AnimAction == 'Reload_AK47'
            || AnimAction == 'Reload_M14' || AnimAction == 'Reload_SCAR'
            || AnimAction == 'Reload_MP7' || AnimAction == 'Reload_AA12'
            || AnimAction == 'Reload_Secondary_M4203' || AnimAction == 'Reload_Fal_Acog'
            || AnimAction == 'Reload_Fal_Acog' || AnimAction == 'Reload_M7A3'
            || AnimAction == 'Reload_KSG' || AnimAction == 'Reload_Flare'
            || AnimAction == 'Reload_DualFlare' || AnimAction == 'Reload_Cheetah'
            || AnimAction == 'Reload_Thompson' || AnimAction == 'Reload_Kriss'
            || AnimAction == 'Reload_Zed' || AnimAction == 'Reload_IJC_spJackHammer'
            || AnimAction == 'Reload_spSinper' || AnimAction == 'Reload_IJC_spThompson_Drum'
            || AnimAction == 'Reload_IJC_SealSqueal' || AnimAction == 'Reload_M32_MGL'
            || AnimAction == 'Reload_IJC_SeekerSix' || AnimAction == 'Reload_IJC_ZEDV2' )
        {
            AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
            PlayAnim(NewAction,, 0.1, 1);
            FireState = FS_Ready;
        }
        else if ( ((Physics == PHYS_None)|| ((Level.Game != None) && Level.Game.IsInState('MatchOver'))) && (DrivenVehicle == None) )
        {
            PlayAnim(AnimAction,,0.1);
            AnimBlendToAlpha(1,0.0,0.05);
        }
        else if ( (DrivenVehicle != None) || (Physics == PHYS_Falling) || ((Physics == PHYS_Walking) && (Velocity.Z != 0)) )
        {
            if ( CheckTauntValid(AnimAction) )
            {
                if (FireState == FS_None || FireState == FS_Ready)
                {
                    AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
                    PlayAnim(NewAction,, 0.1, 1);
                    FireState = FS_Ready;
                }
            }
            else if ( HasAnim(AnimAction) && PlayAnim(AnimAction) )
            {
                if ( Physics != PHYS_None )
                    bWaitForAnim = true;
            }
            else AnimAction = NewAction;
        }
        else if (bIsIdle && !bIsCrouched && (Bot(Controller) == None) ) // standing taunt
        {
            AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
            PlayAnim(AnimAction,,0.1,1);
        }
        else if (FireState == FS_None || FireState == FS_Ready)
        {
            AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
            PlayAnim(NewAction,, 0.1, 1);
            FireState = FS_Ready;
        }
    }
    if( Level.NetMode!=NM_Client )
    {
        // Reset to fix replication.
        bResetingAnimAct = True;
        AnimActResetTime = Level.TimeSeconds+0.45;
    }
}

simulated function StartBurnFX()
{
    if( bDeleteMe )
    {
        return;
    }

    if( ItBUURRNNNS==None )
    {
        ItBUURRNNNS = Spawn(BurnEffect);
        ItBUURRNNNS.SetBase(Self);
        ItBUURRNNNS.Emitters[0].SkeletalMeshActor = self;
        ItBUURRNNNS.Emitters[0].UseSkeletalLocationAs = PTSU_SpawnOffset;
    }
    bBurnApplied = True;
}

simulated function StopBurnFX()
{
    RemoveFlamingEffects();
    if( ItBUURRNNNS!=None )
        ItBUURRNNNS.Kill();
    bBurnApplied = False;
}

// Process a precision hit
function ProcessLocationalDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<DamageType> damageType, array<int> PointsHit )
{
    local int actualDamage, originalDamage, cumulativeDamage, totalDamage, i;
    local int HighestDamagePoint, HighestDamageAmount;
    local bool bHeadShot;
    // Hit detection debugging
//    local coords CO;
//  local vector HeadLoc;
//  local bool bFirstHit;

    originalDamage = damage;

    // If someone else has killed this player , return
    if( bDeleteMe || PointsHit.Length < 1 || Health <= 0 )
        return;

    // Don't process locational damage if we're not going to damage a friendly anyway
    if( TeamGame(Level.Game)!=None && TeamGame(Level.Game).FriendlyFireScale==0 && instigatedBy!=None && instigatedBy!=Self
     && instigatedBy.GetTeamNum()==GetTeamNum() )
    {
        Return;
    }

    for(i=0; i<PointsHit.Length; i++)
    {
        // If someone else has killed this player , return
        if( bDeleteMe || Health <= 0 )
            return;

        actualDamage = originalDamage;

        actualDamage *= Hitpoints[PointsHit[i]].DamageMultiplier;
        totalDamage += actualDamage;
        actualDamage = Level.Game.ReduceDamage(actualDamage, self, instigatedBy, HitLocation, Momentum, DamageType);
        cumulativeDamage += actualDamage;

        if( actualDamage > HighestDamageAmount )
        {
            HighestDamageAmount = actualDamage;
            HighestDamagePoint = PointsHit[i];
        }

        // Store if one of the shots was a headshot
        if( Hitpoints[PointsHit[i]].HitPointType == PHP_Head && class<KFWeaponDamageType>(damageType)!=none &&
            class<KFWeaponDamageType>(damageType).default.bCheckForHeadShots)
        {
            bHeadShot = true;
        }

        //log("We hit "$GetEnum(enum'EPawnHitPointType',Hitpoints[PointsHit[i]].HitPointType));

        // Hit detection debugging
        /*if( PointsHit[i] != 0 && !bFirstHit )
        {
            CO = GetBoneCoords(Hitpoints[PointsHit[i]].PointBone);
            HeadLoc = CO.Origin;
            HeadLoc = HeadLoc + (Hitpoints[PointsHit[i]].PointOffset >> GetBoneRotation(Hitpoints[PointsHit[i]].PointBone));

            DrawLocation = HeadLoc;
            DrawRotation = GetBoneRotation(Hitpoints[PointsHit[i]].PointBone);
            DrawIndex = PointsHit[i];
            HitPointDebugByte++;
            bFirstHit = true;
        }*/

        // Lets exit out if one of the shots killed the player
        if ( cumulativeDamage >=  Health )
        {
            // Play a sound when someone gets a headshot - KFTODO: Replace this with a better bullet hitting a helmet sound
            if( bHeadShot )
            {
                PlaySound(sound'ProjectileSounds.impact_metal09', SLOT_None,2.0,true,500);
            }
            TakeDamage(totalDamage, instigatedBy, hitlocation, momentum, damageType, HighestDamagePoint);
        }
    }

    if( totalDamage > 0 )
    {
        // If someone else has killed this player , return
        if( bDeleteMe || Health <= 0 )
            return;

        // Play a sound when someone gets a headshot
        if( bHeadShot )
        {
            // Play a sound when someone gets a headshot - KFTODO: Replace this with a better bullet hitting a helmet sound
            PlaySound(sound'ProjectileSounds.impact_metal09', SLOT_None,2.0,true,500);
        }

        TakeDamage(totalDamage, instigatedBy, hitlocation, momentum, damageType, HighestDamagePoint);
    }
}

// Hit detection debugging - Only use when debugging
/*
simulated function DrawBoneLocation()
{
    local vector X, Y, Z;

    GetAxes(DrawRotation, X,Y,Z);
    ClearStayingDebugLines();

    DrawStayingDebugLine(HitStart, HitEnd, 0,255,0);
    Spawn(class 'ROEngine.RODebugTracer',self,,HitStart,Rotator(Normal(HitEnd-HitStart)));

    DrawDebugCylinder(DrawLocation,Z,Y,X,Hitpoints[DrawIndex].PointRadius * Hitpoints[DrawIndex].PointScale,Hitpoints[DrawIndex].PointHeight * Hitpoints[DrawIndex].PointScale,10,0, 255, 0);
}

simulated function DrawDebugCylinder(vector Base,vector X, vector Y,vector Z, FLOAT Radius,float HalfHeight,int NumSides, byte R, byte G, byte B)
{
    local float AngleDelta;
    local vector LastVertex, Vertex;
    local int SideIndex;

    AngleDelta = 2.0f * PI / NumSides;
    LastVertex = Base + X * Radius;

    for(SideIndex = 0;SideIndex < NumSides;SideIndex++)
    {
        Vertex = Base + (X * Cos(AngleDelta * (SideIndex + 1)) + Y * Sin(AngleDelta * (SideIndex + 1))) * Radius;

        DrawStayingDebugLine( LastVertex - Z * HalfHeight,Vertex - Z * HalfHeight,R,G,B);
        DrawStayingDebugLine( LastVertex + Z * HalfHeight,Vertex + Z * HalfHeight,R,G,B);
        DrawStayingDebugLine( LastVertex - Z * HalfHeight,LastVertex + Z * HalfHeight,R,G,B);

        LastVertex = Vertex;
    }
}*/

function TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<DamageType> damageType, optional int HitIdx )
{
    local KFPlayerReplicationInfo KFPRI;

    LastHitDamType = damageType;
    LastDamagedBy = instigatedBy;

    super.TakeDamage(Damage, instigatedBy, hitLocation, momentum, damageType);

    healthtoGive-=5;

    KFPRI = KFPlayerReplicationInfo(PlayerReplicationInfo);

    // Just return if this wouldn't even damage us. Prevents us from catching on fire for high level perks that dont take fire damage
    if( KFPRI != none &&  KFPRI.ClientVeteranSkill != none )
    {
       if( KFPRI.ClientVeteranSkill.Static.ReduceDamage(KFPRI, self, instigatedBy, Damage, DamageType) <= 0 )
       {
           return;
       }
    }

    if ( class<DamTypeBurned>(damageType) != none || class<DamTypeFlamethrower>(damageType) != none)
    {
        if( TeamGame(Level.Game)!=None && TeamGame(Level.Game).FriendlyFireScale==0 && instigatedBy!=None && instigatedBy!=Self
         && instigatedBy.GetTeamNum()==GetTeamNum()  )
        {
            Return;
        }

        // Do burn damage if the damage was significant enough
        if( Damage > 2 )
        {
            // If we are already burning, and this damage is more than our current burn amount, add more burn time
            if( BurnDown > 0 && Damage > LastBurnDamage )
            {
                BurnDown = 5;
                BurnInstigator = instigatedBy;
            }

            LastBurnDamage = Damage;

            if (BurnDown <= 0 )
            {
                bBurnified = true;
                BurnDown = 5;
                BurnInstigator = instigatedBy;
                SetTimer(1.5,true);
            }
        }
    }

    if(class<DamTypeVomit>(DamageType)!=none)
    {
        BileCount=7;
        BileInstigator = instigatedBy;
        LastBileDamagedByType=class<DamTypeVomit>(DamageType);
        if(NextBileTime< Level.TimeSeconds )
            NextBileTime = Level.TimeSeconds+BileFrequency;

        if ( Level.Game != none && Level.Game.GameDifficulty >= 4.0 && KFPlayerController(Controller) != none &&
             !KFPlayerController(Controller).bVomittedOn)
        {
            KFPlayerController(Controller).bVomittedOn = true;
            KFPlayerController(Controller).VomittedOnTime = Level.TimeSeconds;

            if ( Controller.TimerRate == 0.0 )
            {
                Controller.SetTimer(10.0, false);
            }
        }
    }
    else if ( class<SirenScreamDamage>(DamageType) != none )
    {
        if ( Level.Game != none && Level.Game.GameDifficulty >= 4.0 && KFPlayerController(Controller) != none &&
             !KFPlayerController(Controller).bScreamedAt)
        {
            KFPlayerController(Controller).bScreamedAt = true;
            KFPlayerController(Controller).ScreamTime = Level.TimeSeconds;

            if ( Controller.TimerRate == 0.0 )
            {
                Controller.SetTimer(10.0, false);
            }
        }
    }
}

function TakeBileDamage()
{
    Super.TakeDamage(2+Rand(3), BileInstigator, Location, vect(0,0,0), LastBileDamagedByType);
    healthtoGive-=5;
}

function bool AddShieldStrength(int ShieldAmount)
{
    if(ShieldStrength >= 100)
        return false;

    ShieldStrength+=ShieldAmount;
    if(ShieldStrength > 100)
        ShieldStrength = 100;
    return true ;
}

function TakeFireDamage(int Damage, pawn BInstigator)
{
    if( Damage > 0 )
    {
        TakeDamage(Damage, BInstigator, Location, vect(0,0,0), class'DamTypeBurned');

        if (BurnDown > 0)
        {
            BurnDown --; // Decrement the number of FireDamage calls left before our Zombie is extinguished :)
        }
        if(BurnDown==0)
        {
            bBurnified = false;
        }
    }
    else
    {
        BurnDown = 0;
        bBurnified = false;
    }
}

function PlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HI )
{
    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;

    // Take you out of ironsights when taking momentum from damage
    if( VSize(Momentum) > 0 && KFWeapon(Weapon) != none )
    {
        KFWeapon(Weapon).bForceLeaveIronsights = true;
    }

    if ( Damage <= 0 )
        return;

    // Call the modified version of the original Pawn playhit
    OldPlayHit(Damage,InstigatedBy,HitLocation,DamageType,Momentum);

    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 );
    }
    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() )
        {
            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);
        }
    }

    // hack for siren
    if ( (DamageType.name == 'SirenScreamDamage') && (Health < 0) )
    {
        if( (InstigatedBy != None) && (VSize(InstigatedBy.Location - Location) < 200) )
        {
            DoDamageFX( 'obliterate', 5000 * Damage, DamageType, Rotator(HitNormal) );
        }
        else
        {
            DoDamageFX( HeadBone, 5000 * Damage, DamageType, Rotator(HitNormal) );
            PlaySound(DecapitationSound, SLOT_Misc,1.30,true,525);
        }
    }
    else
        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 PlayerController Hearer;

    if ( DamageType == None )
        return;
    if ( (Damage <= 0) && ((Controller == None) || !Controller.bGodMode) )
        return;

    if (Damage > DamageType.Default.DamageThreshold) //spawn some blood
    {
        SpawnHitEmitter( Damage, InstigatedBy, HitLocation, damageType, Momentum );
    }
    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;
    }
}

function SpawnHitEmitter( float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum )
{
    local Vector HitNormal;
    local class<Effects> DesiredEffect;
    local class<Emitter> DesiredEmitter;
    local vector BloodOffset, Mo;

    // Play any set effect
    if ( EffectIsRelevant(Location,true) )
    {
        HitNormal = Normal(HitLocation - Location);
        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
        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));
        }
    }
}

simulated function AttachEffect( class<xEmitter> EmitterClass, Name BoneName, Vector Location, Rotator Rotation )
{
    local Actor a;
    local int i;

    if( bSkeletized || (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 (a == none)
     return;

    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 );
}

simulated function SpawnGiblet( class<Gib> GibClass, Vector Location, Rotator Rotation, float GibPerterbation )
{
    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.bFlaming = bFlaming;
    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) * (250 + 260 * FRand());
    Giblet.LifeSpan = Giblet.LifeSpan + 2 * FRand() - 1;
}

State Dying
{
    simulated function AnimEnd( int Channel );

    simulated function bool SpecialCalcView( out Actor ViewActor, out vector CameraLocation, out rotator CameraRotation )
    {
        local Coords Co;
        local vector HL,HN;

        ViewActor = Self;
        Co = GetBoneCoords('CHR_Head');
        CameraLocation = Co.Origin+Co.XAxis*8;
        // Make sure camera dosent show through world geometry.
        if( Trace(HL,HN,CameraLocation+vect(0,0,25),CameraLocation,False)!=None )
            CameraLocation+=HN*25;
        if( Trace(HL,HN,CameraLocation-vect(0,0,25),CameraLocation,False)!=None )
            CameraLocation+=HN*25;
        if( Trace(HL,HN,CameraLocation+vect(25,0,0),CameraLocation,False)!=None )
            CameraLocation+=HN*25;
        if( Trace(HL,HN,CameraLocation-vect(25,0,0),CameraLocation,False)!=None )
            CameraLocation+=HN*25;
        if( Trace(HL,HN,CameraLocation+vect(0,25,0),CameraLocation,False)!=None )
            CameraLocation+=HN*25;
        if( Trace(HL,HN,CameraLocation-vect(0,25,0),CameraLocation,False)!=None )
            CameraLocation+=HN*25;
        GetAxes(GetBoneRotation('CHR_Head'),Co.XAxis,Co.YAxis,Co.ZAxis);
        CameraRotation = OrthoRotation(-Co.YAxis,Co.ZAxis,Co.XAxis); // Turns the camera by 90 degrees.
        Return True;
    }
    event FellOutOfWorld(eKillZType KillType)
    {
        local LavaDeath LD;

        // If we fall past a lava killz while dead- burn off skin.
        if( KillType == KILLZ_Lava )
        {
            if ( !bSkeletized )
            {
                if ( SkeletonMesh != None )
                {
                    LinkMesh(SkeletonMesh, true);
                    Skins.Length = 0;
                }
                bSkeletized = true;

                LD = spawn(class'LavaDeath', , , Location + vect(0, 0, 10), Rotation );
                if ( LD != None )
                    LD.SetBase(self);
                // This should destroy itself once its finished.
                PlaySound( sound'Inf_Weapons.F1.f1_explode01', SLOT_None, 1.5*TransientSoundVolume ); // KFTODO: Replace this sound
            }
            return;
        }
        Super.FellOutOfWorld(KillType);
    }

    simulated function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HI )
    {
        local emitter BloodHit;

        Health -= Damage;

        if (Health <= -200)
        {
            // Gibbed
            BloodHit = Spawn(class'KFMod.FeedingSpray',InstigatedBy,,Location,Rotation);

            // KFTODO: Replace this spawn giblet with
//            SpawnGiblet(class 'ClotGibHead',HitLocation, self.Rotation, 0.06 ) ;
//
//          SpawnGiblet(class 'ClotGibTorso',HitLocation, self.Rotation, 0.06) ;
//          SpawnGiblet(class 'ClotGibLowerTorso',HitLocation, self.Rotation, 0.06 ) ;
//
//          SpawnGiblet(class 'ClotGibArm',HitLocation, self.Rotation, 0.06 ) ;
//          SpawnGiblet(class 'ClotGibArm',HitLocation, self.Rotation, 0.06 ) ;
//
//          SpawnGiblet(class 'ClotGibThigh',HitLocation, self.Rotation, 0.06 ) ;
//          SpawnGiblet(class 'ClotGibThigh',HitLocation, self.Rotation, 0.06 ) ;
//
//          SpawnGiblet(class 'ClotGibLeg',HitLocation, self.Rotation, 0.06 ) ;
//          SpawnGiblet(class 'ClotGibLeg',HitLocation, self.Rotation, 0.06 ) ;

            if( Physics == PHYS_KarmaRagdoll )
            {
                bDestroyAfterRagDollTick = true;
            }
            else
            {
                Destroy();
            }
        }
    }

    simulated function BeginState()
    {
        local int i;

        bSpecialCalcView = Class'KFPawn'.Default.bRealDeathType;
        if ( Controller != None )
        {
            if ( Controller.bIsPlayer )
                Controller.PawnDied(self);
            else Controller.Destroy();
        }
        for (i = 0; i < Attached.length; i++)
            if (Attached[i] != None)
                Attached[i].PawnBaseDied();
        AmbientSound = None;
        if( Level.NetMode==NM_DedicatedServer )
            SetTimer(1,False);
        SetTimer(5,False);
    }

    simulated function Timer()
    {
        if( Level.NetMode==NM_DedicatedServer )
        {
            Destroy();
            Return;
        }
        if( Physics!=PHYS_None )
        {
            if( VSize(Velocity)>10 )
            {
                SetTimer(1,False);
                Return;
            }
            SetPhysics(PHYS_None);
            SetTimer(30,False);
            if(PlayerShadow != None)
                PlayerShadow.bShadowActive = false;
        }
        else if( (Level.TimeSeconds-LastRenderTime)>40 || Level.bDropDetail )
            Destroy();
        else SetTimer(5,False);
    }

    // We shorten the lifetime when the guys comes to rest.
    // Alex: No , you dont.
    event KVelDropBelow()
    {
    }
}

simulated event Destroyed()
{
	local int i;

    // Make attached explosives blow up when this pawn dies
	for( i=0; i<Attached.length; i++ )
	{
		if( SealSquealProjectile(Attached[i])!=None )
		{
			SealSquealProjectile(Attached[i]).HandleBasePawnDestroyed();
		}
	}

    if ( ItBUURRNNNS != none )
    {
        ItBUURRNNNS.Emitters[0].SkeletalMeshActor = none;
        ItBUURRNNNS.Destroy();
    }

    if( Adjuster!=None )
        Adjuster.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 ( AuxCollisionCylinder != none )
    {
        AuxCollisionCylinder.Destroy();
    }

    if ( AttachedEmitter != none )
    {
        AttachedEmitter.Destroy();
    }

    Super.Destroyed();
}

function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
    local Vector            TossVel;
    local Trigger           T;
    local NavigationPoint   N;
    local PlayerDeathMark D;
    local Projectile PP;
    local FakePlayerPawn FP;
	local int i;

    if ( bDeleteMe || Level.bLevelChange || Level.Game == None )
        return; // already destroyed, or level is being cleaned up

    if ( DamageType.default.bCausedByWorld && (Killer == None || Killer == Controller) && LastHitBy != None )
        Killer = LastHitBy;

    // mutator hook to prevent deaths
    // WARNING - don't prevent bot suicides - they suicide when really needed
    if ( Level.Game.PreventDeath(self, Killer, damageType, HitLocation) )
    {
        Health = max(Health, 1); //mutator should set this higher
        return;
    }

    // Turn off the auxilary collision when the player dies
    if (  AuxCollisionCylinder != none )
    {
        AuxCollisionCylinder.SetCollision(false,false,false);
    }

    // Hack fix for team-killing.
    if( KFPlayerReplicationInfo(PlayerReplicationInfo)!=None )
    {
        FP = KFPlayerReplicationInfo(PlayerReplicationInfo).GetBlamePawn();
        if( FP!=None )
        {
            ForEach DynamicActors(Class'Projectile',PP)
            {
                if( PP.Instigator==Self )
                    PP.Instigator = FP;
            }
        }
    }

    D = Spawn(Class'PlayerDeathMark');
    if( D!=None )
        D.Velocity = Velocity;

    Health = Min(0, Health);

    if ( Weapon != None && (DrivenVehicle == None || DrivenVehicle.bAllowWeaponToss) )
    {
        if ( Controller != None )
            Controller.LastPawnWeapon = Weapon.Class;
        Weapon.HolderDied();
        TossVel = Vector(GetViewRotation());
        TossVel = TossVel * ((Velocity Dot TossVel) + 500) + Vect(0,0,200);
        TossWeapon(TossVel);
    }
    if ( DrivenVehicle != None )
    {
        Velocity = DrivenVehicle.Velocity;
        DrivenVehicle.DriverDied();
    }

    if ( Controller != None )
    {
        Controller.WasKilledBy(Killer);
        Level.Game.Killed(Killer, Controller, self, damageType);
    }
    else Level.Game.Killed(Killer, Controller(Owner), self, damageType);

    DrivenVehicle = None;

    if ( Killer != None )
        TriggerEvent(Event, self, Killer.Pawn);
    else TriggerEvent(Event, self, None);

    // make sure to untrigger any triggers requiring player touch
    if ( IsPlayerPawn() || WasPlayerPawn() )
    {
        PhysicsVolume.PlayerPawnDiedInVolume(self);
        ForEach TouchingActors(class'Trigger',T)
            T.PlayerToucherDied(self);

        // event for HoldObjectives
        ForEach TouchingActors(class'NavigationPoint', N)
            if ( N.bReceivePlayerToucherDiedNotify )
                N.PlayerToucherDied( Self );
    }
    // remove powerup effects, etc.
    RemovePowerups();

    Velocity.Z *= 1.3;

    // Make attached explosives blow up when this pawn dies
 	for( i=0; i<Attached.length; i++ )
	{
		if( SealSquealProjectile(Attached[i])!=None )
		{
			SealSquealProjectile(Attached[i]).HandleBasePawnDestroyed();
		}
	}

    if ( IsHumanControlled() )
        PlayerController(Controller).ForceDeathUpdate();

    NetUpdateFrequency = Default.NetUpdateFrequency;
    PlayDying(DamageType, HitLocation);
    if ( !bPhysicsAnimUpdate && !IsLocallyControlled() )
        ClientDying(DamageType, HitLocation);
}

simulated function ChunkUp( Rotator HitRotation, float ChunkPerterbation )
{
  /*
    if ( (Level.NetMode != NM_Client) && (Controller != None) )
    {
        if ( Controller.bIsPlayer )
            Controller.PawnDied(self);
        else
            Controller.Destroy();
    }

    bTearOff = true;
    HitDamageType = class'Gibbed'; // make sure clients gib also
    if ( (Level.NetMode == NM_DedicatedServer) || (Level.NetMode == NM_ListenServer) )
        GotoState('TimingOut');
    if ( Level.NetMode == NM_DedicatedServer )
        return;
    if ( class'GameInfo'.static.UseLowGore() )
    {
        Destroy();
        return;
    }
    SpawnGibs(HitRotation,ChunkPerterbation);

    if ( Level.NetMode != NM_ListenServer )
        Destroy();
        */
}


// toss some of your cash away. (to help a cash-strapped ally or perhaps just to party like its 1994)
exec function TossCash( int Amount )
{
    local Vector X,Y,Z;
    local CashPickup CashPickup ;
    local Vector TossVel;
    local Actor A;

    if( Amount<=0 )
        Amount = 50;
    Controller.PlayerReplicationInfo.Score = int(Controller.PlayerReplicationInfo.Score); // To fix issue with throwing 0 pounds.
    if( Controller.PlayerReplicationInfo.Score<=0 || Amount<=0 )
        return;
    Amount = Min(Amount,int(Controller.PlayerReplicationInfo.Score));

    GetAxes(Rotation,X,Y,Z);

    TossVel = Vector(GetViewRotation());
    TossVel = TossVel * ((Velocity Dot TossVel) + 500) + Vect(0,0,200);

    CashPickup = Spawn(class'CashPickup',,, Location + 0.8 * CollisionRadius * X - 0.5 * CollisionRadius * Y);

    if(CashPickup != none)
    {
        CashPickup.CashAmount = Amount;
        CashPickup.bDroppedCash = true;
        CashPickup.RespawnTime = 0;   // Dropped cash doesnt respawn. For obvious reasons.
        CashPickup.Velocity = TossVel;
        CashPickup.DroppedBy = Controller;
        CashPickup.InitDroppedPickupFor(None);
        Controller.PlayerReplicationInfo.Score -= Amount;

        if ( Level.Game.NumPlayers > 1 && Level.TimeSeconds - LastDropCashMessageTime > DropCashMessageDelay )
        {
            PlayerController(Controller).Speech('AUTO', 4, "");
        }

        // Hack to get Slot machines to accept dosh that's thrown inside their collision cylinder.
        ForEach CashPickup.TouchingActors(class 'Actor', A)
        {
            if(A.IsA('KF_Slot_Machine'))
            {
                A.Touch(Cashpickup);
            }
        }
    }
}

// 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( bSkeletized || (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 );
}

simulated function RemoveFlamingEffects()
{
    local int i;

    if( Level.NetMode == NM_DedicatedServer )
        return;

    for( i=0; i<Attached.length; i++ )
    {
        if( Attached[i].IsA('xEmitter') && !Attached[i].IsA('BloodJet'))
        {
            xEmitter(Attached[i]).mRegen = false;
        }

         if( Attached[i].IsA('KFMonsterFlame'))
        {
          Attached[i].LifeSpan = 0.1;
        }
    }
}

simulated function class<KFVeterancyTypes> GetVeteran()
{
    if( KFPlayerReplicationInfo(PlayerReplicationInfo) != none && KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none )
    {
        return KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill;
    }

    return class'KFVeterancyTypes'; // The base neutral class.
}

function bool CanCarry( float Weight )
{
    Return True;
}

// Validate that client is not hacking.
function bool CanBuyNow()
{
    local ShopVolume Sh;

    if( KFGameType(Level.Game)==None || KFGameType(Level.Game).bWaveInProgress || PlayerReplicationInfo==None )
        return False;
    foreach TouchingActors(Class'ShopVolume',Sh)
        Return True;
    Return False;
}

function bool ItemIsBuyable( Class<Pickup> IC )
{
    local int i;

    if( LevRls==None )
    {
        ForEach DynamicActors(Class'KFLevelRules',LevRls)
            Break;
        if( LevRls==None )
            Return False;
    }

    For( i=0; i<25; i++ )
    {
        if( LevRls.ItemForSale[i]==IC )
            Return True;
    }

    Return False;
}

simulated function ClientCurrentWeaponSold()
{
    local Inventory I;
    local int Count;

    for ( I = Inventory; I != none && Count < 50; I = I.Inventory)
    {
        if ( I != Weapon && I != PendingWeapon && Weapon(I) != none )
        {
            PendingWeapon = Weapon(I);
            break;
        }

        Count++;
    }

    ChangedWeapon();
}

simulated function ClientForceChangeWeapon(Inventory NewWeapon)
{
    PendingWeapon = Weapon(NewWeapon);
    ChangedWeapon();
}

function ServerBuyPotato()
{
    local Controller C;

    for ( C = Level.ControllerList; C != none; C = C.NextController )
    {
        if ( PlayerController(C) != none && PlayerController(C).SteamStatsAndAchievements != none )
        {
            PlayerController(C).SteamStatsAndAchievements.SetSteamAchievementCompleted(132);
        }
    }

    PlayerReplicationInfo.Score -= 70000;
}

function ServerBuyWeapon( Class<Weapon> WClass, float ItemWeight )
{
    local Inventory I, J;
    local float Price;
    local bool bIsDualWeapon, bHasDual9mms, bHasDualHCs, bHasDualRevolvers;

    if ( !CanBuyNow() || Class<KFWeapon>(WClass) == none || Class<KFWeaponPickup>(WClass.Default.PickupClass) == none )
    {
        return;
    }

    if ( Class<KFWeapon>(WClass).Default.AppID > 0 && Class<KFWeapon>(WClass).Default.UnlockedByAchievement != -1 )
    {

        if ( KFSteamStatsAndAchievements(PlayerReplicationInfo.SteamStatsAndAchievements) == none ||
            (!KFSteamStatsAndAchievements(PlayerReplicationInfo.SteamStatsAndAchievements).PlayerOwnsWeaponDLC(Class<KFWeapon>(WClass).Default.AppID) &&
             KFSteamStatsAndAchievements(PlayerReplicationInfo.SteamStatsAndAchievements).Achievements[Class<KFWeapon>(WClass).Default.UnlockedByAchievement].bCompleted != 1 ))
        {
            return;
        }

    }
    else if ( Class<KFWeapon>(WClass).Default.AppID > 0 )
    {
        if ( KFSteamStatsAndAchievements(PlayerReplicationInfo.SteamStatsAndAchievements) == none ||
            !KFSteamStatsAndAchievements(PlayerReplicationInfo.SteamStatsAndAchievements).PlayerOwnsWeaponDLC(Class<KFWeapon>(WClass).Default.AppID))
        {
            return;
        }
    }
    else if ( Class<KFWeapon>(WClass).Default.UnlockedByAchievement != -1  )
    {
        if ( KFSteamStatsAndAchievements(PlayerReplicationInfo.SteamStatsAndAchievements) == none ||
             KFSteamStatsAndAchievements(PlayerReplicationInfo.SteamStatsAndAchievements).Achievements[Class<KFWeapon>(WClass).Default.UnlockedByAchievement].bCompleted != 1 )
        {
            return;
        }
    }

    Price = class<KFWeaponPickup>(WClass.Default.PickupClass).Default.Cost;

    if ( KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none )
    {
        Price *= KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.GetCostScaling(KFPlayerReplicationInfo(PlayerReplicationInfo), WClass.Default.PickupClass);
    }

    for ( I=Inventory; I!=None; I=I.Inventory )
    {
        if( I.Class==WClass )
        {
            Return; // Already has weapon.
        }

        if ( I.Class == class'Dualies' )
        {
            bHasDual9mms = true;
        }
        else if ( I.Class == class'DualDeagle' || I.Class == class'GoldenDualDeagle' )
        {
            bHasDualHCs = true;
        }
        else if ( I.Class == class'Dual44Magnum' )
        {
            bHasDualRevolvers = true;
        }
    }

    if ( WClass == class'DualDeagle' )
    {
        for ( J = Inventory; J != None; J = J.Inventory )
        {
            if ( J.class == class'Deagle' )
            {
                Price = Price / 2;
                break;
            }
        }

        bIsDualWeapon = true;
        bHasDualHCs = true;
    }

    if ( WClass == class'GoldenDualDeagle' )
    {
        for ( J = Inventory; J != None; J = J.Inventory )
        {
            if ( J.class == class'GoldenDeagle' )
            {
                Price = Price / 2;
                break;
            }
        }

        bIsDualWeapon = true;
        bHasDualHCs = true;
    }

    if ( WClass == class'Dual44Magnum' )
    {
        for ( J = Inventory; J != None; J = J.Inventory )
        {
            if ( J.class == class'Magnum44Pistol' )
            {
                Price = Price / 2;
                break;
            }
        }

        bIsDualWeapon = true;
        bHasDualRevolvers = true;
    }

    if ( WClass == class'DualMK23Pistol' )
    {
        for ( J = Inventory; J != None; J = J.Inventory )
        {
            if ( J.class == class'MK23Pistol' )
            {
                Price = Price / 2;
                break;
            }
        }

        bIsDualWeapon = true;
    }

    if ( WClass == class'DualFlareRevolver' )
    {
        for ( J = Inventory; J != None; J = J.Inventory )
        {
            if ( J.class == class'FlareRevolver' )
            {
                Price = Price / 2;
                break;
            }
        }

        bIsDualWeapon = true;
    }

    bIsDualWeapon = bIsDualWeapon || WClass == class'Dualies';

    if ( !CanCarry(ItemWeight) )
    {
        Return;
    }

    if ( PlayerReplicationInfo.Score < Price )
    {
        Return; // Not enough CASH.
    }

    I = Spawn(WClass);

    if ( I != none )
    {
        if ( KFGameType(Level.Game) != none )
        {
            KFGameType(Level.Game).WeaponSpawned(I);
        }

        KFWeapon(I).UpdateMagCapacity(PlayerReplicationInfo);
        KFWeapon(I).FillToInitialAmmo();
        KFWeapon(I).SellValue = Price * 0.75;
        I.GiveTo(self);
        PlayerReplicationInfo.Score -= Price;

        if ( bIsDualWeapon )
        {
            KFSteamStatsAndAchievements(PlayerReplicationInfo.SteamStatsAndAchievements).OnDualsAddedToInventory(bHasDual9mms, bHasDualHCs, bHasDualRevolvers);
        }

        ClientForceChangeWeapon(I);
    }

    SetTraderUpdate();
}

function ServerSellWeapon( Class<Weapon> WClass )
{
    local Inventory I;
    local Single NewSingle;
    local Deagle NewDeagle;
    local Magnum44Pistol New44Magnum;
    local MK23Pistol NewMK23;
    local FlareRevolver NewFlare;
    local float Price;

    if ( !CanBuyNow() || Class<KFWeapon>(WClass) == none || Class<KFWeaponPickup>(WClass.Default.PickupClass) == none )
    {
        SetTraderUpdate();
        Return;
    }

    for ( I = Inventory; I != none; I = I.Inventory )
    {
        if ( I.Class == WClass )
        {
            if ( KFWeapon(I) != none && KFWeapon(I).SellValue != -1 )
            {
                Price = KFWeapon(I).SellValue;
            }
            else
            {
                Price = int(class<KFWeaponPickup>(WClass.default.PickupClass).default.Cost * 0.75);

                if ( KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none )
                {
                    Price *= KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.GetCostScaling(KFPlayerReplicationInfo(PlayerReplicationInfo), WClass.Default.PickupClass);
                }
            }

            if ( Dualies(I) != none && DualDeagle(I) == none && Dual44Magnum(I) == none
                && DualMK23Pistol(I) == none && DualFlareRevolver(I) == none )
            {
                NewSingle = Spawn(class'Single');
                NewSingle.GiveTo(self);
            }

            if ( DualDeagle(I) != none )
            {
                if( GoldenDualDeagle(I) != none )
                {
                    NewDeagle = Spawn(class'GoldenDeagle');
                }
                else
                {
                    NewDeagle = Spawn(class'Deagle');
                }
                NewDeagle.GiveTo(self);
                Price = Price / 2;
                NewDeagle.SellValue = Price;
            }

            if ( Dual44Magnum(I) != none )
            {
                New44Magnum = Spawn(class'Magnum44Pistol');
                New44Magnum.GiveTo(self);
                Price = Price / 2;
                New44Magnum.SellValue = Price;
            }

            if ( DualMK23Pistol(I) != none )
            {
                NewMK23 = Spawn(class'MK23Pistol');
                NewMK23.GiveTo(self);
                Price = Price / 2;
                NewMK23.SellValue = Price;
            }

            if ( DualFlareRevolver(I) != none )
            {
                NewFlare = Spawn(class'FlareRevolver');
                NewFlare.GiveTo(self);
                Price = Price / 2;
                NewFlare.SellValue = Price;
            }

            if ( I == Weapon || I == PendingWeapon )
            {
                ClientCurrentWeaponSold();
            }

            PlayerReplicationInfo.Score += Price;

            I.Destroyed();
            I.Destroy();

            SetTraderUpdate();

            if ( KFGameType(Level.Game) != none )
            {
                KFGameType(Level.Game).WeaponDestroyed(WClass);
            }

            return;
        }
    }
}

function ServerBuyKevlar()
{
    local float Cost;
    local int UnitsAffordable;

    Cost = class'Vest'.default.ItemCost * ((100.0 - ShieldStrength) / 100.0);

    if ( KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none)
    {
        Cost *= KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.GetCostScaling(KFPlayerReplicationInfo(PlayerReplicationInfo), class'Vest');
    }

    if ( !CanBuyNow() || ShieldStrength==100 )
    {
        SetTraderUpdate();
        Return;
    }

    if ( PlayerReplicationInfo.Score >= Cost )
    {
        PlayerReplicationInfo.Score -= Cost;
        ShieldStrength = 100;
    }
    else if ( ShieldStrength > 0 )
    {
        Cost = class'Vest'.default.ItemCost;
        if ( KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none)
        {
            Cost *= KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.GetCostScaling(KFPlayerReplicationInfo(PlayerReplicationInfo), class'Vest');
        }

        Cost /= 100.0;

        UnitsAffordable = int(PlayerReplicationInfo.Score / Cost);

        PlayerReplicationInfo.Score -= int(Cost * UnitsAffordable);

        ShieldStrength += UnitsAffordable;
    }

    SetTraderUpdate();
}

function ServerBuyFirstAid()
{
    local int Cost;

    Cost = class'FirstAidKit'.default.ItemCost;

    if( !CanBuyNow() || Health==100 )
    {
        SetTraderUpdate();
        Return;
    }

    if( PlayerReplicationInfo.Score < Cost )
    {
        SetTraderUpdate();
        Return;
    }

    PlayerReplicationInfo.Score -= Cost;
    GiveHealth(100, 100);

    SetTraderUpdate();
}

function bool ServerBuyAmmo( Class<Ammunition> AClass, bool bOnlyClip )
{
    local Inventory I;
    local float Price;
    local Ammunition AM;
    local KFWeapon KW;
    local int c;
    local float UsedMagCapacity;
    local Boomstick DBShotty;

    if ( !CanBuyNow() || AClass == None )
    {
        SetTraderUpdate();
        return false;
    }

    for ( I=Inventory; I != none; I=I.Inventory )
    {
        if ( I.Class == AClass )
        {
            AM = Ammunition(I);
        }
        else if ( KW == None && KFWeapon(I) != None && (Weapon(I).AmmoClass[0] == AClass || Weapon(I).AmmoClass[1] == AClass) )
        {
            KW = KFWeapon(I);
        }
    }

    if ( KW == none || AM == none )
    {
        SetTraderUpdate();
        return false;
    }

    DBShotty = Boomstick(KW);

    AM.MaxAmmo = AM.default.MaxAmmo;

    if ( KFPlayerReplicationInfo(PlayerReplicationInfo) != none && KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none )
    {
        AM.MaxAmmo = int(float(AM.MaxAmmo) * KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.AddExtraAmmoFor(KFPlayerReplicationInfo(PlayerReplicationInfo), AClass));
    }

    if ( AM.AmmoAmount >= AM.MaxAmmo )
    {
        SetTraderUpdate();
        return false;
    }

    Price = class<KFWeaponPickup>(KW.PickupClass).default.AmmoCost * KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.GetAmmoCostScaling(KFPlayerReplicationInfo(PlayerReplicationInfo), KW.PickupClass); // Clip price.

    if ( KW.bHasSecondaryAmmo && AClass == KW.FireModeClass[1].default.AmmoClass )
    {
        UsedMagCapacity = 1; // Secondary Mags always have a Mag Capacity of 1? KW.default.SecondaryMagCapacity;
    }
    else
    {
        UsedMagCapacity = KW.default.MagCapacity;
    }

    if( KW.PickupClass == class'HuskGunPickup' )
    {
        UsedMagCapacity = class<HuskGunPickup>(KW.PickupClass).default.BuyClipSize;
    }

    if ( bOnlyClip )
    {
        if ( KFPlayerReplicationInfo(PlayerReplicationInfo) != none && KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none )
        {
            if( KW.PickupClass == class'HuskGunPickup' )
            {
                c = UsedMagCapacity * KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.AddExtraAmmoFor(KFPlayerReplicationInfo(PlayerReplicationInfo), AM.Class);
            }
            else
            {
                c = UsedMagCapacity * KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.GetMagCapacityMod(KFPlayerReplicationInfo(PlayerReplicationInfo), KW);
            }
        }
        else
        {
            c = UsedMagCapacity;
        }
    }
    else
    {
        c = (AM.MaxAmmo-AM.AmmoAmount);
    }

    Price = int(float(c) / UsedMagCapacity * Price);

    if ( PlayerReplicationInfo.Score < Price ) // Not enough CASH (so buy the amount you CAN buy).
    {
        c *= (PlayerReplicationInfo.Score/Price);

        if ( c == 0 )
        {
            SetTraderUpdate();
            return false; // Couldn't even afford 1 bullet.
        }

        AM.AddAmmo(c);
        if( DBShotty != none )
        {
            DBShotty.AmmoPickedUp();
        }

        PlayerReplicationInfo.Score = Max(PlayerReplicationInfo.Score - (float(c) / UsedMagCapacity * Price), 0);

        SetTraderUpdate();

        return false;
    }

    PlayerReplicationInfo.Score = int(PlayerReplicationInfo.Score-Price);
    AM.AddAmmo(c);
    if( DBShotty != none )
    {
        DBShotty.AmmoPickedUp();
    }

    SetTraderUpdate();

    return true;
}

function ServerSellAmmo( Class<Ammunition> AClass )
{
    local Inventory I;
    local float Price;
    local Ammunition AM;
    local KFWeapon KW;
    local int c;

    if( !CanBuyNow() || AClass==None )
        Return;
    For( I=Inventory; I!=None; I=I.Inventory )
    {
        if( I.Class==AClass )
            AM = Ammunition(I);
        else if( KW==None && KFWeapon(I)!=None && (Weapon(I).AmmoClass[0]==AClass || Weapon(I).AmmoClass[1]==AClass) )
            KW = KFWeapon(I);
    }
    if( (AM==None && KW==None) || AM.AmmoAmount==AM.MaxAmmo )
        Return;
    Price = Class<KFWeaponPickup>(KW.PickupClass).Default.AmmoCost; // Clip price.
    c = KW.Default.MagCapacity;
    if( c>AM.AmmoAmount )
        c = AM.AmmoAmount;
    Price = float(c)/float(KW.Default.MagCapacity)*Price*0.75;
    PlayerReplicationInfo.Score+=Price;
    AM.AmmoAmount-=c;

    SetTraderUpdate();
}

simulated function SetTraderUpdate()
{
    if ( KFPlayerController(Controller) != none )
    {
        KFPlayerController(Controller).DoTraderUpdate();
    }
}

// Allow players spawn on top of each other.
event bool EncroachingOn( actor Other )
{
    if ( Other.bWorldGeometry || Other.bBlocksTeleport )
        return true;

    if ( (Vehicle(Other) != None) && (Weapon != None) && Weapon.IsA('Translauncher') )
        return true;

    return false;
}
event EncroachedBy( actor Other )
{
    if ( Pawn(Other)!=None && Vehicle(Other)==None && KFPawn(Other)==None )
        gibbedBy(Other);
}

function name GetWeaponBoneFor(Inventory I)
{
     return 'WeaponR_Bone';
}

function name GetOffhandBoneFor(Inventory I)
{
     return 'WeaponL_Bone';
}


simulated function SetWeaponAttachment(WeaponAttachment NewAtt)
{
    local KFWeaponAttachment KFWeapAttach;
    local DualiesAttachment DualiesAttach;
    local int i;

    WeaponAttachment = NewAtt;
    KFWeapAttach =  KFWeaponAttachment(WeaponAttachment);
    DualiesAttach = DualiesAttachment(WeaponAttachment);

    // For remote clients set the brother class for dualies
    if( Level.NetMode == NM_Client && DualiesAttach != none )
    {
        for( i = 0; i < Attached.Length; i++ )
        {
            if( Attached[i] == None )
                continue;

            if( DualiesAttachment(Attached[i]) != none && DualiesAttach != Attached[i] )
            {
                DualiesAttach.brother = DualiesAttachment(Attached[i]);
                DualiesAttachment(Attached[i]).brother = DualiesAttach;
                break;
            }
        }
    }

    if( KFWeapAttach != none )
    {
        MovementAnims[0]= KFWeapAttach.MovementAnims[0];
        MovementAnims[1]= KFWeapAttach.MovementAnims[1];
        MovementAnims[2]= KFWeapAttach.MovementAnims[2];
        MovementAnims[3]= KFWeapAttach.MovementAnims[3];
        TurnLeftAnim = KFWeapAttach.TurnLeftAnim;
        TurnRightAnim = KFWeapAttach.TurnRightAnim;
        SwimAnims[0]= KFWeapAttach.SwimAnims[0];
        SwimAnims[1]= KFWeapAttach.SwimAnims[1];
        SwimAnims[2]= KFWeapAttach.SwimAnims[2];
        SwimAnims[3]= KFWeapAttach.SwimAnims[3];
        CrouchAnims[0]= KFWeapAttach.CrouchAnims[0];
        CrouchAnims[1]= KFWeapAttach.CrouchAnims[1];
        CrouchAnims[2]= KFWeapAttach.CrouchAnims[2];
        CrouchAnims[3]= KFWeapAttach.CrouchAnims[3];
        WalkAnims[0]= KFWeapAttach.WalkAnims[0];
        WalkAnims[1]= KFWeapAttach.WalkAnims[1];
        WalkAnims[2]= KFWeapAttach.WalkAnims[2];
        WalkAnims[3]= KFWeapAttach.WalkAnims[3];
        AirAnims[0]= KFWeapAttach.AirAnims[0];
        AirAnims[1]= KFWeapAttach.AirAnims[1];
        AirAnims[2]= KFWeapAttach.AirAnims[2];
        AirAnims[3]= KFWeapAttach.AirAnims[3];
        TakeoffAnims[0]= KFWeapAttach.TakeoffAnims[0];
        TakeoffAnims[1]= KFWeapAttach.TakeoffAnims[1];
        TakeoffAnims[2]= KFWeapAttach.TakeoffAnims[2];
        TakeoffAnims[3]= KFWeapAttach.TakeoffAnims[3];
        LandAnims[0]= KFWeapAttach.LandAnims[0];
        LandAnims[1]= KFWeapAttach.LandAnims[1];
        LandAnims[2]= KFWeapAttach.LandAnims[2];
        LandAnims[3]= KFWeapAttach.LandAnims[3];
        DoubleJumpAnims[0]= KFWeapAttach.DoubleJumpAnims[0];
        DoubleJumpAnims[1]= KFWeapAttach.DoubleJumpAnims[1];
        DoubleJumpAnims[2]= KFWeapAttach.DoubleJumpAnims[2];
        DoubleJumpAnims[3]= KFWeapAttach.DoubleJumpAnims[3];
        DodgeAnims[0]= KFWeapAttach.DodgeAnims[0];
        DodgeAnims[1]= KFWeapAttach.DodgeAnims[1];
        DodgeAnims[2]= KFWeapAttach.DodgeAnims[2];
        DodgeAnims[3]= KFWeapAttach.DodgeAnims[3];
        AirStillAnim = KFWeapAttach.AirStillAnim;
        TakeoffStillAnim = KFWeapAttach.TakeoffStillAnim;
        CrouchTurnRightAnim = KFWeapAttach.CrouchTurnRightAnim;
        CrouchTurnLeftAnim = KFWeapAttach.CrouchTurnLeftAnim;
        IdleCrouchAnim = KFWeapAttach.IdleCrouchAnim;
        IdleSwimAnim = KFWeapAttach.IdleSwimAnim;
        IdleWeaponAnim = KFWeapAttach.IdleWeaponAnim;
        IdleRestAnim = KFWeapAttach.IdleRestAnim;
        IdleChatAnim = KFWeapAttach.IdleChatAnim;
        FireAnims[0]=KFWeapAttach.FireAnims[0];
        FireAnims[1]=KFWeapAttach.FireAnims[1];
        FireAnims[2]=KFWeapAttach.FireAnims[2];
        FireAnims[3]=KFWeapAttach.FireAnims[3];
        FireAltAnims[0]=KFWeapAttach.FireAltAnims[0];
        FireAltAnims[1]=KFWeapAttach.FireAltAnims[1];
        FireAltAnims[2]=KFWeapAttach.FireAltAnims[2];
        FireAltAnims[3]=KFWeapAttach.FireAltAnims[3];
        FireCrouchAnims[0]=KFWeapAttach.FireCrouchAnims[0];
        FireCrouchAnims[1]=KFWeapAttach.FireCrouchAnims[1];
        FireCrouchAnims[2]=KFWeapAttach.FireCrouchAnims[2];
        FireCrouchAnims[3]=KFWeapAttach.FireCrouchAnims[3];
        FireCrouchAltAnims[0]=KFWeapAttach.FireCrouchAltAnims[0];
        FireCrouchAltAnims[1]=KFWeapAttach.FireCrouchAltAnims[1];
        FireCrouchAltAnims[2]=KFWeapAttach.FireCrouchAltAnims[2];
        FireCrouchAltAnims[3]=KFWeapAttach.FireCrouchAltAnims[3];
        HitAnims[0]=KFWeapAttach.HitAnims[0];
        HitAnims[1]=KFWeapAttach.HitAnims[1];
        HitAnims[2]=KFWeapAttach.HitAnims[2];
        HitAnims[3]=KFWeapAttach.HitAnims[3];
        PostFireBlendStandAnim = KFWeapAttach.PostFireBlendStandAnim;
        PostFireBlendCrouchAnim = KFWeapAttach.PostFireBlendCrouchAnim;
    }
}

simulated function PlayDirectionalHit(Vector HitLoc)
{
    local Vector X,Y,Z, Dir;

    if ( DrivenVehicle != None )
        return;

    GetAxes(Rotation, X,Y,Z);
    HitLoc.Z = Location.Z;

    // random
    if ( VSize(Location - HitLoc) < 1.0 )
    {
        Dir = VRand();
    }
    // hit location based
    else
    {
        Dir = -Normal(Location - HitLoc);
    }

    if ( Dir Dot X > 0.7 || Dir == vect(0,0,0))
    {
        PlayAnim(HitAnims[0],, 0.1);
    }
    else if ( Dir Dot X < -0.7 )
    {
        PlayAnim(HitAnims[1],, 0.1);
    }
    else if ( Dir Dot Y > 0 )
    {
        PlayAnim(HitAnims[3],, 0.1);
    }
    else
    {
        PlayAnim(HitAnims[2],, 0.1);
    }
}

//-----------------------------------------------------------------------------
// FootStepping - overriden to support custom footstep volumes
//-----------------------------------------------------------------------------
simulated function FootStepping(int Side)
{
    local int SurfaceTypeID, i;
    local actor A;
    local material FloorMat;
    local vector HL,HN,Start,End,HitLocation,HitNormal;
    local float FootVolumeMod;

    SurfaceTypeID = 0;
    FootVolumeMod = 1.0;

    for ( i=0; i<Touching.Length; i++ )
        if ( ((PhysicsVolume(Touching[i]) != None) && PhysicsVolume(Touching[i]).bWaterVolume)
            || (FluidSurfaceInfo(Touching[i]) != None) )
        {
            PlaySound(sound'Inf_Player.FootStepWaterDeep', SLOT_Interact, FootstepVolume * 2,, FootStepSoundRadius);

            // Play a water ring effect as you walk through the water
            if ( !Level.bDropDetail && (Level.DetailMode != DM_Low) && (Level.NetMode != NM_DedicatedServer)
                && !Touching[i].TraceThisActor(HitLocation, HitNormal,Location - CollisionHeight*vect(0,0,1.1), Location) )
            {
                    Spawn(class'WaterRingEmitter',,,HitLocation,rot(16384,0,0));
            }

            return;
        }

    // Lets still play the sounds when walking slow, just play them quieter
    if ( bIsCrouched || bIsWalking )
    {
        FootVolumeMod = QuietFootStepVolume;
    }

    if ( (Base!=None) && (!Base.IsA('LevelInfo')) && (Base.SurfaceType!=0) )
    {
        SurfaceTypeID = Base.SurfaceType;
    }
    else
    {
        Start = Location - Vect(0,0,1)*CollisionHeight;
        End = Start - Vect(0,0,16);
        A = Trace(hl,hn,End,Start,false,,FloorMat);
        if (FloorMat !=None)
            SurfaceTypeID = FloorMat.SurfaceType;
    }
    PlaySound(SoundFootsteps[SurfaceTypeID], SLOT_Interact, (FootstepVolume * FootVolumeMod),,(FootStepSoundRadius * FootVolumeMod));
}

// Footstep sound checking for non local player or non player bots
// This function is only called on non owned network clients or bots
simulated function CheckFootSteps(float DeltaTime)
{
    local float Speed2D;
    local float OldBobTime;
    local int m,n;

    OldBobTime = BobTime;

    if (Physics == PHYS_Walking )
    {
        Speed2D = VSize(Velocity);

        if ( Speed2D < 10 )
            BobTime += 0.2 * DeltaTime;
        else
            BobTime += DeltaTime * (0.3 + 0.7 * Speed2D/GroundSpeed);
    }
    else
    {
        BobTime = 0;
    }

    if ( (Physics != PHYS_Walking) || (VSize(Velocity) < 10) )
        return;

    m = int(0.5 * Pi + 9.0 * OldBobTime/Pi);
    n = int(0.5 * Pi + 9.0 * BobTime/Pi);

    if ( (m != n) && !bIsCrawling)
        FootStepping(0);
}

function CheckBob(float DeltaTime, vector Y)
{
    local float Speed2D;
    local float OldBobTime;
    local int m,n;
    local float UsedBobScaleModifier;

    OldBobTime = BobTime;

    Bob = FClamp(Bob, -0.01, 0.01);
    UsedBobScaleModifier = BobScaleModifier;

    // Modify the amount of bob based on the movement state
    if( bIsCrouched )
    {
        UsedBobScaleModifier = 2.5;
    }

    if (Physics == PHYS_Walking )
    {
        Speed2D = VSize(Velocity);

        Speed2D *= BobSpeedModifier;

        if ( Speed2D < 10 )
            BobTime += 0.2 * DeltaTime;
        else
            BobTime += DeltaTime * (0.3 + 0.7 * Speed2D/GroundSpeed);

        WalkBob = Y * (Bob * UsedBobScaleModifier) * Speed2D * sin(8 * BobTime);
        AppliedBob = AppliedBob * (1 - FMin(1, 16 * deltatime));
        WalkBob.Z = AppliedBob;
        if ( Speed2D > 10 )
            WalkBob.Z = WalkBob.Z + 0.75 * (Bob * UsedBobScaleModifier) * Speed2D * sin(16 * BobTime);
        if ( LandBob > 0.01 )
        {
            AppliedBob += FMin(1, 16 * deltatime) * LandBob;
            LandBob *= (1 - 8*Deltatime);
        }
    }
    else if ( Physics == PHYS_Swimming )
    {
        Speed2D = Sqrt(Velocity.X * Velocity.X + Velocity.Y * Velocity.Y);
        WalkBob = Y * Bob *  0.5 * Speed2D * sin(4.0 * Level.TimeSeconds);
        WalkBob.Z = Bob * 1.5 * Speed2D * sin(8.0 * Level.TimeSeconds);
    }
    else
    {
        BobTime = 0;
        WalkBob = WalkBob * (1 - FMin(1, 8 * deltatime));
    }

    if ( (Physics != PHYS_Walking) || (VSize(Velocity) < 10)
        || ((PlayerController(Controller) != None) && PlayerController(Controller).bBehindView) )
        return;

    m = int(0.5 * Pi + 9.0 * OldBobTime/Pi);
    n = int(0.5 * Pi + 9.0 * BobTime/Pi);

    if ( (m != n) )
        FootStepping(0);
}

function Sound GetSound(xPawnSoundGroup.ESoundType soundType)
{
    local int SurfaceTypeID;
    local actor A;
    local vector HL,HN,Start,End;
    local material FloorMat;

    if( SoundGroupClass == none )
    {
       return None;
    }

    if( soundType == EST_Land || soundType == EST_Jump )
    {
        if ( (Base!=None) && (!Base.IsA('LevelInfo')) && (Base.SurfaceType!=0) )
        {
            SurfaceTypeID = Base.SurfaceType;
        }
        else
        {
            Start = Location - Vect(0,0,1)*CollisionHeight;
            End = Start - Vect(0,0,16);
            A = Trace(hl,hn,End,Start,false,,FloorMat);
            if (FloorMat !=None)
                SurfaceTypeID = FloorMat.SurfaceType;
        }
    }

    return SoundGroupClass.static.GetSound(soundType, SurfaceTypeID);
}

/* Quickly select syring, alt fire once, select old weapon again */
simulated exec function QuickHeal()
{
    local Syringe S;
    local Inventory I;
    local byte C;

    if ( Health>=HealthMax )
        return;
    for( I=Inventory; (I!=None && C++<250); I=I.Inventory )
    {
        S = Syringe(I);
        if( S!=None )
            break;
    }
    if ( S == none )
        return;

    if ( S.ChargeBar() < 0.95 )
    {
        if ( PlayerController(Controller) != none && HUDKillingFloor(PlayerController(Controller).myHud) != none )
        {
            HUDKillingFloor(PlayerController(Controller).myHud).ShowQuickSyringe();
        }

        return; // No can heal.
    }

    bIsQuickHealing = 1;
    if ( Weapon==None )
    {
        PendingWeapon = S;
        ChangedWeapon();
    }
    else if ( Weapon!=S )
    {
        PendingWeapon = S;
        Weapon.PutDown();
    }
    else // Syringe already selected, just start healing.
    {
        bIsQuickHealing = 0;
        S.HackClientStartFire();
    }
}

simulated exec function ToggleFlashlight()
{
    local KFWeapon KFWeap;

    if ( Controller == none )
    {
        return;
    }

    if ( KFWeapon(Weapon) != none && KFWeapon(Weapon).bTorchEnabled )
    {
        Weapon.ClientStartFire(1);
    }
    else
    {
        KFWeap = KFWeapon(FindInventoryType(class'Shotgun'));
        if ( KFWeap == none )
        {
            KFWeap = KFWeapon(FindInventoryType(class'BenelliShotgun'));
            if ( KFWeap == none )
            {
                KFWeap = KFWeapon(FindInventoryType(class'Dualies'));
                if ( KFWeap == none || DualDeagle(KFWeap) != none || Dual44Magnum(KFWeap) != none
                    || DualMK23Pistol(KFWeap) != none || DualFlareRevolver(KFWeap) != none )
                {
                    KFWeap = KFWeapon(FindInventoryType(class'Single'));
                }
            }
        }

        if ( KFWeap != none )
        {
            KFWeap.bPendingFlashlight = true;

            PendingWeapon = KFWeap;

            if ( Weapon != none )
            {
                Weapon.PutDown();
            }
            else
            {
                ChangedWeapon();
            }
        }
    }
}

function GiveWeapon(string aClassName )
{
    local class<Weapon> WeaponClass;
    local Inventory I;
    local Weapon NewWeapon;
    local bool bHasDual9mms, bHasDualHCs, bHasDualRevolvers;

    WeaponClass = class<Weapon>(DynamicLoadObject(aClassName, class'Class'));

    for ( I = Inventory; I != none; I = I.Inventory )
    {
        if( I.Class == WeaponClass )
        {
            Return; // Already has weapon.
        }

        if ( I.Class == class'Dualies' )
        {
            bHasDual9mms = true;
        }
        else if ( I.Class == class'DualDeagle' || I.Class == class'GoldenDualDeagle' )
        {
            bHasDualHCs = true;
        }
        else if ( I.Class == class'Dual44Magnum' )
        {
            bHasDualRevolvers = true;
        }
    }

    newWeapon = Spawn(WeaponClass);
    if ( newWeapon != none )
    {
        newWeapon.GiveTo(self);

        if ( WeaponClass == class'Dualies' || WeaponClass == class'DualDeagle' || WeaponClass == class'GoldenDualDeagle' || WeaponClass == class'Dual44Magnum' )
        {
            KFSteamStatsAndAchievements(PlayerReplicationInfo.SteamStatsAndAchievements).OnDualsAddedToInventory(bHasDual9mms, bHasDualHCs, bHasDualRevolvers);
        }

        if ( KFGameType(Level.Game) != none )
        {
            KFGameType(Level.Game).WeaponSpawned(newWeapon);
        }
    }
}

function bool DoJump( bool bUpdating )
{
    if ( Super.DoJump(bUpdating) )
    {
        // Take you out of ironsights if you jump on a non-lowgrav map
        if( KFWeapon(Weapon) != none && PhysicsVolume.Gravity.Z <= class'PhysicsVolume'.default.Gravity.Z )
        {
            KFWeapon(Weapon).ForceZoomOutTime = Level.TimeSeconds + 0.01;
        }
        return true;
    }
    return false;
}

function string GetPlayerName()
{
    if( PlayerReplicationInfo != none )
    {
        return PlayerReplicationInfo.PlayerName;
    }

    return "";
}

defaultproperties
{
     bCanBeHealed=true
     BileFrequency=0.5 // 1.5
     ClientIdleWeaponAnim=""
     ShieldStrengthMax=100.000000
     ShieldHitMat=None
     GibGroupClass=Class'KFMod.KFHumanGibGroup'
     SoundGroupClass=Class'KFMod.KFMaleSoundGroup'
     TeleportFXClass=None
     TransEffects(0)=None
     TransEffects(1)=None
     DeResTime=0.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
     RagdollLifeSpan=9999.000000
     RagDeathVel=75.000000
     RagShootStrength=6000.000000
     RagDeathUpKick=100.000000
     TransOutEffect(0)=None
     TransOutEffect(1)=None
     RequiredEquipment(0)="none"
     RequiredEquipment(1)="none"
     bScriptPostRender=True
     MeleeRange=80.000000
     GroundSpeed=240.000000
     WaterSpeed=180.000000
     AirSpeed=240.000000
     JumpZ=300.000000
     BaseEyeHeight=44.0//54.000000
     EyeHeight=44.0//54.000000
     DodgeSpeedFactor=1.000000
     DodgeSpeedZ=0.000000
     SwimAnims(0)="WalkF"
     SwimAnims(1)="WalkB"
     SwimAnims(2)="WalkL"
     SwimAnims(3)="WalkR"
     AmbientGlow=0
     bClientAnim=True
     CollisionRadius=20.0//26.000000
     CollisionHeight=40.000000
     CrouchRadius=20.0
     bBlockKarma=True

     Mass=400.000000

     BurnEffect = class 'KFMonsterFlame'
    bRealDeathType=True
    bDoAdjustFeet=True
    bDetailedShadows=True

    MovementAnims(0)=JogF_Bullpup//RunF
    MovementAnims(1)=JogB_Bullpup//RunB
    MovementAnims(2)=JogL_Bullpup//RunL
    MovementAnims(3)=JogR_Bullpup//RunR
//    SwimAnims(0)=SwimF
//    SwimAnims(1)=SwimB
//    SwimAnims(2)=SwimL
//    SwimAnims(3)=SwimR
    CrouchAnims(0)=CHwalkF_BullPup
    CrouchAnims(1)=CHwalkB_BullPup
    CrouchAnims(2)=CHwalkL_BullPup
    CrouchAnims(3)=CHwalkR_BullPup
    WalkAnims(0)=WalkF_Bullpup
    WalkAnims(1)=WalkB_Bullpup
    WalkAnims(2)=WalkL_Bullpup
    WalkAnims(3)=WalkR_Bullpup
    AirStillAnim=JumpF_Mid
    AirAnims(0)=JumpF_Mid
    AirAnims(1)=JumpF_Mid
    AirAnims(2)=JumpL_Mid
    AirAnims(3)=JumpR_Mid
    TakeoffStillAnim=JumpF_Takeoff
    TakeoffAnims(0)=JumpF_Takeoff
    TakeoffAnims(1)=JumpF_Takeoff
    TakeoffAnims(2)=JumpL_Takeoff
    TakeoffAnims(3)=JumpR_Takeoff
    LandAnims(0)=JumpF_Land
    LandAnims(1)=JumpF_Land
    LandAnims(2)=JumpL_Land
    LandAnims(3)=JumpR_Land
    DoubleJumpAnims(0)=DoubleJumpF
    DoubleJumpAnims(1)=DoubleJumpB
    DoubleJumpAnims(2)=DoubleJumpL
    DoubleJumpAnims(3)=DoubleJumpR
    DodgeAnims(0)=JumpF_Takeoff
    DodgeAnims(1)=JumpF_Takeoff
    DodgeAnims(2)=JumpL_Takeoff
    DodgeAnims(3)=JumpR_Takeoff
    WallDodgeAnims(0)=WallDodgeF
    WallDodgeAnims(1)=WallDodgeB
    WallDodgeAnims(2)=WallDodgeL
    WallDodgeAnims(3)=WallDodgeR

    TurnRightAnim=TurnR
    TurnLeftAnim=TurnL
    CrouchTurnRightAnim=CH_TurnR
    CrouchTurnLeftAnim=CH_TurnL
    IdleRestAnim=Idle_Rifle//Idle_Rest
    IdleCrouchAnim=CHIdle_BullPup
    IdleSwimAnim=Swim_Tread
    IdleWeaponAnim=Idle_Bullpup//Idle_Rifle
    IdleHeavyAnim=Idle_Bullpup//Idle_Biggun
    IdleRifleAnim=Idle_Bullpup//Idle_Rifle
    IdleChatAnim=Idle_Chat
    FireAnims(0)=Fire_Bullpup
    FireAnims(1)=Fire_Bullpup
    FireAnims(2)=Fire_Bullpup
    FireAnims(3)=Fire_Bullpup
    FireAltAnims(0)=Fire_Bullpup
    FireAltAnims(1)=Fire_Bullpup
    FireAltAnims(2)=Fire_Bullpup
    FireAltAnims(3)=Fire_Bullpup
    FireCrouchAnims(0)=CHFire_BullPup
    FireCrouchAnims(1)=CHFire_BullPup
    FireCrouchAnims(2)=CHFire_BullPup
    FireCrouchAnims(3)=CHFire_BullPup
    FireCrouchAltAnims(0)=CHFire_BullPup
    FireCrouchAltAnims(1)=CHFire_BullPup
    FireCrouchAltAnims(2)=CHFire_BullPup
    FireCrouchAltAnims(3)=CHFire_BullPup
    HitAnims(0)=HitF_Bullpup
    HitAnims(1)=HitB_Bullpup
    HitAnims(2)=HitL_Bullpup
    HitAnims(3)=HitR_Bullpup
    PostFireBlendStandAnim=Blend_Bullpup
    PostFireBlendCrouchAnim=CHBlend_Bullpup

    RootBone="CHR_Pelvis"
    HeadBone="CHR_Head"
    SpineBone1="CHR_Spine2"
    SpineBone2="CHR_Spine3"
    FireRootBone="CHR_Spine1"

    PrePivot=(X=0.0,Y=0.0,Z=0.0);
    Species=class'SPECIES_KFMaleHuman'

    GruntVolume=0.5
    FootstepVolume = 0.45
    QuietFootStepVolume=0.40
    FootStepSoundRadius=125
    BobSpeedModifier=0.90
    BobScaleModifier=1.0

    RagImpactSounds(0)=Sound'KF_PlayerGlobalSnd.Player_BodyImpact'

    SoundFootsteps(0)=Sound'KF_PlayerGlobalSnd.Player_StepDefault'//Sound'Inf_Player.FootStepDirt'
    SoundFootsteps(1)=Sound'KF_PlayerGlobalSnd.Player_StepDirt'//Sound'Inf_Player.FootstepGravel' // Rock
    SoundFootsteps(2)=Sound'KF_PlayerGlobalSnd.Player_StepDirt'//Sound'Inf_Player.FootStepDirt' // Dirt
    SoundFootsteps(3)=Sound'KF_PlayerGlobalSnd.Player_StepMetal'//Sound'Inf_Player.FootstepMetal' // Metal
    SoundFootsteps(4)=Sound'KF_PlayerGlobalSnd.Player_StepWood'//Sound'Inf_Player.FootstepWoodenfloor' // Wood
    SoundFootsteps(5)=Sound'KF_PlayerGlobalSnd.Player_StepGrass'//Sound'Inf_Player.FootstepGrass' // Plant
    SoundFootsteps(6)=Sound'KF_PlayerGlobalSnd.Player_StepDirt'//Sound'Inf_Player.FootStepDirt' // Flesh
    SoundFootsteps(7)=Sound'KF_PlayerGlobalSnd.Player_StepDefault'//Sound'Inf_Player.FootstepSnowRough' // Ice
    SoundFootsteps(8)=Sound'KF_PlayerGlobalSnd.Player_StepDefault'//Sound'Inf_Player.FootstepSnowHard'
    SoundFootsteps(9)=Sound'KF_PlayerGlobalSnd.Player_StepWater'//Sound'Inf_Player.FootstepWaterShallow' // Shallow Water
    SoundFootsteps(10)=Sound'KF_PlayerGlobalSnd.Player_StepBrGlass'//Sound'Inf_Player.FootstepGravel' // Glass- Replaceme
    SoundFootsteps(11)=Sound'KF_PlayerGlobalSnd.Player_StepDefault'//Sound'Inf_Player.FootstepGravel' // Gravel
    SoundFootsteps(12)=Sound'KF_PlayerGlobalSnd.Player_StepConc'//Sound'Inf_Player.FootstepAsphalt' // Concrete
    SoundFootsteps(13)=Sound'KF_PlayerGlobalSnd.Player_StepWood'//Sound'Inf_Player.FootstepWoodenfloor' // HollowWood
    SoundFootsteps(14)=Sound'KF_PlayerGlobalSnd.Player_StepDefault'//Sound'Inf_Player.FootstepMud' // Mud
    SoundFootsteps(15)=Sound'KF_PlayerGlobalSnd.Player_StepMetal'//Sound'Inf_Player.FootstepMetal' // MetalArmor
    SoundFootsteps(16)=Sound'KF_PlayerGlobalSnd.Player_StepDefault'//Sound'Inf_Player.FootstepAsphalt_P' // Paper
    SoundFootsteps(17)=Sound'KF_PlayerGlobalSnd.Player_StepDefault'//Sound'Inf_Player.FootStepDirt' // Cloth
    SoundFootsteps(18)=Sound'KF_PlayerGlobalSnd.Player_StepDefault'//Sound'Inf_Player.FootStepDirt' // Rubber
    SoundFootsteps(19)=Sound'KF_PlayerGlobalSnd.Player_StepDefault'//Sound'Inf_Player.FootStepDirt' // Crap

    SeveredHeadAttachScale=1.1
    SeveredLegAttachScale=1.1
    SeveredArmAttachScale=1.1
    SeveredHeadAttachClass = class'SeveredHeadAttachment'
    SeveredArmAttachClass = class'SeveredArmAttachment'
    SeveredLegAttachClass = class'SeveredLegAttachment'
    NeckSpurtEmitterClass=class'DismembermentJetHead'
    LimbSpurtEmitterClass=class'DismembermentJetLimb'
    ObliteratedEffectClass=class'PlayerObliteratedEmitter'
    // These two are fallbacks. Should assign these in subclasses
    DetachedArmClass=class'SeveredArmSoldier'
    DetachedLegClass=class'SeveredLegSoldier'
    ProjectileBloodSplatClass=class'ROEffects.ProjectileBloodSplat'
    DecapitationSound=sound'KF_EnemyGlobalSnd.Generic_Decap'

    DropCashMessageDelay=10.0

    bBlockHitPointTraces=False

    // whole body, do no further checks if we don't at least hit this
    Hitpoints(MAINCOLLISIONINDEX)=(PointRadius=40,PointHeight=60.0,PointScale=1.0,PointBone=CHR_Pelvis,PointOffset=(X=0.0,Y=-10.0,Z=0.0),DamageMultiplier=0.0,HitPointType=PHP_None)

    // head
    Hitpoints(1)=(PointRadius=6.5,PointHeight=8.0,PointScale=1.0,PointBone=CHR_Head,PointOffset=(X=2.0,Y=-2.0,Z=0.0),DamageMultiplier=5.0,HitPointType=PHP_Head)
    // body
    Hitpoints(2)=(PointRadius=13.0,PointHeight=15.0,PointScale=1.0,PointBone=CHR_Spine3,PointOffset=(X=0.0,Y=-5.0,Z=0.0),DamageMultiplier=1.0,HitPointType=PHP_Torso)
    Hitpoints(3)=(PointRadius=12.0,PointHeight=11.0,PointScale=1.0,PointBone=CHR_Spine1,PointOffset=(X=-3.0,Y=-3.0,Z=0.0),DamageMultiplier=1.0,HitPointType=PHP_Torso)
//
//  // upper appendages
    Hitpoints(4)=(PointRadius=7.0,PointHeight=12.0,PointScale=1.0,PointBone=CHR_LThigh,PointOffset=(X=18.0,Y=1.0,Z=0.0),DamageMultiplier=0.5,HitPointType=PHP_Leg)
    Hitpoints(5)=(PointRadius=7.0,PointHeight=12.0,PointScale=1.0,PointBone=CHR_RThigh,PointOffset=(X=18.0,Y=1.0,Z=0.0),DamageMultiplier=0.5,HitPointType=PHP_Leg)
    Hitpoints(6)=(PointRadius=5.0,PointHeight=11.0,PointScale=1.0,PointBone=CHR_LArmUpper,PointOffset=(X=8.0,Y=0.0,Z=1.0),DamageMultiplier=0.3,HitPointType=PHP_Arm)
    Hitpoints(7)=(PointRadius=5.0,PointHeight=11.0,PointScale=1.0,PointBone=CHR_RArmUpper,PointOffset=(X=8.0,Y=0.0,Z=1.0),DamageMultiplier=0.3,HitPointType=PHP_Arm)
//
//  // lower appendages
    Hitpoints(8)=(PointRadius=6.0,PointHeight=18.0,PointScale=1.0,PointBone=CHR_LCalf,PointOffset=(X=15.0,Y=0.0,Z=0.0),DamageMultiplier=0.4,HitPointType=PHP_Leg)
    Hitpoints(9)=(PointRadius=6.0,PointHeight=18.0,PointScale=1.0,PointBone=CHR_RCalf,PointOffset=(X=15.0,Y=0.0,Z=0.0),DamageMultiplier=0.4,HitPointType=PHP_Leg)
    Hitpoints(10)=(PointRadius=4.0,PointHeight=10.0,PointScale=1.0,PointBone=CHR_LArmForeArm,PointOffset=(X=6.0,Y=0.0,Z=0.0),DamageMultiplier=0.2,HitPointType=PHP_Arm)
    Hitpoints(11)=(PointRadius=4.0,PointHeight=10.0,PointScale=1.0,PointBone=CHR_RArmForeArm,PointOffset=(X=6.0,Y=0.0,Z=0.0),DamageMultiplier=0.2,HitPointType=PHP_Arm)
//
//  // hands and feet
    Hitpoints(12)=(PointRadius=4.0,PointHeight=4.0,PointScale=1.0,PointBone=CHR_LArmPalm,PointOffset=(X=5.0,Y=2.0,Z=0.0),DamageMultiplier=0.1,HitPointType=PHP_Hand)
    Hitpoints(13)=(PointRadius=4.0,PointHeight=4.0,PointScale=1.0,PointBone=CHR_RArmPalm,PointOffset=(X=5.0,Y=2.0,Z=0.0),DamageMultiplier=0.1,HitPointType=PHP_Hand)
    Hitpoints(14)=(PointRadius=4.0,PointHeight=8.0,PointScale=1.0,PointBone=CHR_LToe1,PointOffset=(X=0.0,Y=0.0,Z=0.0),DamageMultiplier=0.1,HitPointType=PHP_Foot)
    Hitpoints(15)=(PointRadius=4.0,PointHeight=8.0,PointScale=1.0,PointBone=CHR_RToe1,PointOffset=(X=0.0,Y=0.0,Z=0.0),DamageMultiplier=0.1,HitPointType=PHP_Foot)
    bForceSkelUpdate=true;
}
